在两台计算机之间(非常近的距离)通过声音传输数据


12

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

  • 距离非常近,即两台计算机基本上彼此相邻

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

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

  • 如果可能的话:没有头,标志,校验和...。因为我只想要一个非常基本的示例,演示通过声音传输数据的基础。无需幻想。

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

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

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

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

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

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

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

解码器稍微复杂一点,但这是一个概述:

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

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

扫描点或破折号的开始。您可能希望在点周期内看到至少一定数量的1,以将样本视为点。然后继续扫描以查看是否是破折号。不要指望完美的信号-您会在1的中间看到几个0,在0的中间看到几个1。如果噪声很小,则将“接通”周期与“断开”周期区分开应该很容易。

然后逆转以上过程。如果看到破折号,则将1推至缓冲区,如果将破折号,则推至零。

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

更新:

我做了一些看起来(一定)可操作的Matlab代码。我首先使用幅度移位键控(采样频率48000 Hz,F_on = 5000 Hz,比特率= 10 bits / s)调制信号,然后将其与标头和结束序列相加(当然也对它们进行调制)。标头和结束序列是临时选择的(是的,这是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”:在此处链接发送文本“ 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我正在尝试发送文本。我可能会遇到一些错误(例如“音频”->“ Apdio”):)我也不是很明白,例如,对于幅移键控,当您发送1时,您发送正弦波;发送0时,则仅发送您知道第一个0吗?我的意思是在无噪音的环境中,但是在第一个1之前会有很多0,对吗?那你怎么知道
Dang Manh Truong

我建议您看看类似老式的14.4调制解调器的想法。

@StanleyPawlukiewicz我已经取得了一些进展。请检查更新。非常感谢你。
Dang Manh Truong

有很多评论。假设您正在使用前同步码,则可能需要查看Barker序列

Answers:


8

如您所知,进行数字通信的难点在于载波,符号和帧同步以及信道估计/均衡。

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

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

另一个简单的建议是执行“批处理”而不是“实时处理”。这就是说,存储整个接收到的信号并随后对其进行处理。这比流或实时处理更容易实现。

我更实质性的建议是读这本书:约翰逊(Johnson),塞瑟雷斯(Sethares)和克莱因(Klein),“软件接收器设计”,剑桥。它以非常清晰的术语解释了接收器的每一部分,并提供了许多示例Matlab代码。史蒂文·特雷特(Steven Tretter)有一本类似的书,关于在DSP上实现通信系统(我现在不记得确切的标题)。

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


我读了你的论文。保持良好的工作!一个问题:在本文中,您讨论了学生用来查找通道响应的几种方法(使用脉冲,正弦波等)。我也需要找到频道响应吗?:)
Dang Manh Truong

1
谢谢您的客气:)问题是您要确保在信道响应平坦的频带上传输;否则,您将在接收器中需要一个均衡器。如果您不想估计通道响应,您可以做的是在所有音频设备都应该适应的频率(例如5000 Hz)上使用非常低的数据速率(例如100 b / s)。
MBaz

1
@DangManhTruong还有一件事:请确保使用带宽受限的脉冲,例如平方根升高的余弦,而不要使用带宽较大且很可能会失真的平方脉冲。
MBaz

我已经按照您的建议阅读了《软件接收器设计》一书(实际上我浏览了其中的大部分内容,并专注于第8章:从符号到信号到信号)。所以我有一些问题。您说了一些关于脉冲的内容,但是在本书的示例中,他们使用了汉明窗作为脉冲,是否可以?我的理解是正确的:首先使用ASK调制信号,然后使用脉冲整形。然后,在接收器上,您首先与脉冲信号相关以接收调制信号。然后您解调。这是对的吗?
Dang Manh Truong

如果我希望以数据包的形式发送数据,并在头和末尾都标有头,比如说1 1 1 1 1 1 1 1,那么我应该将其附加数据,然后对其进行调制,然后对其进行脉冲整形。在接收器上,我将接收到的信号与脉冲形状(平方根升余弦,..)相关联,然后我必须对信号进行解调,然后再与标头相关联。我的理解正确吗?
Dang Manh Truong

4

最后,我使用了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 ms的静默期和超过100ms的任何音调期):

在此处输入图片说明

(从上至下:零信号,移动平均滤波器后的信号,去除阈值以下的信号后的信号差,阈值后的信号)

  • 首先,对上一步的结果进行归一化,然后通过移动平均滤波器(滤波器大小等于10ms * Fs)。如果将结果绘制成图,我们将清楚地看到“ 0”和“ 1”的形状。因此,我认为它在这种情况下可以用作包络检测器。
  • 然后,所有低于某个阈值的信号都将被切断(我选择了0.1)。
  • 最后找到所有超过阈值且时间间隔大于100ms的间隔(请注意,该图像无法从代码中复制出来,您必须四处挖掘才能做到)

然后,我们将这些位组合起来并转换回文本:)

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

P / S:最初我这样做是因为我的数字通信老师说,只要这样做,谁都不必参加期末考试就可以得到A,但是我只能在考试后这样做。所以,我所有的努力都在这里:(

P / S2:我有C + :(


0

如果您想要一个具有很好同步性的开源库,我建议https://github.com/jgaeddert/liquid-dsp使用msequences对齐,然后进行均衡并解调有效负载。我做了一个音频调制解调器,它可以在顶部运行,并且运行良好,因此,如果没有其他问题,液体方法应该会有所帮助

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.