两台电脑之间通过声音传输数据(距离很近)

信息处理 声音的 调制 声音 fsk
2022-01-18 08:18:36

我正在写一个关于通过两台计算机之间的声音传输数据的示例。一些要求:

  • 距离很近,即两台电脑基本相邻。

  • 噪音很小(我认为我的老师不会打开摇滚歌曲作为噪音源)

  • 错误是可以接受的:例如,如果我发送“无线电通信”,那么如果另一台计算机收到“RadiQ communEcation”,也可以。

  • 如果可能:没有标头、标志、校验和……因为我只想要一个非常基本的示例来演示通过声音传输数据的基础知识。没必要花哨。

我尝试根据此链接使用音频频移键控:

Lab 5 APRS(自动包裹报告系统)

并得到了一些结果: 我的 Github 页面

但这还不够。我不知道如何进行时钟恢复,同步,......(链接有锁相环作为定时恢复机制,但显然还不够)。

所以我认为我应该找到一种更简单的方法。在这里找到一个链接:

数据到音频并返回。带源代码的调制/解调

但是OP没有实现答案中建议的方法,所以恐怕它可能非常复杂。我也不清楚答案中建议的解码方法:

解码器有点复杂,但这里有一个大纲:

可选择对 11Khz 左右的采样信号进行带通滤波。这将提高嘈杂环境中的性能。FIR 滤波器非常简单,有一些在线设计小程序可以为您生成滤波器。

阈值信号。高于 1/2 最大幅度的每个值都是 1,低于最大幅度的每个值都是 0。这假设您已经对整个信号进行了采样。如果这是实时的,您要么选择一个固定阈值,要么进行某种自动增益控制,在一段时间内跟踪最大信号电平。

扫描点或破折号的开头。您可能希望在您的点期间至少看到一定数量的 1,以将样本视为一个点。然后继续扫描,看看这是否是破折号。不要指望一个完美的信号 - 你会在 1 的中间看到几个 0,在 0 的中间看到几个 1。如果噪音很小,那么区分“开”时段和“关”时段应该相当容易。

然后把上面的过程反过来。如果您看到破折号将 1 位推入缓冲区,如果一个点推入零。

在将其归类为点之前,我不明白有多少个 1,......所以现在有很多事情我不明白。请向我建议一种通过声音传输数据的简单方法,以便我了解该过程。非常感谢 :)

更新:

我制作了一些看起来(有点)可操作的 Matlab 代码。我首先使用幅移键控(采样频率 48000 Hz,F_on = 5000 Hz,比特率 = 10 位/秒)调制信号,然后将其添加到标题和结束序列(当然也调制它们)。标题和结束序列是在临时基础上选择的(是的,这是一个 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];

然后我通过声音传输它们,并用我的智能手机记录下来。然后我将录制的音频发送回我的计算机,使用另一段代码读取音频。然后我将接收到的信号(尚未解调)与调制的报头和结束序列相关联,以找出开始和结束。之后我只取相关信号(从头到尾,如相关部分所示)。然后我解调和采样以找到数字数据。这里有3个音频文件:

  • “DigitalCommunication_ask”:链接这里它发送文本“数字通信”。相对无噪音,尽管您可以在开头和结尾听到一些背景噪音。然而结果只显示“数字通讯”

  • “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

请告诉我为什么它不起作用?非常感谢

3个回答

正如您所意识到的,进行数字通信的困难部分是载波、符号和帧同步以及信道估计/均衡。

坏消息是你无法解决这些问题。好消息是实施这些并不难,只要您限制自己使用窄带 BPSK。我知道,因为我自己做过,我的(本科)学生也做过(见http://ieeexplore.ieee.org/document/5739249/

解决载波同步问题的一个简单建议是使用 AM DSB-LC 对基带信号进行上变频。然后,您可以使用没有载波和相位同步的包络检波器。这会降低您的电源效率,但这不是您的优先事项。

另一个简单的建议是做“批处理”而不是“实时处理”;这意味着,存储整个接收到的信号并在之后进行处理。这比流或实时处理更容易实现。

我更实质性的建议是阅读这本书:Johnson、Sethares 和 Klein,“软件接收器设计”,剑桥。它以非常清晰的术语解释了接收器的每一部分,并有很多示例 Matlab 代码。Steven Tretter 有一本类似的书,关于在 DSP 上实现通信系统(我现在不记得确切的标题)。

祝你好运; 如果您有新的、更具体的问题,请提出。

最后,我使用了 DTMF(双音多频信令)。原始 DTMF 有 16 个信号,每个信号使用 2 个频率的组合。但这里我只使用了“1”(697 Hz 和 1209 Hz)和“0”(941Hz 和 1336 Hz)

代码如何工作的概述:

  • 发送方将文本转换为二进制,然后发送“0”/“1” DTMF 信号(这里的时间是 0.3s 为音调持续时间,0.1s 为音调之间的静默期)。传输代码取自: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)。
  • 最后找到所有高于阈值且时间间隔大于 100ms 的时间间隔(请注意,图像无法从代码中重现,您必须四处挖掘才能制作)

然后我们组装这些位并转换回文本:)

视频演示:https ://www.youtube.com/watch?v=vwQVmNnWa4s ,在我的笔记本电脑和我兄弟的电脑之间发送文本“Xin chao” :)

P/S:本来我是因为我的数字通信老师说做这个的人不用期末考试就可以拿A,但我只有在考试后才能做到这一点。所以这是我所有的努力:(

P/S2: 我有一个 C+ :(

如果你想要一个同步性非常好的开源库,我推荐https://github.com/jgaeddert/liquid-dsp,它使用 msequences 对齐,然后进行均衡和解调有效载荷。我做了一个在上面运行的音频调制解调器,它工作得很好,所以如果没有别的,液体的方法应该会有所帮助