2台のコンピューター間で音声でデータを送信します(非常に近い距離)


12

私は、2台のコンピューター間でサウンドを介してデータを送信する例を作成しています。いくつかの要件:

  • 距離が非常に近い。つまり、2台のコンピューターは基本的に互いに隣接している

  • ノイズが非常に少ない(先生がノイズソースとしてロックソングをオンにするとは思わない)

  • エラーは許容されます。たとえば、「無線通信」を送信した場合、他のコンピューターが「RadiQ通信」を受信した場合も同様に問題ありません。

  • 可能であれば:ヘッダー、フラグ、チェックサムなどはありません。サウンドを介してデータを送信する基本を示す非常に基本的な例が必要なためです。派手になる必要はありません。

このリンクに従って、オーディオ周波数シフトキーイングを使用してみました。

ラボ5 APRS(自動パッケージレポートシステム)

そしていくつかの結果を得ました: 私のGithubページ

しかし、それだけでは十分ではありません。クロックリカバリ、同期の方法がわかりません...(リンクにはタイミングリカバリメカニズムとしてフェーズロックループがありますが、明らかに十分ではありませんでした)。

だから私はもっと簡単なアプローチを見つけるべきだと思う。ここにリンクを見つけました:

データを音声に変換して戻します。ソースコードによる変調/復調

しかし、OPは回答で提案されたメソッドを実装していなかったため、非常に複雑になる可能性があります。また、私は答えで提案されているデコード方法を明確に理解していません:

デコーダはもう少し複雑ですが、概要は次のとおりです。

必要に応じて、サンプリングされた信号を11Khz付近でバンドパスフィルターします。これにより、ノイズの多い環境でパフォーマンスが向上します。FIRフィルターは非常にシンプルで、フィルターを生成するオンラインデザインアプレットがいくつかあります。

信号をしきい値処理します。最大振幅の1/2を超える値はすべて1で、以下の値はすべて0です。これは、信号全体をサンプリングしたことを前提としています。これがリアルタイムの場合は、固定のしきい値を選択するか、一定の時間にわたって最大信号レベルを追跡する何らかの自動ゲイン制御を行います。

ドットまたはダッシュの開始をスキャンします。サンプルをドットと見なすために、ドット期間に少なくとも一定数の1を表示することをお勧めします。次に、スキャンを続けて、これがダッシュかどうかを確認します。完全な信号を期待しないでください。1の真ん中にいくつかの0が表示され、0の真ん中にいくつかの1が表示されます。ノイズがほとんどない場合、「オン」期間と「オフ」期間を区別するのはかなり簡単です。

次に、上記のプロセスを逆にします。ダッシュが1ビットをバッファにプッシュする場合、ドットが0をプッシュする場合。

ドットとして分類する前に1がいくつあるのかわかりません。そのため、今のところわからないことがたくさんあります。プロセスを理解できるように、音声でデータを送信する簡単な方法を提案してください。どうもありがとうございました :)

更新:

(ある程度)動作しているように見えるMatlabコードをいくつか作成しました。まず、振幅シフトキーイング(サンプリング周波数48000 Hz、F_on = 5000 Hz、ビットレート= 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 communication」が送信されます。比較的ノイズはありませんが、最初と最後に背景ノイズが聞こえます。しかし、結果は「デジタル通信」のみを示しました

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

受信者(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

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テキストを送信しようとしています。私はいくつかのエラーで生きることができます(「オーディオ」->「アプディオ」など):)また、例えば、振幅シフトキーイングでは、1を持っているときに正弦波を送信すると0、その後は何もしません最初の0を知っていますか?私はノイズのない環境を意味しますが、最初の1の前には多くの0がありますか?それでは、どうやってそれを知っていますか?
ダンマンチュオン

昔ながらの14.4モデムのようなものを考えてみることをお勧めします。

@StanleyPawlukiewicz私はいくつかの進歩を遂げました。アップデートを確認してください。どうもありがとうございました。
ダンマンチュオン

多くのコメントがあります。あなたはプリアンブルを使用していることを考えると、あなたのプリアンブルのためのバーカー系列で見たいと思うかもしれません

回答:


8

お気づきのとおり、デジタル通信を行う上で難しいのは、キャリア、シンボルとフレームの同期、およびチャネル推定/イコライゼーションです。

悪いニュースは、これらの問題を回避できないことです。良いニュースは、狭帯域BPSKに限定する限り、これらの実装はそれほど難しくないことです。私はこれを自分でやったので、私の(学部生)の生徒もいるので知っています(http://ieeexplore.ieee.org/document/5739249/を参照)

キャリア同期の問題を回避するための1つの簡単な提案は、AM DSB-LCを使用してベースバンド信号をアップコンバートすることです。次に、キャリアと位相の同期なしでエンベロープ検出器を使用できます。これにより電力効率が低下しますが、それは優先事項ではありません。

もう1つの簡単な提案は、「リアルタイム処理」ではなく「バッチ処理」を行うことです。つまり、受信した信号全体を保存し、後で処理します。これは、ストリーム処理またはリアルタイム処理よりも実装がはるかに簡単です。

私のより実質的な提案は、この本を読むことです。ジョンソン、セタレス、クライン、「ソフトウェア受信機設計」、ケンブリッジ。レシーバーのすべてのピースを非常に明確な用語で説明し、多くのサンプルMatlabコードを持っています。DSPに通信システムを実装することについて、Steven Tretterによる同様の本があります(正確なタイトルは今思い出せません)。

幸運を; 新しい具体的な質問がある場合はお問い合わせください。


あなたの論文を読みました。良い仕事を続けてください!1つの質問:論文では、学生がチャネル応答を見つけるために使用するいくつかの方法について説明しました(インパルス、正弦波などを使用)。チャンネルの応答も見つける必要がありますか?:)
ダンマンチュオン

1
親切な言葉をありがとう:)問題は、チャンネル応答がフラットな周波数帯域で送信することを確認したいということです。そうでない場合は、受信機にイコライザーが必要です。チャネル応答を推定したくない場合、できることは、すべてのオーディオ機器が快適であるはずの周波数(たとえば、5000 Hz)で非常に低いデータレート(たとえば、100 b / s)を使用することです。
MBaz

