Форум программистов, компьютерный форум, киберфорум
Программирование звука
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.83/35: Рейтинг темы: голосов - 35, средняя оценка - 4.83
0 / 0 / 0
Регистрация: 12.03.2018
Сообщений: 41

Передача информации по акустическому каналу

06.11.2019, 13:03. Показов 8554. Ответов 41
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Доброго времени суток. Передо мной стоит задача написать программу, которая позволяла бы двум ноутбукам обмениваться текстовой информацией при помощи звука. Как я сам вижу алгоритм для решения данной задачи: 1) Вводится текст; 2) Введенный текст преобразуется в двоичный код, например, по таблице Unicode; 3) Для 0 и 1 задаются определенные частоты; 4) В соответствии с заданными частотами создается и воспроизводится аудио-файл; 5) Второй ноутбук, выступающий в качестве приемника, должен принять аудио-файл с первого ноутбука и перевести его обратно в текст.
Собственно мой вопрос заключается в том, каким образом реализовать данный алгоритм? Для C# есть библиотека для работы со звуком nAudio, но у меня не получилось найти к ней нормальной документации.
Буду рад любым предложениям и мыслям по способам реализации данной задачи. Возможно кто-то с чем-то подобным уже сталкивался или видел какие-нибудь интересные статьи по этой тематике?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
06.11.2019, 13:03
Ответы с готовыми решениями:

Передача знаков по каналу
Помогите пожалуйста решить такую задачку: По каналу связи передается 1500 знаков. Каждый знак может быть искажен с вероятностью 0.001....

Передача данных по дуплексному каналу связи
Добрый день, подскажите правильно ли решена задачи, если нет, то ткните носом где ошибка. Прошу помощи.:sorry: В системе передачи...

Передача пакетов по дуплексному каналу связи
В системе передачи данных производится обмен пакетами между пунктами А и В по дуплексному каналу связи (возможна одновременная передача...

41
2736 / 891 / 331
Регистрация: 10.02.2018
Сообщений: 2,128
14.11.2019, 14:15
Студворк — интернет-сервис помощи студентам
c1gnum, для работы с железом, если всё нужно выдавать/принимать под Windows, то можно попробовать WASAPI.
1
0 / 0 / 0
Регистрация: 12.03.2018
Сообщений: 41
14.11.2019, 15:21  [ТС]
Ygg, спасибо, приму к сведению.
0
Asm/C++/Delphi/Py/PHP/VBA
 Аватар для Jin X
6816 / 2055 / 239
Регистрация: 14.12.2014
Сообщений: 4,319
Записей в блоге: 12
16.11.2019, 01:47
Цитата Сообщение от c1gnum Посмотреть сообщение
Пока буду заниматься основами ЦОС.
Cтивен Смит. Цифровая обработка сигналов. Практическое руководство для инженеров и научных работников (2012)
Нагуглить несложно.
1
0 / 0 / 0
Регистрация: 12.03.2018
Сообщений: 41
08.05.2020, 13:13  [ТС]
Здравствуйте, кто-нибудь может подсказать, какие коррективы необходимо внести в код, чтобы микрофон начинал записывать звук только после того, как уровень амплитуды звука превысит определенное заданное значение и заканчивал запись, например, спустя 1 секунду после того, как уровень амплитуды снизился до определенного значения?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NAudio.Wave;
namespace WindowsFormsApplication8
{
   
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        WaveIn waveIn;
        WaveFileWriter waveWriter;
 
 
        private void waveIn_DataAvailable(object sender, WaveInEventArgs e)
        {
            if (waveWriter == null) return;
 
            waveWriter.Write(e.Buffer, 0, e.BytesRecorded);
            waveWriter.Flush();
        }
 
 
        private void button1_Click(object sender, EventArgs e)
        {
            waveIn = new WaveIn();
            waveIn.DeviceNumber = 0;
            string outputFilename = Application.StartupPath + "\\123.wav"; 
            waveIn.WaveFormat = new WaveFormat(8000, 1);
 
            waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
            waveWriter = new WaveFileWriter(outputFilename, waveIn.WaveFormat);
            waveIn.StartRecording();
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
 
            if (waveIn != null)
            {
                waveIn.StopRecording();
                waveIn.Dispose();
                waveIn = null;
            }
            if (waveWriter != null)
            {
                waveWriter.Dispose();
                waveWriter = null;
            }
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
 
        }
 
 
 
  
    }
}
0
Asm/C++/Delphi/Py/PHP/VBA
 Аватар для Jin X
