Przesyłaj dane przez dźwięk między 2 komputerami (bardzo bliska odległość)

12

Piszę przykład przesyłania danych przez dźwięk między 2 komputerami. Niektóre wymagania:

  • Odległość jest bardzo bliska, tzn. 2 komputery są w zasadzie przylegające do siebie

  • Bardzo mało hałasu (nie sądzę, żeby mój nauczyciel włączył rockową piosenkę jako źródło hałasu)

  • Błąd jest dopuszczalny: na przykład, jeśli wyślę „Komunikacja radiowa”, to jeśli drugi komputer odbierze „RadiQ communEcation”, to również jest w porządku.

  • Jeśli to możliwe: brak nagłówka, flagi, sumy kontrolnej, .... ponieważ chcę tylko bardzo prosty przykład demonstrujący podstawy przesyłania danych przez dźwięk. Nie musisz być fantazyjny.

Próbowałem użyć Audio Shift Keying Shift według tego linku:

Lab 5 APRS (automatyczny system raportowania paczek)

i uzyskałem kilka wyników: Moja strona Github

ale to nie wystarczy. Nie wiem, jak wykonać odzyskiwanie zegara, synchronizację, ... (link ma mechanizm synchronizacji fazy jako mechanizm odzyskiwania czasu, ale najwyraźniej to nie wystarczyło).

Myślę więc, że powinienem znaleźć prostsze podejście. Znaleziono link tutaj:

Dane do audio i wstecz. Modulacja / demodulacja z kodem źródłowym

ale OP nie wdrożył metody sugerowanej w odpowiedzi, więc obawiam się, że może być bardzo złożona. Również nie rozumiem jasno metody dekodowania zaproponowanej w odpowiedzi:

Dekoder jest nieco bardziej skomplikowany, ale oto zarys:

Opcjonalnie filtruj pasmowo-filtrowy próbkowany sygnał około 11 kHz. Poprawi to wydajność w głośnym otoczeniu. Filtry FIR są dość proste i istnieje kilka apletów projektu online, które wygenerują filtr dla Ciebie.

Przekrocz próg sygnału. Każda wartość powyżej 1/2 maksymalnej amplitudy wynosi 1, a każda wartość poniżej wynosi 0. To zakłada, że ​​próbkowałeś cały sygnał. Jeśli odbywa się to w czasie rzeczywistym, albo wybierasz stały próg, albo wykonujesz automatyczną kontrolę wzmocnienia, w której śledzisz maksymalny poziom sygnału przez pewien czas.

Wyszukaj początek kropki lub myślnika. Prawdopodobnie chcesz zobaczyć przynajmniej pewną liczbę jedynek w okresie kropki, aby uznać próbki za kropkę. Następnie kontynuuj skanowanie, aby sprawdzić, czy to kreska. Nie oczekuj idealnego sygnału - zobaczysz kilka zer na środku swoich zer i kilka jedynek na środku swoich zer. Jeśli występuje niewielki hałas, odróżnienie okresów włączenia od okresów wyłączenia powinno być dość łatwe.

Następnie odwróć powyższy proces. Jeśli widzisz myślnik wypchnij 1 bit do bufora, jeśli kropka popchnie zero.

Nie rozumiem, ile cyfr 1 sklasyfikowano jako kropkę ... Więc jest wiele rzeczy, których teraz nie rozumiem. Proszę zasugerować mi prostą metodę przesyłania danych dźwiękiem, aby zrozumieć proces. Dziękuję Ci bardzo :)

AKTUALIZACJA:

Zrobiłem trochę kodu Matlaba, który wydaje się (nieco) działać. Najpierw moduluję sygnał za pomocą kluczowania z przesunięciem amplitudy (częstotliwość próbkowania 48000 Hz, F_on = 5000 Hz, przepływność = 10 bitów / s), a następnie dodaję go z nagłówkiem i sekwencją końcową (oczywiście je również moduluję). Nagłówek i sekwencja końcowa zostały wybrane na zasadzie ad hoc (tak, to był hack):

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];

Następnie przesyłam je dźwiękiem i nagrywam na smartfonie. Następnie wysyłam nagrany dźwięk z powrotem do mojego komputera, użyj innego fragmentu kodu, aby odczytać dźwięk. Następnie koreluję odbierany sygnał (jeszcze nie zdemodulowany) z modulowanym nagłówkiem i sekwencją końcową, aby znaleźć początek i koniec. Następnie biorę tylko odpowiedni sygnał (od początku do końca, jak w części dotyczącej korelacji). Następnie demoduluję i próbkuję, aby znaleźć dane cyfrowe. Oto 3 pliki audio:

  • „DigitalCommunication_ask”: Link tutaj przesyła tekst „Komunikacja cyfrowa”. Stosunkowo bezszumowy, chociaż na początku i na końcu słychać szum tła. Jednak wynik pokazał tylko „Digital Commincatio”

  • „HelloWorld_ask”: Link tutaj wysyła tekst „Hello world”. Bez hałasu jak „DigitalCommunication_ask”. Jednak wynik tego był poprawny

  • „HelloWorld_noise_ask”: Link tutaj wysyła tekst „Hello world”. Jest jednak trochę hałasu, który wydałem (właśnie powiedziałem kilka przypadkowych rzeczy „A, B, C, D, E,…” podczas transmisji). Niestety ten zawiódł