1
@DangManhTruongもう1つ:帯域幅が大きく、歪みが発生する可能性が高い平方パルスではなく、平方根コサインロールオフなどの帯域幅制限パルスを使用してください。
MBaz

あなたが示唆したように、ソフトウェア受信機の設計の本を読みました(実際、その大部分をざっと読み、第8章:ビットからシンボル、信号に集中しました)。そこで、いくつか質問があります。あなたはパルスについて何かを言いましたが、本の例ではハミング窓をパルスとして使用しましたが、私がそうしても大丈夫ですか?そして、私の理解は正しいです。最初に、たとえばASKを使用して信号を変調し、次にパルス整形を使用します。次に、受信機で、最初にパルス信号と相関させて変調信号を受信します。その後、復調します。それが正しいか?
ダンマンチュオン

そして、最初と最後にヘッダーがあるパケット形式でデータを送信したい場合、たとえば1 1 1 1 1 1 1 1 1のように、データを追加し、変調してからパルス整形する必要があります。受信機では、受信した信号をパルス形状(平方根コサインロールオフ、..)と相関させ、その後、ヘッダーと相関させた後、信号を復調する必要があります。私の理解は正しいですか?
ダンマンチュオン

4

最後に、DTMF(Dual Tone Multi Frequency Signaling)を使用しました。元のDTMFには、2つの周波数の組み合わせを使用する16の信号があります。しかし、ここでは「1」(697 Hzと1209 Hz)と「0」(941Hzと1336 Hz)のみを使用しました

コードの仕組みの概要:

  • 送信者はテキストをバイナリに変換してから、「0」/「1」DTMF信号を送信します(ここで、タイミングはトーン持続時間で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ミリ秒を超えるトーン期間を検出します)。

ここに画像の説明を入力してください

(上から下:ゼロ信号、移動平均フィルター後の信号、しきい値以下のものを除去した後の信号の差、しきい値処理後の信号)

  • 最初に、前のステップの結果が正規化され、移動平均フィルター(フィルターサイズが10ms * Fsに等しい)を通過しました。結果をプロットすると、「0」と「1」の形状がはっきりと見えることがわかります。したがって、この場合はエンベロープ検出器として機能すると思います。
  • 次に、特定のしきい値を下回るすべての信号が遮断されます(0.1を選択しました)。
  • 最後に、100ミリ秒を超える時間間隔を持つしきい値を超えるすべての間隔を見つけます(画像がコードから再現できないことに注意してください。

次に、ビットを組み立ててテキストに戻します:)

ビデオデモ:https : //www.youtube.com/watch?v=vwQVmNnWa4s。ここで、ラップトップと弟のPCの間で「Xin chao」というテキストを送信します:)

P / S:もともと私が行ったのは、デジタルコミュニケーションの先生が、誰でも最終試験を行わなくてもAを取得できると言っていたからですが、試験後にしかできませんでした。だからここに私のすべての努力が行きます:(

P / S2:C +を取得しました:(


0

非常に良好な同期を備えたオープンソースライブラリが必要な場合は、msequencesを使用してアライメントし、ペイロードをイコライズおよび復調するhttps://github.com/jgaeddert/liquid-dspをお勧めします。一番上で動作するオーディオモデムを作成し、非常にうまく機能するので、他に何もなければ、液体の方法が役立つはずです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.