Trasmissione dei dati attraverso l'audio tra 2 computer (distanza molto ravvicinata)


12

Sto scrivendo un esempio sulla trasmissione di dati attraverso l'audio tra computer 2. Alcuni requisiti:

  • La distanza è molto vicina, ovvero i 2 computer sono sostanzialmente adiacenti l'uno all'altro

  • Molto poco rumore (non credo che il mio insegnante avrebbe attivato una canzone rock come fonte di rumore)

  • L'errore è accettabile: ad esempio se invio "Comunicazione radio", anche se l'altro computer riceve "RadiQ communEcation", va bene lo stesso.

  • Se possibile: nessuna intestazione, flag, checksum, .... poiché voglio solo un esempio molto semplice che dimostri le basi della trasmissione dei dati attraverso il suono. Non c'è bisogno di essere fantasiosi.

Ho provato a utilizzare la codifica di spostamento della frequenza audio in base a questo link:

Lab 5 APRS (sistema di reportistica automatica dei pacchetti)

e ho ottenuto alcuni risultati: La mia pagina Github

Ma non è abbastanza. Non so come fare il recupero del clock, la sincronizzazione, ... (il collegamento ha un Phase Locked Loop come meccanismo di recupero del timing, ma apparentemente non era abbastanza).

Quindi penso che dovrei trovare un approccio più semplice. Ho trovato un link qui:

Dati in audio e ritorno. Modulazione / demodulazione con codice sorgente

ma l'OP non ha implementato il metodo suggerito nella risposta, quindi temo che potrebbe essere molto complesso. Inoltre non capisco chiaramente il metodo di decodifica suggerito nella risposta:

Il decoder è un po 'più complicato ma ecco uno schema:

Facoltativamente, il filtro passa-banda filtra il segnale campionato intorno a 11Khz. Ciò migliorerà le prestazioni in un ambiente rumoroso. I filtri FIR sono piuttosto semplici e ci sono alcune applet di design online che genereranno il filtro per te.

Soglia il segnale. Ogni valore superiore a 1/2 dell'ampiezza massima è 1 ogni valore inferiore è 0. Ciò presuppone che sia stato campionato l'intero segnale. Se questo è in tempo reale, scegli una soglia fissa o esegui una sorta di controllo automatico del guadagno in cui segui il livello massimo del segnale nel tempo.

Cerca l'inizio del punto o del trattino. Probabilmente vuoi vedere almeno un certo numero di 1 nel tuo punto periodo per considerare i campioni come un punto. Quindi continua la scansione per vedere se si tratta di un trattino. Non aspettarti un segnale perfetto: vedrai alcuni 0 nel mezzo dei tuoi 1 e alcuni 1 nel mezzo dei tuoi 0. Se c'è poco rumore, differenziare i periodi "on" dai periodi "off" dovrebbe essere abbastanza semplice.

Quindi invertire il processo sopra descritto. Se vedi trattino spingere un bit per il tuo buffer, se un punto spinge uno zero.

Non capisco quanti 1 sono prima di classificarlo come punto, ... Quindi ci sono molte cose che non capisco in questo momento. Per favore, suggeriscimi un metodo semplice per trasmettere dati attraverso il suono in modo che io possa capire il processo. Grazie mille :)

AGGIORNARE:

Ho creato un codice Matlab che sembra essere (in qualche modo) operativo. Per prima cosa modulo il segnale usando il key shift di ampiezza (frequenza di campionamento 48000 Hz, F_on = 5000 Hz, bit rate = 10 bit / s), quindi lo aggiungo con un'intestazione e una sequenza finale (ovviamente modulandoli anche). L'intestazione e la sequenza finale sono state scelte su una base ad hoc (sì, era un trucco):

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