Oto kod nadawcy (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);

W przypadku odbiornika (odbiornik.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

ZAPYTAJ kod modulacji (ask_modulate):

function [bandlimited_and_modulated_signal] = ask_modulate(bit_stream, fs, F_on, bit_rate)
% Amplitude shift keying: Modulation
% Dang Manh Truong ([email protected])
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 demodulation (ask_demodulate.m) (Zasadniczo jest to po prostu wykrywanie obwiedni, dla którego użyłem transformacji Hilberta)

function [demodulated_signal] = ask_demodulate(received_signal, fs, F_on, bit_rate)
% Amplitude shift keying: Demodulation
% Dang Manh Truong ([email protected])

demodulated_signal = abs(hilbert(received_signal));

end

Powiedz mi, dlaczego to nie działa? Dziękuję Ci bardzo

Dang Manh Truong
źródło
Teoretycznie (w środowisku pozbawionym hałasu) wdrożenie tego byłoby trywialne, ale w praktyce jest to o wiele trudniejsze. Jednak zależy to od rodzaju informacji, które próbujesz wysłać. Przekazywanie tekstu byłoby niezwykle trudne, ponieważ nawet najmniejszy szum spowodowałby, że tekst nie byłby rozpoznawany.
dsp_user,
@dsp_user Próbuję wysłać tekst. Mogę żyć z pewnym błędem (np. „Audio” -> „Apdio”) :) Nie rozumiem też, że na przykład dla Amplitude Shift Keying, gdy masz 1, wysyłasz falę sinusoidalną, 0 to nic więcej jak znasz pierwsze 0? Mam na myśli w środowisku bez hałasu, ale przed pierwszym 1 byłoby dużo 0, prawda? Więc skąd to wiesz?
Dang Manh Truong,
Proponuję spojrzeć na coś w stylu starego modemu 14.4 na pomysły.
@StanleyPawlukiewicz Poczyniłem pewne postępy. Sprawdź aktualizację. Dziękuję Ci bardzo.
Dang Manh Truong,
Jest wiele do skomentowania. Możesz zajrzeć do sekwencji Barkera dla twojej preambuły, biorąc pod uwagę, że używasz preambuł

Odpowiedzi:

8

Jak zauważyłeś, trudną częścią komunikacji cyfrowej jest synchronizacja nośnej, symbolu i ramki oraz szacowanie / wyrównanie kanału.

Zła wiadomość jest taka, że ​​nie można obejść tych problemów. Dobra wiadomość jest taka, że ​​ich wdrożenie nie jest takie trudne, o ile ograniczysz się do wąskopasmowego BPSK. Wiem, ponieważ sam to zrobiłem, podobnie jak moi (studenci) studenci (patrz http://ieeexplore.ieee.org/document/5739249/ )

Jedną z prostych propozycji obejścia problemu synchronizacji nośnej jest użycie AM DSB-LC do konwersji sygnału w paśmie podstawowym. Następnie możesz użyć detektora obwiedni bez synchronizacji fazy i nośnej. Będzie to kosztować wydajność energetyczną, ale w twoim przypadku nie jest to priorytetem.

Inną prostą sugestią jest „przetwarzanie wsadowe” zamiast „przetwarzanie w czasie rzeczywistym”; oznacza to, że zapisz cały odebrany sygnał, a następnie przetworz go. Jest to o wiele łatwiejsze do wdrożenia niż przetwarzanie strumieniowe lub przetwarzanie w czasie rzeczywistym.

Bardziej merytoryczną propozycją jest przeczytanie tej książki: Johnson, Sethares i Klein, „Software Receiver Design”, Cambridge. Wyjaśnia bardzo jasno każdy element odbiornika i zawiera wiele przykładowych kodów Matlab. Istnieje podobna książka Stevena Trettera o wdrażaniu systemu komunikacji na DSP (nie pamiętam teraz dokładnego tytułu).

Powodzenia; i proszę zadawać nowe, bardziej szczegółowe pytania, jeśli je masz.

MBaz
źródło
Przeczytałem twój artykuł. Tak trzymaj! Jedno pytanie: W artykule mówiłeś o kilku metodach wykorzystywanych przez studentów do znalezienia odpowiedzi kanału (za pomocą impulsu, fal sinusoidalnych, ...). Czy muszę też znaleźć odpowiedź kanału? :)
Dang Manh Truong
1
Dziękuję za miłe słowa :) Chodzi o to, że chcesz mieć pewność, że transmitujesz w paśmie częstotliwości, w którym odpowiedź kanału jest płaska; w przeciwnym razie będziesz potrzebować korektora w odbiorniku. Jeśli nie chcesz oszacować odpowiedzi kanału, możesz użyć bardzo niskiej szybkości transmisji danych (powiedzmy 100 b / s) na częstotliwości, z którą wszystkie urządzenia audio powinny czuć się komfortowo (powiedzmy 5000 Hz).
MBaz,
1
@DangManhTruong Jeszcze jedno: pamiętaj, aby używać impulsów o ograniczonej przepustowości, takich jak podniesiony cosinus o pierwiastku kwadratowym, a nie impulsów kwadratowych o dużej szerokości pasma i bardzo prawdopodobne, że zniekształcą się.
MBaz,
Przeczytałem książkę Projekt odbiornika oprogramowania, jak zasugerowałeś (właściwie przejrzałem większość z nich i skoncentrowałem się na rozdziale 8: Bity do symboli do sygnałów). Mam więc kilka pytań. Powiedziałeś coś o pulsach, ale w przykładzie książki wykorzystali okno Hamminga jako puls, czy to w porządku, jeśli to zrobię? I czy rozumiem poprawnie: najpierw modulujesz sygnał za pomocą, powiedzmy, ZAPYTAJ, a następnie używasz kształtowania impulsowego. Następnie na odbiorniku najpierw korelujesz z sygnałem pulsacyjnym, aby otrzymać sygnał modulowany. Potem demodulujesz. Czy to jest poprawne?
Dang Manh Truong,
A jeśli chcę wysłać dane w formie pakietu, z nagłówkiem na początku i na końcu, powiedzmy 1 1 1 1 1 1 1 1, więc powinienem dołączyć je z danymi, następnie modulować, a następnie kształtować pulsacyjnie. Na odbiorniku skorelowałem odbierany sygnał z kształtem impulsu (podniesiony cosinus o pierwiastku kwadratowym, ...), a następnie muszę zdemodulować sygnał, a następnie skorelować go z nagłówkiem. Czy moje rozumowanie jest prawidłowe?
Dang Manh Truong,
4