6816 / 2055 / 239
Регистрация: 14.12.2014
Сообщений: 4,319
Записей в блоге: 12
08.05.2020, 23:15
c1gnum, не записывая звук, вы не сможете определить уровень сигнала.
Но можно начинать использовать сигнал после достижения им определённого пика (а до этого игнорировать).
Про определение пикового уровня есть в доках: https://github.com/naudio/NAud... elMeter.md
Только вам надо не определить пик во всём буфере, а просто найти место, где амплитуда превышает определённое пороговое значение. После этого начинаете отсчитывать 8000 семплов (если у вас 8 кГц и нужна 1 секунда). Если в течение этого периода будет найдено значение, превышающее нижний порог, значит начинаете отсчёт заново (снова 8000). До тех пор, пока все значения не будут ниже порога отключения.
1
09.05.2020, 10:14

Не по теме:

Цитата Сообщение от Jin X Посмотреть сообщение
не записывая звук, вы не сможете определить уровень сигнала.
Правильные вещи вы говорите, но я бы внёс чисто формальное замечание. В компьютерной обработке сигналов встречаются два термина "запись" (record) и "захват" (capture). Иногда их используют как синонимы, а иногда значение терминов различают. "Захват" - это такой процесс, в результате которого внешний сигнал оказывается в оперативной памяти компьютера. А "запись" - это такой процесс, в результате которого данные оказываются на жёстком диске. В такой терминологии для анализа сигнала без записи на диски правильнее было бы употребить термин захват.

