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


12

Я пишу приклад передачі даних через звук між комп'ютерами 2. Деякі вимоги:

  • Відстань дуже близька, тобто два комп’ютери в основному примикають один до одного

  • Дуже мало шуму (я не думаю, що мій учитель увімкнув би рок-пісню як джерело шуму)

  • Помилка є прийнятною. Наприклад, якщо я надсилаю "Радіозв'язок", то якщо інший комп'ютер отримує "RadiQ communEcation", це також добре.

  • Якщо можливо: Без заголовка, прапора, контрольної суми, .... оскільки я просто хочу дуже базовий приклад, що демонструє основи передачі даних через звук. Не потрібно бути фантазією.

Я спробував скористатися звуковим перемиканням частоти звуку за цим посиланням:

Лабораторія 5 APRS (Система автоматичної звітності про пакети)

і отримав деякі результати: Моя сторінка Github

але цього недостатньо. Я не знаю, як зробити відновлення синхронізації, синхронізацію, ... (посилання має Phase Locked Loop як механізм відновлення синхронізації, але, мабуть, це було недостатньо).

Тому я думаю, що мені слід знайти простіший підхід. Тут знайдено посилання:

Дані на аудіо та назад. Модуляція / демодуляція з вихідним кодом

але ОП не застосував запропонований у відповіді метод, тому я боюся, що це може бути дуже складним. Також я чітко не розумію метод декодування, запропонований у відповіді:

Декодер трохи складніший, але ось такий контур:

Необов'язково смуговий фільтр відібраного сигналу близько 11 кГц. Це поліпшить ефективність роботи в умовах шумного оточення. Фільтри FIR досить прості, і є декілька аплет в Інтернеті, які генерують фільтр для вас.

Поріг сигналу. Кожне значення вище 1/2 максимальної амплітуди - 1, кожне значення нижче - 0. Це передбачає, що ви відібрали весь сигнал. Якщо це в режимі реального часу, ви або вибираєте фіксований поріг, або робите якийсь автоматичний контроль посилення, де ви відстежуєте максимальний рівень сигналу протягом певного часу.

Сканування на початок крапки або тире. Ймовірно, ви хочете побачити принаймні певну кількість значень 1 у точковий період, щоб вважати зразки крапкою. Потім продовжуйте сканувати, щоб побачити, чи це тире. Не чекайте ідеального сигналу - в середині 1-х ви побачите декілька "0" та "1" в середині 0-х. Якщо шуму мало, то диференціювати періоди "увімкнення" від періодів "вимкнення" слід досить просто.

Потім поверніть вищезазначений процес. Якщо ви бачите тире, натисніть 1 біт на свій буфер, якщо крапка натисне нуль.

Я не розумію, скільки 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);

Для приймача (приймача.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 Я намагаюся надіслати текст. Я можу жити з деякою помилкою (наприклад, "Audio" -> "Apdio") :) Також я не дуже розумію, що, наприклад, для клавіш Shift Amplitude Shift, коли у вас є 1, ви надсилаєте синусоїду, 0 тоді нічого, але як ти знаєш перший 0? Я маю на увазі в безшумному середовищі, але до першого 1 було б багато 0 так? Тоді як ти це знаєш?
Dang Manh Truong

Я пропоную вам подивитися на щось на зразок старомодного модему 14.4 для ідей.

@StanleyPawlukiewicz Я досяг певного прогресу. Перевірте оновлення. Дуже дякую.
Данг Ман Труонг

Є що багато коментувати. Ви можете переглянути послідовності Баркера для своєї преамбули, враховуючи, що ви використовуєте преамбули

Відповіді:


8

Як ви зрозуміли, важкою частиною цифрових комунікацій є синхронізація носіїв, символів і кадрів та оцінка / вирівнювання каналів.

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

Одне з простих пропозицій вирішити проблему синхронізації несучої - використовувати AM DSB-LC для підвищення конвертації базового сигналу. Потім ви можете використовувати детектор конвертів без несучої та фазової синхронізації. Це обійдеться вам в енергоефективності, але це не є пріоритетним у вашому випадку.

Ще одна проста пропозиція - зробити "пакетну обробку" замість "обробки в режимі реального часу"; що це означає, зберігайте весь отриманий сигнал і обробляйте його згодом. Це набагато простіше здійснити, ніж потокова чи обробка в режимі реального часу.

Моя більш вагома пропозиція - прочитати цю книгу: Джонсон, Сетарес і Кляйн, "Дизайн програмного приймача", Кембридж. Він дуже чітко пояснює кожну частину приймача і має багато прикладу Matlab-коду. Існує подібна книга Стівена Треттера про впровадження системи зв’язку на DSP (я не можу пригадати точну назву зараз).

Удачі; і, будь ласка, задайте нові, більш конкретні запитання, якщо у вас є.


Я прочитав твій документ. Продовжуй гарно працювати! Одне запитання: У роботі ви розповідали про кілька методів, якими користуються учні для пошуку реакції каналу (використовуючи імпульс, синусоїду, ..). Чи потрібно мені також знайти відповідь каналу? :)
Dang Manh Truong

1
Дякуємо за добрі слова :) Справа в тому, що ви хочете переконатися, що ви передаєте по частотному діапазону, де відповідь каналу рівна; інакше вам знадобиться еквалайзер у приймачі. Якщо ви не хочете оцінювати реакцію каналу, ви можете зробити дуже низьку швидкість передачі даних (скажімо, 100 b / s) на частоті, з якою має бути комфортним все аудіообладнання (скажімо, 5000 Гц).
MBaz

1
@DangManhTruong Ще одне: не забудьте використовувати імпульси з обмеженою пропускною здатністю, такі як косинус, піднятий квадратним коренем, а не квадратні імпульси, які мають велику пропускну здатність і дуже ймовірно зазнають спотворень.
MBaz

Я прочитав книгу "Дизайн приймача програмного забезпечення", як ви запропонували (насправді я проскочив більшість із них і зосередився на главі 8: Біти до символів до сигналів). Тож у мене є запитання. Ви щось сказали про імпульси, але в прикладі книги вони використовували вікно Хеммінга як імпульс, це добре, якщо я це роблю? І чи правильно я розумію: спочатку ви модулюєте сигнал, скажімо, ASK, потім використовуєте форму імпульсу. Потім на приймачі ви спочатку співвідносите імпульсний сигнал для отримання модульованого сигналу. Потім ти демодулюєш. Це правильно?
Данг Ман Труонг

І якщо я хочу надіслати дані в пакетній формі, із заголовком на початку та в кінці, скажімо, 1 1 1 1 1 1 1 1, тож я повинен додати їх до даних, потім модулювати, а потім імпульсно формувати. На приймачі я співвідносив би отриманий сигнал з формою імпульсу (квадратний корінь, піднятий косинусом, ..), тоді я повинен демодулювати сигнал, після чого співвіднести із заголовком. Чи правильно моє розуміння?
Dang Manh Truong

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 . Мабуть, автор застосував незначно стабільний IIR фільтр для реалізації цифрового осцилятора.
  • Сторона приймача спочатку використовує 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 , де я надсилаю текст "Сінь чао" між моїм ноутбуком та комп'ютером мого брата :)

P / S: Спочатку я це робив, тому що мій вчитель цифрового спілкування сказав, що той, хто це зробив, отримає А без того, щоб робити остаточний іспит, але мені вдалося це зробити лише після іспиту. Тож тут ідуть усі мої зусилля :(

P / S2: я отримав C + :(


0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.