W końcu zastosowałem DTMF (sygnalizacja wieloczęstotliwościowa Dual Tone). Oryginalny DTMF ma 16 sygnałów, każdy przy użyciu kombinacji 2 częstotliwości. Ale tutaj użyłem tylko „1” (697 Hz i 1209 Hz) i „0” (941 Hz i 1336 Hz)

Zarys działania kodu:

  • Nadawca konwertuje tekst na binarny, a następnie przesyła sygnały DTMF „0” / „1” (tutaj taktowanie wynosi 0,3 s dla czasu trwania tonu i 0,1 s dla okresu ciszy między tonami). Kod transmisji pochodzi z: https://sites.google.com/a/nd.edu/adsp-nik-kleber/home/advanced-digital-signal-processing/project-3-touch-tone . Najwyraźniej autor zastosował marginalnie stabilny filtr IIR do wdrożenia cyfrowego oscylatora.
  • Strona odbiornika najpierw wykorzystuje 2 absurdalnie wysokie uporządkowane i śmiesznie wąskie filtry pasmowoprzepustowe, aby wyodrębnić odpowiednio składowe częstotliwości „0” i „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);
    

Po wykonaniu tej czynności znajdziemy początek i koniec każdego sygnału „1” i „0”. Kod pochodzi z https://github.com/codyaray/dtmf-signaling . Zasadniczo znajduje okres ciszy wynoszący co najmniej 10 ms i dowolny okres tonu dłuższy niż 100 ms):

wprowadź opis zdjęcia tutaj

(Od góry do dołu: sygnał zerowy, sygnał po ruchomym filtrze średnim, różnica sygnału po usunięciu tych poniżej progu, sygnał po progowaniu)

  • Najpierw znormalizowany jest wynik z poprzedniego kroku, a następnie przeszedł przez filtr średniej ruchomej (przy rozmiarze filtra równym 10 ms * Fs). Jeśli wykreślimy wynik, zobaczymy, że kształt „0” i „1” jest wyraźnie widoczny. Myślę więc, że w tym przypadku działa to jak detektor kopert.
  • Następnie cały sygnał poniżej pewnego progu zostaje odcięty (wybrałem 0,1).
  • Na koniec znajdź wszystkie przedziały powyżej progu, który ma przedział czasowy większy niż 100 ms (zwróć uwagę, że obraz nie jest odtwarzalny z kodu, musisz przekopać się, aby go zrobić)

Następnie montujemy bity i przekształcamy z powrotem w tekst :)

Demo wideo: https://www.youtube.com/watch?v=vwQVmNnWa4s , gdzie wysyłam tekst „Xin chao” między moim laptopem a komputerem mojego brata :)

P / S: Początkowo to zrobiłem, ponieważ mój nauczyciel komunikacji cyfrowej powiedział, że ktokolwiek to zrobi, dostanie A bez konieczności zdawania egzaminu końcowego, ale mogłem to zrobić dopiero po egzaminie. Oto wszystkie moje wysiłki :(

P / S2: Mam C + :(

Dang Manh Truong
źródło
0

Jeśli chcesz mieć bibliotekę open source z bardzo dobrą synchronizacją, polecam https://github.com/jgaeddert/liquid-dsp, który używa sekwencji do wyrównania, a następnie dokonuje korekcji i demoduluje ładunek. Zrobiłem modem audio, który działa na górze i działa całkiem dobrze, więc jeśli nic więcej, metody cieczy powinny być pomocne

Brian Armstrong
źródło