0
0 / 0 / 0
Регистрация: 12.03.2018
Сообщений: 41
11.05.2020, 00:03  [ТС]
Доброго времени суток. Не могли бы вы дать ссылки на примеры "захвата" звука. Мне это необходимо для того, чтобы программа, услышав определенный звук (пусть звук, превышающий определенный уровень громкости) , сама начинала запись и затем останавливала. На данный момент я сам нажимаю на кнопки для того, чтобы начать и остановить запись.
Ещё можете подсказать, каким образом выбрать частоты для передачи? Я реализовал два варианта: передача по битам, в этом случае используются всего 2 частоты, и передача по байтам с использованием 16 частот. Первый вариант работает более менее, но при использовании 16 частот очень часто возникают ошибки. Я пробовал увеличивать количество семплов для передачи одного байта - количество ошибок уменьшается, но все равно они возникают довольно часто, в особенности, когда происходит переход с низких частот (100-500 Гц) на более высокие (1кГц и выше). Частоты определяются с помощью fft с количеством отсчётов равным половине частоты дискретизации, точнее чуть большим (4096 для частоты дискретизации 8 кГц). Заметил, что ошибки возникают, когда микрофон и динамик не находятся в зоне прямой видимости (я использую в качестве микрофона микрофон веб-камеры, если ее поставить прямо перед колонкой, то сигналы распознаются довольно точно, если же вебку поставить, например, за монитор, а за ним идёт сразу стена, и, соответственно, микрофон принимает отраженный звук, то в этом случае только часть символов распознаются точно. Я не знаю, особо не вникал в физику звука, может волны определенных частот гасят друг друга?
0
2736 / 891 / 331
Регистрация: 10.02.2018
Сообщений: 2,128
11.05.2020, 10:38
Цитата Сообщение от c1gnum Посмотреть сообщение
Не могли бы вы дать ссылки на примеры "захвата" звука.
В вашем коде есть всё необходимое.
Объект waweIn отвечает за захват данных в память компьютера.
Объект waveWriter отвечает за запись захваченных данных в файл на диски.
Захват производится блоками семплов. Каждый раз, когда очередной блок захвачен, вызывается функция waveIn_DataAvailable. Сейчас внутри этой функции нет никакого анализа, только передача данных на запись в файл.

Опишите более детально, как именно вы превращаете двоичные данные в семплы звука.

Добавлено через 53 минуты
При анализе звука с помощью fft есть определённая зависимость от положения окна анализа. Так, сдвинув окно на четверть вы можете получить уже совсем иной результат распределения частот. Сколько длится одна посылка данных, как данные разделяются, как вы определяете смещение для окна анализа и как его двигаете?

Есть ещё алгоритм Гёрцеля. С его помощью можно вычислять амплитуды отдельных частот. Если частот не много, то это может оказаться значительно быстрее вычисления всего спектра с помощью fft.
1
0 / 0 / 0
Регистрация: 12.03.2018
Сообщений: 41
11.05.2020, 12:53  [ТС]
Цитата Сообщение от Ygg Посмотреть сообщение
Опишите более детально, как именно вы превращаете двоичные данные в семплы звука.
Если говорить о передаче с использованием 16 - ти частот, то изначально введенный текст я преобразую в его байтовое представление, после перевожу полученные данные в шестнадцатиречный формат, после чего шестнадцатиричные числа разбиваются на составляющие (например, число 31 будет разбито на 3 и 1), затем каждому символу шестнадцатиричного алфавита присваивается своя частота, и после формируется сам сигнал:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
string Test = textBox1.Text;
            StringBuilder sb = new StringBuilder();
 
            foreach (byte b in System.Text.Encoding.Unicode.GetBytes(Test))
            {
                sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
            }
 
            string hex = sb.ToString();
            char[] ch = new char[hex.Length];
            ch = hex.ToCharArray();
 
            int sampleRate = 8000 * 1; // наша частота дискретизации.
            short[] data = new short[2048 / 2 * ch.Length];// количество семплов
            Console.Write(data.Length);
            double[] fre = new double[16];
            double sum1 = 0;
            for (int i = 0; i < fre.Length; i++)
            {
                sum1 += 100;
                fre[i] = sum1;
            }
 
            double frequency0 = Math.PI * 2 * fre[0] / sampleRate;
            double frequency1 = Math.PI * 2 * fre[1] / sampleRate;
            double frequency2 = Math.PI * 2 * fre[2] / sampleRate;
            double frequency3 = Math.PI * 2 * fre[3] / sampleRate;
            double frequency4 = Math.PI * 2 * fre[4] / sampleRate;
            double frequency5 = Math.PI * 2 * fre[5] / sampleRate;
            double frequency6 = Math.PI * 2 * fre[6] / sampleRate;
            double frequency7 = Math.PI * 2 * fre[7] / sampleRate;
            double frequency8 = Math.PI * 2 * fre[8] / sampleRate;
            double frequency9 = Math.PI * 2 * fre[9] / sampleRate;
            double frequencya = Math.PI * 2 * fre[10] / sampleRate;
            double frequencyb = Math.PI * 2 * fre[11] / sampleRate;
            double frequencyc = Math.PI * 2 * fre[12] / sampleRate;
            double frequencyd = Math.PI * 2 * fre[13] / sampleRate;
            double frequencye = Math.PI * 2 * fre[14] / sampleRate;
            double frequencyf = Math.PI * 2 * fre[15] / sampleRate;
            int ind0 = 0;
 
            for (int i = 0; i < ch.Length; i++)
            {
                double frequency = 0;
                int ind1 = ind0;
                if (ch[i] == '0')
                {
                    frequency = frequency0;
                }
                if (ch[i] == '1')
                {
                    frequency = frequency1;
                }
                if (ch[i] == '2')
                {
                    frequency = frequency2;
                }
                if (ch[i] == '3')
                {
                    frequency = frequency3;
                }
                if (ch[i] == '4')
                {
                    frequency = frequency4;
                }
                if (ch[i] == '5')
                {
                    frequency = frequency5;
                }
                if (ch[i] == '6')
                {
                    frequency = frequency6;
                }
                if (ch[i] == '7')
                {
                    frequency = frequency7;
                }
                if (ch[i] == '8')
                {
                    frequency = frequency8;
                }
                if (ch[i] == '9')
                {
                    frequency = frequency9;
                }
                if (ch[i] == 'a')
                {
                    frequency = frequencya;
                }
                if (ch[i] == 'b')
                {
                    frequency = frequencyb;
                }
                if (ch[i] == 'c')
                {
                    frequency = frequencyc;
                }
                if (ch[i] == 'd')
                {
                    frequency = frequencyd;
                }
                if (ch[i] == 'e')
                {
                    frequency = frequencye;
                }
                if (ch[i] == 'f')
                {
                    frequency = frequencyf;
                }
 
 
                for (int index = ind1; index < (ind1 + 2048 / 2); index++)
                {
                    data[ind0] = (short)(Sine(index, frequency) * short.MaxValue); // Приводим уровень к амплитуде от 32767 до -32767.
                    ind0++;
                }
            }
Цитата Сообщение от Ygg Посмотреть сообщение
При анализе звука с помощью fft есть определённая зависимость от положения окна анализа. Так, сдвинув окно на четверть вы можете получить уже совсем иной результат распределения частот. Сколько длится одна посылка данных, как данные разделяются, как вы определяете смещение для окна анализа и как его двигаете?
Я могу задавать скольким количеством семплов кодировать один символ передаваемой информации. Никаких пауз или еще какого-либо разделения между символами нет, просто идет звук с определенной последовательностью частот. После того как звук записан, из wav файла извлекаются значения амплитуд, и заносятся в массив, после происходит поиск первого и последнего вхождения определенной амплитуды в этот массив (так я определяю начало и конец сигнала, который необходимо анализировать). Затем я формирую массив, в который помещаю данные для fft, количество отсчетов я беру равным половине частоты дискретизации (чтобы сократить время обработки), происходит проходка по всему массиву анализируемых данных, количество выбираемых значений берется равным количеству семплов, которое было задано при генерации сигнала для кодирования одного символа информации, оставшаяся часть массива заполняется нулями. Так как я беру количество отсчетов, равное половине частоты дискретизации, то я получаю смещенный спектр (если в данной части анализируемого сигнала была частота, к примеру, 500 ГЦ, то на выходе я получу пик амплитуды на примерно 250 Гц, это учитывается в дальнейшей обработке).
Вот код для fft, я его нашел в интернете, так как не очень понимаю математику этого преобразования.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
static private int n, nu;
 
        static private int BitReverse(int j)
        {
            int j2;
            int j1 = j;
            int k = 0;
            for (int i = 1; i <= nu; i++)
            {
                j2 = j1 / 2;
                k = 2 * k + j1 - 2 * j2;
                j1 = j2;
            }
            return k;
        }
 
        static public double[] FFTDb(ref double[] x)
        {
            // Assume n is a power of 2
            n = x.Length;
            nu = (int)(Math.Log(n) / Math.Log(2));
            int n2 = n / 2;
            int nu1 = nu - 1;
            double[] xre = new double[n];
            double[] xim = new double[n];
            double[] decibel = new double[n2];
            double tr, ti, p, arg, c, s;
            for (int i = 0; i < n; i++)
            {
                xre[i] = x[i];
                xim[i] = 0.0f;
            }
            int k = 0;
            for (int l = 1; l <= nu; l++)
            {
                while (k < n)
                {
                    for (int i = 1; i <= n2; i++)
                    {
                        p = BitReverse(k >> nu1);
                        arg = 2 * (double)Math.PI * p / n;
                        c = (double)Math.Cos(arg);
                        s = (double)Math.Sin(arg);
                        tr = xre[k + n2] * c + xim[k + n2] * s;
                        ti = xim[k + n2] * c - xre[k + n2] * s;
                        xre[k + n2] = xre[k] - tr;
                        xim[k + n2] = xim[k] - ti;
                        xre[k] += tr;
                        xim[k] += ti;
                        k++;
                    }
                    k += n2;
                }
                k = 0;
                nu1--;
                n2 = n2 / 2;
            }
            k = 0;
            int r;
            while (k < n)
            {
                r = BitReverse(k);
                if (r > k)
                {
                    tr = xre[k];
                    ti = xim[k];
                    xre[k] = xre[r];
                    xim[k] = xim[r];
                    xre[r] = tr;
                    xim[r] = ti;
                }
                k++;
            }
            for (int i = 0; i < n / 2; i++)
                decibel[i] = 10.0 * Math.Log10((float)(Math.Sqrt((xre[i] * xre[i]) + (xim[i] * xim[i]))));
            return decibel;
        }
А вот код непосредственно демодулятора:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
Double[] data;
            byte[] wave;
            string path = @"test.wav";
            System.IO.FileStream WaveFile = System.IO.File.OpenRead(path);
            wave = new byte[WaveFile.Length];
            data = new Double[(wave.Length - 44) / 2]; // Смещение байтов в wav-файле;
            WaveFile.Read(wave, 0, Convert.ToInt32(WaveFile.Length)); // чтение wav-файла в массив
            WaveFile.Close();
            /***********Конвертация и PCM учёт***************/
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = (BitConverter.ToInt16(wave, 44 + i * 2)) / 65536.0;
            }
 
            double amp = 0.02;
            int one = 0;
 
            for (int i = 0; i < data.Length; i++)
            {
                if (data[i] >= amp)
                {
                    one = i;
                    break;
                }
            }
 
            int last = 0;
 
            for (int i = 0; i < data.Length; i++)
            {
                if (data[i] > amp)
                {
                    last = i;
                }
            }
 
            double[] data1 = new double[last - one];
 
            for (int i = 0; i < data1.Length; i++)
            {
                data1[i] = data[one];
                one++;
            }
 
            double[] bit1 = new double[8192 / 2];
            List<char> by = new List<char>();
            List<int> f = new List<int>();
            
 
            for (int i = 0; i < data1.Length / 1024; i++)
            {
                Array.Copy(data1, 2048 / 2 * i, bit1, 0, 2048 / 2);
                double[] spectrum = Form1.FFTDb(ref bit1);
                double max = 0;
                int k = 0;
 
                for (int j = 0; j < spectrum.Length; j++)
                {
                    if (spectrum[j] > max)
                    {
                        max = spectrum[j];
                        k = j;
                    }
                }
                f.Add(k);
            }
 
            double[] fre = new double[16];
            double sum1 = 0;
            for (int i = 0; i < fre.Length; i++)
            {
                sum1 += 100;
                fre[i] = sum1;
            }
            double shag = 20;
            for (int i = 0; i < f.Count; i++)
            {
                if (f[i] > (fre[0] / 1.95 - shag) & f[i] < (fre[0] / 1.95 + shag))
                {
                    by.Add('0');
                }
                if (f[i] > (fre[1] / 1.95 - shag) & f[i] < (fre[1] / 1.95 + shag))
                {
                    by.Add('1');
                }
                if (f[i] > (fre[2] / 1.95 - shag) & f[i] < (fre[2] / 1.95 + shag))
                {
                    by.Add('2');
                }
                if (f[i] > (fre[3] / 1.95 - shag) & f[i] < (fre[3] / 1.95 + shag))
                {
                    by.Add('3');
                }
                if (f[i] > (fre[4] / 1.95 - shag) & f[i] < (fre[4] / 1.95 + shag))
                {
                    by.Add('4');
                }
                if (f[i] > (fre[5] / 1.95 - shag) & f[i] < (fre[5] / 1.95 + shag))
                {
                    by.Add('5');
                }
                if (f[i] > (fre[6] / 1.95 - shag) & f[i] < (fre[6] / 1.95 + shag))
                {
                    by.Add('6');
                }
                if (f[i] > (fre[7] / 1.95 - shag) & f[i] < (fre[7] / 1.95 + shag))
                {
                    by.Add('7');
                }
                if (f[i] > (fre[8] / 1.95 - shag) & f[i] < (fre[8] / 1.95 + shag))
                {
                    by.Add('8');
                }
                if (f[i] > (fre[9] / 1.95 - shag) & f[i] < (fre[9] / 1.95 + shag))
                {
                    by.Add('9');
                }
                if (f[i] > (fre[10] / 1.95 - shag) & f[i] < (fre[10] / 1.95 + shag))
                {
                    by.Add('a');
                }
                if (f[i] > (fre[11] / 1.95 - 4) & f[i] < (fre[11] / 1.95 + 4))
                {
                    by.Add('b');
                }
                if (f[i] > (fre[12] / 1.95 - 4) & f[i] < (fre[12] / 1.95 + 4))
                {
                    by.Add('c');
                }
                if (f[i] > (fre[13] / 1.95 - 4) & f[i] < (fre[13] / 1.95 + 4))
                {
                    by.Add('d');
                }
                if (f[i] > (fre[14] / 1.95 - 4) & f[i] < (fre[14] / 1.95 + 4))
                {
                    by.Add('e');
                }
                if (f[i] > (fre[15] / 1.95 - 4) & f[i] < (fre[15] / 1.95 + 4))
                {
                    by.Add('f');
                }
            }
 
            int[] mas = new int[by.Count];
 
            for (int i = 0; i < by.Count; i++)
            {
                if (by[i] == '0')
                {
                    mas[i] = 0;
                }
                if (by[i] == '1')
                {
                    mas[i] = 1;
                }
                if (by[i] == '2')
                {
                    mas[i] = 2;
                }
                if (by[i] == '3')
                {
                    mas[i] = 3;
                }
                if (by[i] == '4')
                {
                    mas[i] = 4;
                }
                if (by[i] == '5')
                {
                    mas[i] = 5;
                }
                if (by[i] == '6')
                {
                    mas[i] = 6;
                }
                if (by[i] == '7')
                {
                    mas[i] = 7;
                }
                if (by[i] == '8')
                {
                    mas[i] = 8;
                }
                if (by[i] == '9')
                {
                    mas[i] = 9;
                }
                if (by[i] == 'a')
                {
                    mas[i] = 10;
                }
                if (by[i] == 'b')
                {
                    mas[i] = 11;
                }
                if (by[i] == 'c')
                {
                    mas[i] = 12;
                }
                if (by[i] == 'd')
                {
                    mas[i] = 13;
                }
                if (by[i] == 'e')
                {
                    mas[i] = 14;
                }
                if (by[i] == 'f')
                {
                    mas[i] = 15;
                }
            }
 
            int[] mas1 = new int[2];
            byte[] mas2 = new byte[by.Count / 2];
 
            for (int i = 0; i < mas.Length / 2; i++)
            {
                double sum = 0;
                int exp = 1;
                Array.Copy(mas, i * 2, mas1, 0, 2);
 
                for (int j = 0; j < mas1.Length; j++)
                {
                    sum += mas1[j] * Math.Pow(16, exp);
                    exp--;
                }
 
                mas2[i] = (byte)sum;
            }
 
            UnicodeEncoding unicode = new UnicodeEncoding();
            textBox1.Text = unicode.GetString(mas2);
        }
