Transmet des données via le son entre 2 ordinateurs (distance très proche)


12

J'écris un exemple sur la transmission de données via le son entre deux ordinateurs. Quelques exigences:

  • La distance est très proche, c'est-à-dire que les 2 ordinateurs sont fondamentalement adjacents l'un à l'autre

  • Très peu de bruit (je ne pense pas que mon professeur allumerait une chanson rock comme source de bruit)

  • L'erreur est acceptable: par exemple, si j'envoie "Communication radio", si l'autre ordinateur reçoit "RadiQ communEcation", ça va aussi.

  • Si possible: pas d'en-tête, indicateur, somme de contrôle, .... car je veux juste un exemple très basique démontrant les bases de la transmission de données par le son. Pas besoin d'être chic.

J'ai essayé d'utiliser la modulation de fréquence audio selon ce lien:

Lab 5 APRS (Automatic Package Reporting System)

et obtenu quelques résultats: Ma page Github

Mais ce n'est pas assez. Je ne sais pas comment faire la récupération d'horloge, la synchronisation, ... (le lien a une boucle à verrouillage de phase comme mécanisme de récupération de synchronisation, mais ce n'était apparemment pas suffisant).

Je pense donc que je devrais trouver une approche plus simple. Trouvé un lien ici:

Données audio et retour. Modulation / démodulation avec code source

mais l'OP n'a pas mis en œuvre la méthode suggérée dans la réponse, donc je crains que cela ne soit très complexe. De plus, je ne comprends pas clairement la méthode de décodage suggérée dans la réponse:

Le décodeur est un peu plus compliqué mais voici un aperçu:

Optionnellement, filtre passe-bande du signal échantillonné autour de 11 kHz. Cela améliorera les performances dans un environnement bruyant. Les filtres FIR sont assez simples et il existe quelques applets de conception en ligne qui généreront le filtre pour vous.

Seuil le signal. Chaque valeur au-dessus de 1/2 amplitude maximale est 1 chaque valeur au-dessous est 0. Cela suppose que vous avez échantillonné la totalité du signal. Si c'est en temps réel, vous choisissez un seuil fixe ou effectuez une sorte de contrôle automatique du gain où vous suivez le niveau de signal maximum sur une certaine période.

Recherchez le début du point ou du tiret. Vous voulez probablement voir au moins un certain nombre de 1 dans votre période de points pour considérer les échantillons comme un point. Continuez ensuite à scanner pour voir s'il s'agit d'un tiret. Ne vous attendez pas à un signal parfait - vous verrez quelques 0 au milieu de vos 1 et quelques 1 au milieu de vos 0. S'il y a peu de bruit, différencier les périodes "marche" des périodes "arrêt" devrait être assez facile.

Inversez ensuite le processus ci-dessus. Si vous voyez un tiret, poussez 1 bit vers votre tampon, si un point pousse un zéro.

Je ne comprends pas combien de 1 avant de le classer comme un point, ... Il y a donc beaucoup de choses que je ne comprends pas en ce moment. Veuillez me suggérer une méthode simple pour transmettre des données par le son afin que je puisse comprendre le processus. Merci beaucoup :)

MISE À JOUR:

