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

12

Я пишу пример передачи данных через звук между двумя компьютерами. Некоторые требования:

  • Расстояние очень близко, то есть 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

Пожалуйста, скажите мне, почему это не работает? большое спасибо

Данг Ман Чыонг
источник
Теоретически (в бесшумной среде) это было бы тривиально реализовать, но на практике это намного сложнее. Тем не менее, это зависит от типа информации, которую вы пытаетесь отправить. Текст будет очень трудно надежно передать, потому что даже малейший шум сделает текст неузнаваемым.
dsp_user
@dsp_user Я пытаюсь отправить текст. Я могу жить с некоторой ошибкой (например, «Аудио» -> «Apdio») :) Также я не очень понимаю, что, например, для Amplitude Shift Keying, когда у вас есть 1, то вы посылаете синусоидальную волну, 0, то ничего, кроме как ты знаешь первый 0? Я имею в виду в бесшумной среде, но до первого 1 было бы много 0, верно? Тогда откуда ты это знаешь?
Данг
Я предлагаю вам взглянуть на что-то вроде старомодного 14.4 модема для идей.
@StanleyPawlukiewicz Я добился определенного прогресса. Пожалуйста, проверьте обновление. Большое спасибо.
Данг Мань Чыонг
Есть много комментариев. Возможно, вы захотите взглянуть на последовательности Баркера для вашей преамбулы, учитывая, что вы используете преамбулы

Ответы:

8

Как вы уже поняли, сложная часть цифровой связи - это синхронизация несущей, символа и кадра, а также оценка / выравнивание канала.

Плохая новость в том, что вы не можете обойти эти проблемы. Хорошая новость заключается в том, что реализовать их не так сложно, если вы ограничиваете себя узкополосным BPSK. Я знаю, потому что я сделал это сам, как и мои (старшекурсники) студенты (см. Http://ieeexplore.ieee.org/document/5739249/ )

Одним простым предложением обойти проблему синхронизации несущей является использование AM DSB-LC для преобразования с повышением частоты сигнала основной полосы частот. Затем вы можете использовать детектор огибающей без несущей и фазовой синхронизации. Это будет стоить вам энергоэффективности, но это не является приоритетом в вашем случае.

Другое простое предложение - выполнять «пакетную обработку» вместо «обработки в реальном времени»; Это означает, что весь полученный сигнал сохраняется и обрабатывается впоследствии. Это гораздо проще реализовать, чем потоковую обработку или обработку в реальном времени.

Мое более существенное предложение состоит в том, чтобы прочитать эту книгу: Джонсон, Сетэрес и Кляйн, «Проектирование программного приемника», Кембридж. Это объясняет в очень четких терминах каждый кусочек получателя и содержит множество примеров кода Matlab. Есть похожая книга Стивена Треттера о внедрении системы связи на DSP (я не могу вспомнить точное название прямо сейчас).

Удачи; и, пожалуйста, задавайте новые, более конкретные вопросы, если они у вас есть.

MBaz
источник
Я прочитал вашу газету. Продолжайте хорошую работу! Один вопрос: в статье вы рассказали о нескольких методах, используемых студентами для нахождения отклика канала (с использованием импульса, синусоидальной волны ...). Нужно ли мне найти ответ канала? :)
Dang
1
Спасибо за ваши добрые слова :) Дело в том, что вы хотите убедиться, что вы передаете в полосе частот, где отклик канала плоский; в противном случае вам понадобится эквалайзер в приемнике. Если вы не хотите оценивать отклик канала, то вы можете использовать очень низкую скорость передачи данных (скажем, 100 б / с) на частоте, с которой должно быть удобно все аудиооборудование (скажем, 5000 Гц).
MBaz
1
@DangManhTruong Еще одна вещь: обязательно используйте импульсы с ограниченной полосой пропускания, такие как квадратный корень с повышенным косинусом, а не прямоугольные импульсы, которые имеют большую полосу пропускания и с большой вероятностью будут испытывать искажения.
MBaz
Я прочитал книгу «Дизайн программного приемника», как вы и предлагали (на самом деле я пролистал большую часть ее и сосредоточился на главе 8: биты от символов до сигналов). Итак, у меня есть несколько вопросов. Вы сказали что-то об импульсах, но в примере книги они использовали окно Хемминга в качестве импульса, хорошо, если я это сделаю? И правильно ли я понимаю: сначала вы модулируете сигнал с помощью, скажем, ASK, затем вы используете формирование импульсов. Затем на приемнике вы сначала коррелируете с импульсным сигналом, чтобы получить модулированный сигнал. Тогда вы демодулируете. Это правильно?
Данг Мань Чыонг
И если я хочу отправить данные в форме пакета с заголовком в начале и в конце, скажем, 1 1 1 1 1 1 1 1, поэтому я должен добавить их к данным, затем модулировать их, а затем сформировать импульс. На приемнике я бы коррелировал принятый сигнал с формой импульса (квадратный корень с приподнятым косинусом, ..), затем я должен демодулировать сигнал, после этого коррелировать с заголовком. Правильно ли мое понимание?
Данг Мань Чыонг
4