Так как получить точное значение частоты анализируемого участка сигнала мне не удается, то я проверяю попадает ли найденная частота в определенный коридор частот:
C#
1
2
3
4
5
double shag = 20;
if (f[i] > (fre[0] / 1.95 - shag) & f[i] < (fre[0] / 1.95 + shag))
                {
                    by.Add('0');
                }
Мне кажется в этом может заключаться серьезная проблема (при повышении частот, fft дает все менее точный результат и соответственно приходится увеличивать допущение по частоте, но в этом случае высокие частоты определяются верно, а низкие наоборот определяются хуже). Хотя я пробовал все 16 частот уместить в небольшой интервал, к примеру 100...340 Гц, и соответственно уменьшал допущение по частоте, но результат все равно оставлял желать лучшего.
Вот сами приложения: Projects.rar
0
Asm/C++/Delphi/Py/PHP/VBA
 Аватар для Jin X
6816 / 2055 / 239
Регистрация: 14.12.2014
Сообщений: 4,319
Записей в блоге: 12
11.05.2020, 15:50
Я сейчас попробовал на ноуте через микрофон записать звук, генерируемый с настольного компа через колоночки. Получился трэш вообще. Мало того, что громкость и так довольно низкая, так после ≈ 4 кГц она сильно падает, а после ≈ 8 кГц вообще ничего не записывается. Да ещё и форма волны вообще получается совершенно иной, с кучей паразитных частот. Даже чередуя звук 2 кГц и 4 кГц по 0.01 сек (с записью 48 кГц), по волне вообще очень сложно понять, что там происходит. Т.е., записывая без кабеля, вряд ли вы добьётесь нормальной скорости передачи (передать пару сотен байт в секунду – это будет уже серьёзный прогресс, ИМХО).
0
2736 / 891 / 331
Регистрация: 10.02.2018
Сообщений: 2,128
11.05.2020, 16:50
----------
Не, я не правильно посчитал. Вроде с частотами при генерации у вас всё хорошо.
Может с частотами при анализе что-то не очень...