J'ai créé du code Matlab qui semble être (quelque peu) opérationnel. Je module d'abord le signal en utilisant la modulation par décalage d'amplitude (fréquence d'échantillonnage 48000 Hz, F_on = 5000 Hz, débit binaire = 10 bits / s), puis je l'ajoute avec un en-tête et une séquence de fin (bien sûr, modulez-les également). L'en-tête et la séquence de fin ont été choisis sur une base ad hoc (ouais c'était un 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];

Je les transmets ensuite par le son et je les enregistre avec mon smartphone. Ensuite, j'envoie l'audio enregistré à mon ordinateur, utilise un autre morceau de code pour lire l'audio. Ensuite, je corrèle le signal reçu (pas encore démodulé) avec l'en-tête modulé et la séquence de fin pour connaître le début et la fin. Après cela, je ne prends que le signal pertinent (du début à la fin, comme on le trouve dans la partie corrélation). Ensuite, je démodule et échantillonne pour trouver les données numériques. Voici 3 fichiers audio:

  • "DigitalCommunication_ask": Le lien ici envoie le texte "Communication numérique". Relativement sans bruit, bien que vous puissiez entendre un bruit de fond au début et à la fin. Cependant, le résultat n'a montré que "Digital Commincatio"

  • "HelloWorld_ask": Le lien ici envoie le texte "Hello world". Sans bruit comme "DigitalCommunication_ask". Cependant le résultat pour celui-ci était correct

  • "HelloWorld_noise_ask": Le lien ici envoie le texte "Hello world". Cependant il y a du bruit que j'ai fait (je viens de dire quelques trucs aléatoires "A, B, C, D, E, ...." pendant la transmission). Malheureusement celui-ci a échoué

Voici le code de l'expéditeur (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);

Pour le récepteur (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

Code de modulation 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

Démodulation ASK (ask_demodulate.m) (Fondamentalement, ce n'est que la détection d'enveloppe, pour laquelle j'ai utilisé la transformée de 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

Veuillez me dire pourquoi cela ne fonctionne pas? Merci beaucoup


En théorie (dans un environnement sans bruit), cela serait trivial à mettre en œuvre mais en pratique c'est beaucoup plus difficile. Cela dépend quand même du type d'informations que vous essayez d'envoyer. Le texte serait extrêmement difficile à transmettre de manière fiable car même le plus petit bruit rendrait le texte méconnaissable.
dsp_user

@dsp_user J'essaie d'envoyer du texte. Je peux vivre avec une erreur (comme "Audio" -> "Apdio") :) De plus, je ne comprends pas vraiment que, pour Amplitude Shift Keying par exemple, lorsque vous en avez 1, vous envoyez une onde sinusoïdale, 0 puis rien mais comment connaissez-vous le premier 0? Je veux dire dans un environnement sans bruit, mais avant le premier 1, il y aurait beaucoup de 0 non? Alors comment le sais-tu?
Dang Manh Truong

Je vous suggère de regarder quelque chose comme un modem 14.4 à l'ancienne pour des idées.

@StanleyPawlukiewicz J'ai fait des progrès. Veuillez vérifier la mise à jour. Merci beaucoup.
Dang Manh Truong

Il y a beaucoup à dire. Vous voudrez peut-être regarder les séquences de Barker pour votre préambule, étant donné que vous utilisez des préambules

Réponses:


8

Comme vous l'avez compris, la partie difficile des communications numériques est la synchronisation des porteuses, des symboles et des trames, ainsi que l'estimation / l'égalisation des canaux.

La mauvaise nouvelle est que vous ne pouvez pas contourner ces problèmes. La bonne nouvelle est que leur mise en œuvre n'est pas si difficile, tant que vous vous limitez au BPSK à bande étroite. Je sais, parce que je l'ai fait moi-même, tout comme mes étudiants (de premier cycle) (voir http://ieeexplore.ieee.org/document/5739249/ )

Une suggestion simple pour contourner le problème de la synchronisation des porteuses consiste à utiliser AM DSB-LC pour convertir le signal en bande de base. Ensuite, vous pouvez utiliser un détecteur d'enveloppe sans synchronisation de porteuse et de phase. Cela vous coûtera en efficacité énergétique, mais ce n'est pas une priorité dans votre cas.

Une autre suggestion simple est de faire un "traitement par lots" au lieu d'un "traitement en temps réel"; ce que cela signifie, stocker tout le signal reçu et le traiter ensuite. C'est beaucoup plus facile à mettre en œuvre que le traitement en continu ou en temps réel.

Ma suggestion plus substantielle est de lire ce livre: Johnson, Sethares et Klein, "Software receiver design", Cambridge. Il explique en termes très clairs chaque pièce du récepteur et contient de nombreux exemples de code Matlab. Il y a un livre similaire de Steven Tretter, sur l'implémentation d'un système de communication sur un DSP (je ne me souviens pas du titre exact en ce moment).

Bonne chance; et veuillez poser de nouvelles questions plus spécifiques si vous en avez.


J'ai lu votre article. Continuez votre bon travail! Une question: Dans l'article, vous avez parlé de plusieurs méthodes utilisées par les élèves pour trouver la réponse du canal (en utilisant l'impulsion, les ondes sinusoïdales, ..). Aurais-je besoin de trouver la réponse du canal aussi? :)
Dang Manh Truong

1
Merci pour vos aimables paroles :) Le fait est que vous voulez vous assurer de transmettre sur une bande de fréquences où la réponse du canal est plate; sinon, vous aurez besoin d'un égaliseur dans le récepteur. Si vous ne souhaitez pas estimer la réponse du canal, vous pouvez utiliser un débit de données très faible (par exemple, 100 b / s) sur une fréquence avec laquelle tout l'équipement audio devrait être à l'aise (par exemple, 5000 Hz).
MBaz

1
@DangManhTruong Une dernière chose: assurez-vous d'utiliser des impulsions à bande passante limitée comme le cosinus surélevé à racine carrée, et non des impulsions carrées qui ont une large bande passante et subiront très probablement une distorsion.
MBaz

J'ai lu le livre Conception du récepteur logiciel comme vous l'avez suggéré (en fait, j'ai parcouru la majeure partie de celui-ci et je me suis concentré sur le chapitre 8: Bits to Symbols to Signals). J'ai donc quelques questions. Vous avez dit quelque chose sur les impulsions, mais dans l'exemple du livre, ils ont utilisé une fenêtre Hamming comme impulsion, est-ce que ça va si je le fais? Et si je comprends bien: vous modulez d'abord le signal en utilisant, disons, ASK, puis vous utilisez la mise en forme d'impulsion. Ensuite, sur le récepteur, vous corrélez d'abord avec le signal d'impulsion pour recevoir le signal modulé. Ensuite, vous démodulez. Est-ce correct?
Dang Manh Truong

Et si je souhaite envoyer des données sous forme de paquets, avec un en-tête au début et à la fin, disons 1 1 1 1 1 1 1 1, je devrais donc les ajouter aux données, puis les moduler, puis les façonner par impulsions. Sur le récepteur, je corrélerais le signal reçu avec la forme d'impulsion (cosinus surélevé à racine carrée, ..) puis je dois démoduler le signal, après cela corréler avec l'en-tête. Ma compréhension est-elle correcte?
Dang Manh Truong

4

Au final, j'ai utilisé le DTMF (Dual Tone Multi Frequency signaling). Le DTMF d'origine a 16 signaux chacun utilisant une combinaison de 2 fréquences. Mais ici, je n'ai utilisé que "1" (697 Hz et 1209 Hz) et "0" (941Hz et 1336 Hz)

Un aperçu du fonctionnement du code:

  • L'expéditeur convertit le texte en binaire, puis transmet les signaux DTMF "0" / "1" (ici, le temps est de 0,3 s pour la durée de la tonalité et de 0,1 s pour la période de silence entre les tonalités). Le code de transmission provient de: https://sites.google.com/a/nd.edu/adsp-nik-kleber/home/advanced-digital-signal-processing/project-3-touch-tone . Apparemment, l'auteur a utilisé un filtre IIR marginalement stable pour implémenter un oscillateur numérique.
  • Le côté récepteur utilise d'abord 2 filtres passe-bande ridiculement élevés et ridiculement ordonnés pour extraire les composantes de fréquence "0" et "1", respectivement:

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

Après cela, nous trouverons le début et la fin de chaque signal "1" et "0". Le code provient de https://github.com/codyaray/dtmf-signaling . Fondamentalement, il trouve la période de silence qui est d'au moins 10 ms et toute période de tonalité supérieure à 100 ms):

entrez la description de l'image ici

(De haut en bas: signal zéro, signal après le déplacement de la moyenne du filtre, différence de signal après suppression de ceux en dessous du seuil, signal après seuillage)

  • D'abord, le résultat de l'étape précédente est normalisé, puis passé par un filtre à moyenne mobile (avec une taille de filtre égale à 10 ms * Fs). Si nous traçons le résultat, nous verrions que la forme du "0" et du "1" est clairement visible. Je pense donc que cela fonctionne un peu comme un détecteur d'enveloppe dans ce cas.
  • Ensuite, tout le signal en dessous d'un certain seuil est coupé (j'ai choisi 0,1).
  • Enfin, trouvez tous les intervalles au-dessus du seuil qui a un intervalle de temps supérieur à 100 ms (notez que l'image n'est pas reproductible à partir du code, vous devrez fouiller pour la faire)

Ensuite, nous assemblons les bits et les reconvertissons en texte :)

Démo vidéo: https://www.youtube.com/watch?v=vwQVmNnWa4s , où j'envoie le texte "Xin chao" entre mon ordinateur portable et le PC de mon frère :)

P / S: À l'origine, je l'ai fait parce que mon professeur de communication numérique a dit que celui qui le ferait obtiendrait un A sans avoir à passer l'examen final, mais je n'ai pu le faire qu'après l'examen. Voici donc tous mes efforts :(

P / S2: j'ai un C + :(


0

Si vous voulez une bibliothèque open source avec une très bonne synchronisation, je recommande https://github.com/jgaeddert/liquid-dsp qui utilise msequences pour s'aligner, puis fait l'égalisation et démodule la charge utile. J'ai créé un modem audio qui fonctionne sur le dessus et il fonctionne assez bien, donc si rien d'autre, les méthodes de Liquid devraient être utiles

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.