В конце я использовал DTMF (двухтональная многочастотная сигнализация). Оригинальный DTMF имеет 16 сигналов, каждый из которых использует комбинацию из 2 частот. Но здесь я использовал только «1» (697 Гц и 1209 Гц) и «0» (941 Гц и 1336 Гц)

Краткое описание того, как работает код:

  • Отправитель преобразует текст в двоичный формат, а затем передает DTMF-сигналы «0» / «1» (здесь время составляет 0,3 с для длительности тона и 0,1 с для периода молчания между тонами). Код передачи взят с: https://sites.google.com/a/nd.edu/adsp-nik-kleber/home/advanced-digital-signal-processing/project-3-touch-tone . Видимо, автор использовал предельно стабильный БИХ-фильтр для реализации цифрового генератора.
  • Сторона приемника сначала использует 2 смехотворно высоких упорядоченных и смехотворно узких полосовых фильтра для выделения частотных компонентов «0» и «1» соответственно:

    filter_order = 1000;

    one_band = [[((2696)/Fs) ((2698)/Fs)] [((21208)/Fs) ((21210)/Fs)]];
    
    one_dtmf_filter = fir1(filter_order, one_band);
    
    zero_band = [[((2940)/Fs) ((2942)/Fs)] [((21335)/Fs) ((21337)/Fs)]];
    
    zero_dtmf_filter = fir1(filter_order, zero_band);
    

После того, как это будет сделано, мы найдем начало и конец каждого сигнала «1» и «0». Код от https://github.com/codyaray/dtmf-signaling . В основном он находит период молчания, который составляет не менее 10 мс и любой период тона более 100 мс):

введите описание изображения здесь

(Сверху вниз: нулевой сигнал, сигнал после фильтра скользящего среднего, разность сигналов после удаления ниже порогового значения, сигнал после порогового значения)

  • Сначала нормализуется результат предыдущего шага, затем проходит фильтр скользящей средней (размер фильтра равен 10 мс * Fs). Если мы нанесем результат, мы увидим, что форма «0» и «1» может быть ясно видна. Так что я думаю, что в этом случае он работает как детектор огибающей.
  • Затем весь сигнал ниже определенного порога обрезается (я выбрал 0.1).
  • Наконец, найдите все интервалы выше порогового значения, которое имеет временной интервал более 100 мс (обратите внимание, что изображение не воспроизводится из кода, вам придется покопаться, чтобы сделать его)

Затем мы собираем биты и конвертируем обратно в текст :)

Демонстрация видео: https://www.youtube.com/watch?v=vwQVmNnWa4s , где я отправляю текст «Xin chao» между моим ноутбуком и компьютером моего брата :)

P / S: Первоначально я делал это, потому что мой учитель по цифровой коммуникации сказал, что, кто бы ни делал это, он получит оценку A без необходимости сдавать итоговый экзамен, но я смог сделать это только после экзамена. Так что здесь идут все мои усилия :(

P / S2: я получил C + :(

Данг Ман Чыонг
источник
0

Если вам нужна библиотека с открытым исходным кодом с очень хорошей синхронизацией, я рекомендую https://github.com/jgaeddert/liquid-dsp, которая использует msequence для выравнивания, затем выполняет выравнивание и демодулирует полезную нагрузку. Я сделал аудио модем, который работает на вершине, и он работает довольно хорошо, поэтому, если ничего другого, методы жидкости должны быть полезны

Брайан Армстронг
источник