Добавлено через 15 минут
Да, при анализе что-то не понятное. f[i] - это индекс частоты, а не частота. А сравниваете вы её с частотой fre. Верну назад формулу для пересчёта индекса в частоту, которую уже стёр.

При построение ДПФ частоты считаются по формуле: F(k)=Fд*k/N
F(k) - частота гармоники с индексом k
Fд - частота дискретизации (8 КГц)
k - номер гармоники, от 0 до 1023
N - количество отчётов (1024)

F(0)=8000*0/1024=0
F(1)=8000*1/1024=7,8125
F(2)=8000*2/1024=15,625
...
F(512)=8000*512/1024=4000 - частота отсечения
...
0
0 / 0 / 0
Регистрация: 12.03.2018
Сообщений: 41
11.05.2020, 17:51  [ТС]
Цитата Сообщение от Ygg Посмотреть сообщение
При построение ДПФ частоты считаются по формуле: F(k)=Fд*k/N
Извиняюсь за такие глупые вопросы, но я явно что-то не понимаю, вот, к примеру, такой спектр я получаю для 1024 семплов, которые кодировали элемент , которому соответствовала частота 100 ГЦ:

Как видно, пик приходит на значение равное 103. По той формуле, что вы привели, получится следующее значение частоты: F(103) = 8000 * 103 / 1024 = 804,6875.
0
2736 / 891 / 331
Регистрация: 10.02.2018
Сообщений: 2,128
11.05.2020, 19:36
c1gnum, а почему на вашей картинке более 4000 значений?
0
0 / 0 / 0
Регистрация: 12.03.2018
Сообщений: 41
11.05.2020, 19:45  [ТС]
Цитата Сообщение от Ygg Посмотреть сообщение
c1gnum, а почему на вашей картинке более 4000 значений?
Потому что в данном случае я подал 8192 значения в fft.
0
2736 / 891 / 331
Регистрация: 10.02.2018
Сообщений: 2,128
11.05.2020, 19:51
Цитата Сообщение от c1gnum Посмотреть сообщение
Потому что в данном случае я подал 8192 значения в fft.
Ну так, тогда в формулу вместо 1024 нужно подставлять 8192.
F(0)=8000*0/8192
F(1)=8000*1/8192
F(2)=8000*2/8192
...

