Я пишу пример передачи данных через звук между двумя компьютерами. Некоторые требования:
Расстояние очень близко, то есть 2 компьютера в основном расположены рядом друг с другом
Очень мало шума (я не думаю, что мой учитель включил бы рок-песню в качестве источника шума)
Ошибка допустима: например, если я отправляю «Радиосвязь», тогда, если другой компьютер получает «RadiQ communEcation», это также хорошо.
Если возможно: нет заголовка, флага, контрольной суммы, .... так как я просто хочу очень простой пример, демонстрирующий основы передачи данных через звук. Не нужно быть модным.
Я попытался использовать Audio Frequency Shift Keying по этой ссылке:
Lab 5 APRS (Автоматическая система отчетов о пакетах)
и получил некоторые результаты: моя страница Github
но этого не достаточно. Я не знаю, как сделать восстановление часов, синхронизацию, ... (ссылка имеет фазовую петлю в качестве механизма восстановления синхронизации, но этого было явно недостаточно).
Поэтому я думаю, что я должен найти более простой подход. Нашел ссылку здесь:
Данные на аудио и обратно. Модуляция / демодуляция с исходным кодом
но ОП не реализовал метод, предложенный в ответе, поэтому я боюсь, что он может быть очень сложным. Также я не совсем понимаю метод декодирования, предложенный в ответе:
Декодер немного сложнее, но вот схема:
Опционально полосовой фильтр дискретизированного сигнала около 11 кГц. Это улучшит производительность в шумной обстановке. КИХ-фильтры довольно просты, и есть несколько онлайн-апплетов дизайна, которые сгенерируют фильтр для вас.
Порог сигнал. Каждое значение выше 1/2 максимальной амплитуды равно 1, каждое значение ниже равно 0. Это предполагает, что вы сэмплировали весь сигнал. Если это в режиме реального времени, вы либо выбираете фиксированный порог, либо выполняете какую-то автоматическую регулировку усиления, где вы отслеживаете максимальный уровень сигнала в течение некоторого времени.
Сканирование для начала точки или тире. Вы, вероятно, хотите увидеть хотя бы определенное число единиц в периоде точек, чтобы считать выборки точкой. Затем продолжайте сканирование, чтобы увидеть, если это тире. Не ожидайте идеального сигнала - вы увидите несколько 0 в середине ваших 1 и несколько 1 в середине ваших 0. Если шумов мало, то отличить периоды «вкл» от периодов «выключен» должно быть довольно просто.
Затем отмените вышеуказанный процесс. Если вы видите тире, нажмите 1 бит в буфере, если точка - ноль.
Я не понимаю, сколько единиц, прежде чем классифицировать это как точку, ... Так что есть много вещей, которые я не понимаю сейчас. Пожалуйста, предложите мне простой метод передачи данных через звук, чтобы я мог понять процесс. Большое спасибо :)
ОБНОВИТЬ:
Я сделал некоторый код Matlab, который кажется (несколько) работоспособным. Сначала я модулирую сигнал, используя амплитудную манипуляцию (частота дискретизации 48000 Гц, F_on = 5000 Гц, скорость передачи битов = 10 бит / с), затем добавляю его с заголовком и конечной последовательностью (конечно, также модулирую их). Заголовок и конечная последовательность были выбраны на специальной основе (да, это был взлом):
header = [0 0 1 0 1 1 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 1 1 0 1 0 1];
end_seq = [1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 0 0 1 0 0 0 1];
Затем я передаю их через звук и записываю на свой смартфон. Затем я отправляю записанное аудио обратно на свой компьютер, использую другой фрагмент кода для чтения аудио. Затем я сопоставляю полученный сигнал (еще не демодулированный) с модулированным заголовком и конечной последовательностью, чтобы узнать начало и конец. После этого я беру только соответствующий сигнал (от начала до конца, как указано в корреляционной части). Затем я демодулирую и пробую, чтобы найти цифровые данные. Вот 3 аудио файла:
"DigitalCommunication_ask": Ссылка здесь отправляет текст "Цифровая связь". Относительно бесшумный, хотя вы можете услышать некоторый фоновый шум в начале и в конце. Однако результат показал только «Digital Commincatio»
«HelloWorld_ask»: ссылка здесь отправляет текст «Hello world». Без шума, как "DigitalCommunication_ask". Однако результат для этого был правильным
«HelloWorld_noise_ask»: Ссылка здесь отправляет текст «Hello world». Однако, есть некоторый шум, который я сделал (я просто сказал некоторые случайные вещи «A, B, C, D, E, ....» во время передачи). К сожалению, это не удалось
Вот код для отправителя (sender.m):
clear
fs = 48000;
F_on = 5000;
bit_rate = 10;
% header = [0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ];
% header = [0 0 1 0 1 1 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 ];
header = [0 0 1 0 1 1 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 1 1 0 1 0 1];
% end_seq = [1 0 0 1 0 1 0 0 1 0 1 1 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 1];
% end_seq = [1 0 0 1 0 1 0 0 1 0 1 1 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 1 0 1 0 0 1 1 0 0 1 1 0 1 1 0 0 1 ];
% end_seq = [0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0];
end_seq = [1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 0 0 1 0 0 0 1];
num_of_samples_per_bit = round(fs / bit_rate);
modulated_header = ask_modulate(header, fs, F_on, bit_rate);
modulated_end_seq = ask_modulate(end_seq, fs, F_on, bit_rate);
% input_str = 'Ah';
input_str = 'Hello world';
ascii_list = double(input_str); % https://www.mathworks.com/matlabcentral/answers/298215-how-to-get-ascii-value-of-characters-stored-in-an-array
bit_stream = [];
for i = 1:numel(ascii_list)
bit = de2bi(ascii_list(i), 8, 'left-msb');
bit_stream = [bit_stream bit];
end
bit_stream = [header bit_stream end_seq];
num_of_bits = numel(bit_stream);
bandlimited_and_modulated_signal = ask_modulate(bit_stream, fs, F_on, bit_rate);
sound(bandlimited_and_modulated_signal, fs);
Для получателя (receive.m):
clear
fs = 48000;
F_on = 5000;
bit_rate = 10;
% header = [0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ];
% header = [0 0 1 0 1 1 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 ];
header = [0 0 1 0 1 1 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 0 1 1 0 1 0 1];
% end_seq = [1 0 0 1 0 1 0 0 1 0 1 1 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 1];
% end_seq = [1 0 0 1 0 1 0 0 1 0 1 1 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 1 0 1 0 0 1 1 0 0 1 1 0 1 1 0 0 1 ];
% end_seq = [0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0];
end_seq = [1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 0 0 1 0 0 0 1];
modulated_header = ask_modulate(header, fs, F_on, bit_rate);
modulated_end_seq = ask_modulate(end_seq, fs, F_on, bit_rate);
% recObj = audiorecorder(fs,8,1);
% time_to_record = 10; % In seconds
% recordblocking(recObj, time_to_record);
% received_signal = getaudiodata(recObj);
% [received_signal, fs] = audioread('SounddataTruong_Ask.m4a');
% [received_signal, fs] = audioread('HelloWorld_noise_ask.m4a');
% [received_signal, fs] = audioread('HelloWorld_ask.m4a');
[received_signal, fs] = audioread('DigitalCommunication_ask.m4a');
ereceived_signal = received_signal(:)';
num_of_samples_per_bit = round(fs / bit_rate);
modulated_header = ask_modulate(header, fs, F_on, bit_rate);
modulated_end_seq = ask_modulate(end_seq, fs, F_on, bit_rate);
y= xcorr(modulated_header, received_signal); % do cross correlation
[m,ind]=max(y); % location of largest correlation
headstart=length(received_signal)-ind+1;
z = xcorr(modulated_end_seq, received_signal);
[m,ind]=max(z); % location of largest correlation
end_index=length(received_signal)-ind+1;
relevant_signal = received_signal(headstart + num_of_samples_per_bit * numel(header) : end_index - 1);
% relevant_signal = received_signal(headstart + num_of_samples_per_bit * numel(header): end);
demodulated_signal = ask_demodulate(relevant_signal, fs, F_on, bit_rate);
sampled_points_in_demodulated_signal = demodulated_signal(round(num_of_samples_per_bit / 2) : num_of_samples_per_bit :end);
digital_output = (sampled_points_in_demodulated_signal > (max(sampled_points_in_demodulated_signal(:)) / 2));
% digital_output = (sampled_points_in_demodulated_signal > 0.05);
% Convert to characters
total_num_of_bits = numel(digital_output);
total_num_of_characters = total_num_of_bits / 8;
first_idx = 0;
last_idx = 0;
output_str = '';
for i = 1:total_num_of_characters
first_idx = last_idx + 1;
last_idx = first_idx + 7;
binary_repr = digital_output(first_idx:last_idx);
ascii_value = bi2de(binary_repr(:)', 'left-msb');
character = char(ascii_value);
output_str = [output_str character];
end
output_str
ASK код модуляции (ask_modulate):
function [bandlimited_and_modulated_signal] = ask_modulate(bit_stream, fs, F_on, bit_rate)
% Amplitude shift keying: Modulation
% Dang Manh Truong (dangmanhtruong@gmail.com)
num_of_bits = numel(bit_stream);
num_of_samples_per_bit = round(fs / bit_rate);
alpha = 0;
d_alpha = 2 * pi * F_on / fs;
A = 3;
analog_signal = [];
for i = 1 : num_of_bits
bit = bit_stream(i);
switch bit
case 1
for j = 1 : num_of_samples_per_bit
analog_signal = [analog_signal A * cos(alpha)];
alpha = alpha + d_alpha;
end
case 0
for j = 1 : num_of_samples_per_bit
analog_signal = [analog_signal 0];
alpha = alpha + d_alpha;
end
end
end
filter_order = 15;
LP_filter = fir1(filter_order, (2*6000)/fs, 'low');
bandlimited_analog_signal = conv(analog_signal, LP_filter,'same');
% plot(abs(fft(bandlimited_analog_signal)))
% plot(bandlimited_analog_signal)
bandlimited_and_modulated_signal = bandlimited_analog_signal;
end
Демодуляция ASK (ask_demodulate.m) (в основном это просто обнаружение огибающей, для которого я использовал преобразование Гильберта)
function [demodulated_signal] = ask_demodulate(received_signal, fs, F_on, bit_rate)
% Amplitude shift keying: Demodulation
% Dang Manh Truong (dangmanhtruong@gmail.com)
demodulated_signal = abs(hilbert(received_signal));
end
Пожалуйста, скажите мне, почему это не работает? большое спасибо
источник
Ответы:
Как вы уже поняли, сложная часть цифровой связи - это синхронизация несущей, символа и кадра, а также оценка / выравнивание канала.
Плохая новость в том, что вы не можете обойти эти проблемы. Хорошая новость заключается в том, что реализовать их не так сложно, если вы ограничиваете себя узкополосным BPSK. Я знаю, потому что я сделал это сам, как и мои (старшекурсники) студенты (см. Http://ieeexplore.ieee.org/document/5739249/ )
Одним простым предложением обойти проблему синхронизации несущей является использование AM DSB-LC для преобразования с повышением частоты сигнала основной полосы частот. Затем вы можете использовать детектор огибающей без несущей и фазовой синхронизации. Это будет стоить вам энергоэффективности, но это не является приоритетом в вашем случае.
Другое простое предложение - выполнять «пакетную обработку» вместо «обработки в реальном времени»; Это означает, что весь полученный сигнал сохраняется и обрабатывается впоследствии. Это гораздо проще реализовать, чем потоковую обработку или обработку в реальном времени.
Мое более существенное предложение состоит в том, чтобы прочитать эту книгу: Джонсон, Сетэрес и Кляйн, «Проектирование программного приемника», Кембридж. Это объясняет в очень четких терминах каждый кусочек получателя и содержит множество примеров кода Matlab. Есть похожая книга Стивена Треттера о внедрении системы связи на DSP (я не могу вспомнить точное название прямо сейчас).
Удачи; и, пожалуйста, задавайте новые, более конкретные вопросы, если они у вас есть.
источник
В конце я использовал DTMF (двухтональная многочастотная сигнализация). Оригинальный DTMF имеет 16 сигналов, каждый из которых использует комбинацию из 2 частот. Но здесь я использовал только «1» (697 Гц и 1209 Гц) и «0» (941 Гц и 1336 Гц)
Краткое описание того, как работает код:
Сторона приемника сначала использует 2 смехотворно высоких упорядоченных и смехотворно узких полосовых фильтра для выделения частотных компонентов «0» и «1» соответственно:
filter_order = 1000;
После того, как это будет сделано, мы найдем начало и конец каждого сигнала «1» и «0». Код от https://github.com/codyaray/dtmf-signaling . В основном он находит период молчания, который составляет не менее 10 мс и любой период тона более 100 мс):
(Сверху вниз: нулевой сигнал, сигнал после фильтра скользящего среднего, разность сигналов после удаления ниже порогового значения, сигнал после порогового значения)
Затем мы собираем биты и конвертируем обратно в текст :)
Демонстрация видео: https://www.youtube.com/watch?v=vwQVmNnWa4s , где я отправляю текст «Xin chao» между моим ноутбуком и компьютером моего брата :)
P / S: Первоначально я делал это, потому что мой учитель по цифровой коммуникации сказал, что, кто бы ни делал это, он получит оценку A без необходимости сдавать итоговый экзамен, но я смог сделать это только после экзамена. Так что здесь идут все мои усилия :(
P / S2: я получил C + :(
источник
Если вам нужна библиотека с открытым исходным кодом с очень хорошей синхронизацией, я рекомендую https://github.com/jgaeddert/liquid-dsp, которая использует msequence для выравнивания, затем выполняет выравнивание и демодулирует полезную нагрузку. Я сделал аудио модем, который работает на вершине, и он работает довольно хорошо, поэтому, если ничего другого, методы жидкости должны быть полезны
источник