Quindi le trasmetto attraverso il suono e le ho registrate con il mio smartphone. Quindi rispedisco l'audio registrato al mio computer, uso un altro codice per leggere l'audio. Quindi correlo il segnale ricevuto (non ancora demodulato) con l'intestazione modulata e la sequenza finale per scoprire l'inizio e la fine. Successivamente prendo solo il segnale rilevante (dall'inizio alla fine, come si trova nella parte di correlazione). Quindi demodulo e campiono per trovare i dati digitali. Ecco 3 file audio:

  • "DigitalCommunication_ask": Link qui invia il testo "Comunicazione digitale". Relativamente privo di rumore sebbene si possa sentire un po 'di rumore di fondo all'inizio e alla fine. Tuttavia, il risultato ha mostrato solo "Digital Commincatio"

  • "HelloWorld_ask": Link qui invia il testo "Hello world". Senza rumore come "DigitalCommunication_ask". Tuttavia, il risultato per questo è stato corretto

  • "HelloWorld_noise_ask": Link qui invia il testo "Hello world". Tuttavia c'è un po 'di rumore che ho fatto (ho appena detto alcune cose a caso "A, B, C, D, E, ..." durante la trasmissione). Purtroppo questo ha fallito

Ecco il codice per il mittente (mittente.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);

Per il ricevitore (receiver.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

Chiedere il codice di modulazione (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

CHIEDERE la demodulazione (ask_demodulate.m) (Fondamentalmente si tratta solo del rilevamento dell'inviluppo, per il quale ho usato la trasformazione di Hilbert)

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

Per favore, dimmi perché non funziona? Grazie mille


In teoria (in un ambiente privo di rumore), questo sarebbe banale da implementare, ma in pratica è molto più difficile. Tuttavia, dipende dal tipo di informazioni che stai tentando di inviare. Il testo sarebbe estremamente difficile da trasmettere in modo affidabile perché anche il rumore più piccolo renderebbe il testo irriconoscibile.
dsp_user

@dsp_user Sto provando a inviare testo. Posso vivere con qualche errore (come "Audio" -> "Apdio") :) Inoltre non capisco davvero che, ad esempio, per Amplitude Shift Keying, quando ne hai 1, allora invii un'onda sinusoidale, 0 quindi nient'altro che come conosci il primo 0? Voglio dire in un ambiente privo di rumore, ma prima del primo 1 ci sarebbero molti 0, giusto? Allora come lo sai?
Dang Manh Truong,

Ti suggerisco di guardare qualcosa come un vecchio modem 14,4 per idee.

@StanleyPawlukiewicz Ho fatto alcuni progressi. Si prega di verificare l'aggiornamento. Grazie mille.
Dang Manh Truong,

C'è molto da commentare. Potresti voler guardare le sequenze di Barker per il tuo preambolo, dato che stai usando i preamboli

Risposte:


8

Come ti sei reso conto, la parte difficile del fare comunicazioni digitali è la sincronizzazione di carrier, simboli e frame e la stima / equalizzazione dei canali.

La cattiva notizia è che non puoi aggirare questi problemi. La buona notizia è che l'implementazione di questi non è così difficile, purché ti limiti al BPSK a banda stretta. Lo so, perché l'ho fatto da solo, e così anche i miei studenti (laureandi) (vedi http://ieeexplore.ieee.org/document/5739249/ )

Un semplice suggerimento per aggirare il problema della sincronizzazione dell'operatore è utilizzare AM DSB-LC per convertire il segnale in banda base. Quindi, è possibile utilizzare un rilevatore di inviluppo senza sincronizzazione portante e fase. Questo ti costerà in termini di efficienza energetica, ma non è una priorità nel tuo caso.

Un altro semplice suggerimento è quello di fare "elaborazione batch" anziché "elaborazione in tempo reale"; ciò significa che, memorizzare l'intero segnale ricevuto ed elaborarlo in seguito. Questo è molto più facile da implementare rispetto allo streaming o all'elaborazione in tempo reale.

Il mio suggerimento più sostanziale è di leggere questo libro: Johnson, Sethares e Klein, "Progettazione di ricevitori software", Cambridge. Spiega in termini molto chiari ogni singolo pezzo del ricevitore e ha molti esempi di codice Matlab. C'è un libro simile di Steven Tretter, sull'implementazione di un sistema di comunicazione su un DSP (non ricordo il titolo esatto in questo momento).

In bocca al lupo; e per favore fai nuove domande più specifiche se le hai.


Ho letto il tuo articolo. Continua così! Una domanda: nel documento hai parlato di diversi metodi usati dagli studenti per trovare la risposta del canale (usando impulso, onde sinusoidali, ..). Dovrei trovare anche la risposta del canale? :)
Dang Manh Truong,

1
Grazie per le tue gentili parole :) Il fatto è che vuoi assicurarti di trasmettere su una banda di frequenza in cui la risposta del canale è piatta; altrimenti, avrai bisogno di un equalizzatore nel ricevitore. Se non si desidera stimare la risposta del canale, è possibile utilizzare una velocità dati molto bassa (diciamo, 100 b / s) su una frequenza con cui tutte le apparecchiature audio dovrebbero essere a proprio agio (diciamo, 5000 Hz).
MBaz,

1
@DangManhTruong Un'altra cosa: assicurati di usare impulsi limitati dalla larghezza di banda come il coseno sollevato con radice quadrata, non impulsi quadrati che hanno una larghezza di banda elevata e molto probabilmente subiranno distorsioni.
MBaz,

Ho letto il libro Design del ricevitore del software come mi hai suggerito (in realtà ho sfogliato gran parte di esso e mi sono concentrato sul capitolo 8: Bit to Symbols to Signals). Quindi ho alcune domande. Hai detto qualcosa sugli impulsi, ma nell'esempio del libro hanno usato una finestra di Hamming come impulso, va bene se lo faccio? E capisco bene: prima moduli il segnale usando, diciamo, CHIEDERE, poi usi la modulazione degli impulsi. Quindi sul ricevitore, si collega prima con il segnale a impulsi per ricevere il segnale modulato. Quindi demoduli. È corretto?
Dang Manh Truong,

E se desidero inviare i dati in un formato di pacchetto, con un'intestazione all'inizio e alla fine, dì 1 1 1 1 1 1 1 1, quindi dovrei aggiungerli con i dati, quindi modularli, quindi modellarli. Sul ricevitore, metterei in correlazione il segnale ricevuto con la forma dell'impulso (coseno sollevato con radice quadrata, ..), quindi devo demodulare il segnale, dopo quello correlato con l'intestazione. La mia comprensione è corretta?
Dang Manh Truong,

4

Alla fine, ho usato DTMF (segnalazione multi frequenza a doppio tono). Il DTMF originale ha 16 segnali ciascuno usando una combinazione di 2 frequenze. Ma qui ho usato solo "1" (697 Hz e 1209 Hz) e "0" (941Hz e 1336 Hz)

Una descrizione di come funziona il codice:

  • Il mittente converte il testo in binario, quindi trasmette segnali DTMF "0" / "1" (qui il tempo è di 0,3 secondi per la durata dei toni e 0,1 secondi per il periodo di silenzio tra i toni). Il codice di trasmissione è tratto da: https://sites.google.com/a/nd.edu/adsp-nik-kleber/home/advanced-digital-signal-processing/project-3-touch-tone . Apparentemente l'autore ha usato un filtro IIR marginalmente stabile per implementare un oscillatore digitale.
  • Il lato ricevitore utilizza innanzitutto 2 filtri passa-banda ridicolmente-alti-ordinati-e-ridicolmente stretti per estrarre rispettivamente i componenti di frequenza "0" e "1":

    filtro_ordine = 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);
    

Fatto ciò troveremo l'inizio e la fine di ciascun segnale "1" e "0". Il codice proviene da https://github.com/codyaray/dtmf-signaling . Fondamentalmente trova il periodo di silenzio che è di almeno 10 ms e qualsiasi periodo di tono più di 100 ms):

inserisci qui la descrizione dell'immagine

(Dall'alto verso il basso: segnale zero, segnale dopo lo spostamento del filtro medio, differenza di segnale dopo aver rimosso quelli al di sotto della soglia, segnale dopo la soglia)

  • Innanzitutto il risultato del passaggio precedente viene normalizzato, quindi passa attraverso un filtro a media mobile (con dimensione del filtro uguale a 10ms * Fs). Se tracciamo il risultato vedremmo che la forma di "0" e "1" può essere chiaramente vista. Quindi penso che in questo caso funzioni come un rilevatore di inviluppi.
  • Quindi tutto il segnale al di sotto di una certa soglia viene interrotto (ho scelto 0.1).
  • Infine trova tutti gli intervalli sopra la soglia che ha un intervallo di tempo superiore a 100 ms (nota che l'immagine non è riproducibile dal codice, dovrai scavare per farlo)

Quindi assembliamo i bit e convertiamo nuovamente in testo :)

Demo video: https://www.youtube.com/watch?v=vwQVmNnWa4s , dove invio il testo "Xin chao" tra il mio laptop e il PC di mio fratello :)

P / S: Inizialmente l'ho fatto perché il mio insegnante di comunicazione digitale ha detto che chiunque lo avesse fatto avrebbe ottenuto una A senza dover sostenere l'esame finale, ma sono stato in grado di farlo solo dopo l'esame. Quindi ecco tutti i miei sforzi :(

P / S2: ho ottenuto un C + :(


0

Se vuoi una libreria open source con una sincronizzazione molto buona, ti consiglio https://github.com/jgaeddert/liquid-dsp che usa le conseguenze per allineare, quindi fa l'equalizzazione e demodula il payload. Ho creato un modem audio che funziona in cima e funziona abbastanza bene, quindi se non altro, i metodi di Liquid dovrebbero essere di qualche aiuto

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.