При таких значениях частоты и количества выборок индекс очень близок к частоте индекса.
1
0 / 0 / 0
Регистрация: 12.03.2018
Сообщений: 41
11.05.2020, 20:04  [ТС]
Цитата Сообщение от Ygg Посмотреть сообщение
Ну так, тогда в формулу вместо 1024 нужно подставлять 8192.
F(0)=8000*0/8192
F(1)=8000*1/8192
F(2)=8000*2/8192
...
При таких значениях частоты и количества выборок индекс очень близок к частоте индекса.
Понял, и в правду так значения лучше будут получаться. Спасибо большое.
0
0 / 0 / 0
Регистрация: 12.03.2018
Сообщений: 41
12.05.2020, 21:11  [ТС]
Сейчас при генерации сигнала, у меня рисуется следующая картина:

т.е. происходит смещение фазы в моменты смены частот. Эти смещения фазы при воспроизведении звука воспринимаются как короткие щелчки. Я думаю, что из-за этого явления могут возникать ошибки при дешифровке. Можно ли как-то устранить эти сдвиги по фазе, например, чтобы частоты сменялись только в моменты пересечения нуля?
0
 Аватар для Ovederax
604 / 404 / 212
Регистрация: 30.04.2017
Сообщений: 744
Записей в блоге: 1
12.05.2020, 22:46
c1gnum, я думаю это как-то можно привязать формулой к частоте дискретизации и частоте несущей. При 800 отсчетах на один hex при дискретизации 8000 разрыва не видно.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private void button1_Click(object sender, EventArgs e) {
            int sampleRate = 8000 * 1; // частота дискретизации.
            
            byte[] dataIn = System.Text.Encoding.Unicode.GetBytes(textBox1.Text);
            short[] dataOut = new short[2048 / 2 * dataIn.Length*2];
 
            double[] fre = new double[16];
            double sum1 = 0;
            for (int i = 0; i < fre.Length; i++)
            {
                sum1 += 100;
                fre[i] = sum1;
            }
 
 
            double[] frequencyArr = new double[16];
            for (int i = 0; i < 16; ++i)
                frequencyArr[i] = Math.PI * 2 * fre[i] / sampleRate;
 
 
            int ind0 = 0;
            double[] frequency = { 0, 0 };
            for (int i = 0; i < dataIn.Length; i++) {
                frequency[0] = frequencyArr[dataIn[i] / 16];
                frequency[1] = frequencyArr[dataIn[i] % 16];
 
                for (int j = 0; j < 2; ++j) {
                    for (int index = 0; index < (800); index++) {
                        dataOut[ind0] = (short)(Utils.Sine(index, frequency[j]) * short.MaxValue); // Приводим уровень к амплитуде от 32767 до -32767.
                        ind0++;
                    }
                }
            }
 
            Stream file = File.Create("test.wav");      // Создаем новый файл и стыкуем его с потоком.
            Utils.SaveWave(file, dataOut, sampleRate);  // Записываем наши данные в поток.
            file.Close();                               // Закрываем поток
 
            SoundPlayer sp = new SoundPlayer();
            sp.SoundLocation = "test.wav";
            sp.Load();
            sp.Play();
        }
0
0 / 0 / 0
Регистрация: 12.03.2018
Сообщений: 41
12.05.2020, 23:05  [ТС]
У меня получается как таковой несущей нет.

Т.е. есть, если приводить аналогию, то имеются генераторы, формирующие синусоиды различных частот, и в зависимости от модулирующего сигнала выбирается та или иная частота.
0
Asm/C++/Delphi/Py/PHP/VBA
 Аватар для Jin X
6816 / 2055 / 239
Регистрация: 14.12.2014
Сообщений: 4,319
Записей в блоге: 12
13.05.2020, 11:57
Цитата Сообщение от c1gnum Посмотреть сообщение
происходит смещение фазы в моменты смены частот.
Начинайте просто с того же угла, на котором закончили. Вернее, со следующего шага.

Код не читал ваш, но для генерации синусоиды используем формулу: Sin(i*2*Pi*Freq/Rate+StAng)*Amp, где Freq – частота звука, Rate – частота дискретизации, i – номер семпла, Amp – амплитуда, StAng – начальный угол (фаза). Изначально StAng = 0.

Например, была частота 500 Гц, сгенерили 100 семплов (i менялось от 1 до 100, лучше с 1). Нужно перейти к частоте 300 Гц. Запоминаем значение, которое было последним: StAng = 100*2*Pi*500/Rate+StAng. Далее будем использовать этот начальный угол StAng при генерации следующей частоты (Sin(i*2*Pi*300/Rate+StAng)*Amp). Почему i лучше начинать с 1? Потому что если будем генерить с нуля, то первый семпл новой частоты будет повторять последней сгенерированный предыдущей частоты (т.к. 0*2*Pi*Freq/Rate = 0).

Добавлено через 4 минуты
Цитата Сообщение от c1gnum Посмотреть сообщение
чтобы частоты сменялись только в моменты пересечения нуля
А для этого нужно подбирать частоты и длительность блока семплов (которым кодируется передача одного бита) так, чтобы они заканчивались в 0.
Т.е. если длительность 480 семплов при 48 кГц (480/48000 = 0,01 сек), значит частоты должны быть кратными 1/0,01 = 100 Гц. Можно выбрать 500 и 1000 Гц, например (да хоть 1300 и 2700).
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
13.05.2020, 11:57

Передача текста по радио каналу(чат)
Задумал сделать передачу текста, предложений по радио каналу, нашёл исходники подправил под себя, но текст в терминале отображается...

Передача информации
Здравствуйте,скажите пожалуйста вот такая ситуация: создана БД Гостиница в ней 3 таблицы клиенты,заказы и проживание так вот диаграмма у...

Передача информации
Файл передается V=100 Кбит/c(сделать combobox с ед.измерения) Q=1 мб(сделать combobox с ед.измерения) t=? t=Q/V=1 мб/100 Кбит/c...

Передача информации
Здравствуйте.Программирую в C++/Qt Проблема состоит в передаче данных между формами.Мне нужно передать информацию введенную на Form1 в...

Передача информации из массива GET
&lt;?php session_start(); if(isset($_GET)){ header('location: g1.php'); } ?&gt; &lt;form method=&quot;GET&quot; action...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
сукцессия 30. Фосфорный и азотный замки по "честному" закону лимитирования Либиха
anaschu 16.07.2026
Основные достижения за последние дни 1. Механизм отложенного высвобождения фосфора Добавлен специальный пул для медленного разложения меланизированных гиф ЕСМ Реализована разная скорость. . .
Запрет пометки на удаление и отмены проведения
Maks 16.07.2026
Алгоритм из решения ниже реализован на нетиповом документе "ПутевойЛист" разработанного в КА2. Задача: запретить помечать на удаление и отменять проведения путевого листа при условии, если он сдан. . .
Как надо было делать правильно - вообще не слушать ИИ, его дело - текст.
Hrethgir 15.07.2026
А вообще надо было делать не так, чтобы не исправлять ошибку https:/ / www. cyberforum. ru/ blogs/ 223907/ 10996. html , правильно было : удалил кнопку, а потом добавил её. Всё это стресс конечно. Почему?. . .
Знаете, от чего появляется стресс?
kumehtar 15.07.2026
Стресс появляется оттого, что вы, зная как поступить правильно, делаете наоборот. Ларри Уингет
Нас бьют, а мы крепчаем? Какая чушь!
kumehtar 15.07.2026
Вот это вот бытующее мнение, что человек меняется только под действием проблем, всегда считал вредоносным. Поверь, от проблем у людей появляются не изменения, а нервное расстройство и ПТСР. А. . .
сукцессия 29. Переход от одних деревьев на другие делать более или менее вероятностным?
anaschu 12.07.2026
Насколько смена типов микоризы — исключительное событие в двухвековой сукцессии? Оценка вероятности в пространстве параметров В текущей версии модели успешно реализован ключевой механизм. . .
сукцессия 27. Думаю, как переделывать уже написанную статью с планами на сукцессию.
anaschu 12.07.2026
Анализ соответствия модели требованиям Реализованные компоненты: Механизм закисления почвы через протонную помпу Конкуренция между типами микориз pH как триггер сукцессии C/ P соотношение. . .
Сукцессия 26. Мат модель создана.
anaschu 12.07.2026
Модель смены растительных сукцессий посредством управления грибами работает внутри небольшой ячейки почвы, восстанавливающейся после пожара, где ненадолго бывшее царство хвойных снова захватили. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru