简介
本文章介绍了一个简易的用matlab写的声音通信系统
主要由如下部分组成:
调制 -> 发送接收 -> 同步 -> 解调
调制
首先一个实际的通信系统在调制之前应该还有编码+码元划分的过程,但是因为这里只是简单的过一遍声音通信系统的流程,尽量只保留核心部分,因此在随机生成码元之后,就直接开始调制了。
在本系统中,我们设定 1 symbol(码元) = 2 bit ,那么一个码元的取值就有 0 1 2 3四种情况,
% 系统参数设置
symbols_tx = [0, 1, 2, 3];
symbols_tx = repmat(symbols_tx, 1, 40);
samples_per_symbol = 600;
fs = 48000;
period = samples_per_symbol / fs;
nBits = 16;
amplitude = 2 ^ nBits - 1;
nChannels = 1;
为了使发送的音频时长适中,发送160个码元,每个码元600个采样点,采样率设为常用的48000HZ(发送96000个采样点,时长为2s)
随机生成码元后,就可以开始调制了。
% 定义chirp载波
% 频段设计原则
% 高频段尽量不要downchirp表示另一个,不然容易搞混
ts = linspace(0, period, samples_per_symbol);
chirp_0 = chirp(ts, 100, period, 4000, 'linear') * amplitude;
chirp_1 = chirp(ts, 4000, period, 100, 'linear') * amplitude;
chirp_2 = chirp(ts, 4000, period, 12000, 'linear') * amplitude;
chirp_3 = chirp(ts, 12000, period, 4000, 'linear') * amplitude;
chirps_tx = [];
for i=1:length(symbols_tx)
switch symbols_tx(i)
case 0
chirps_tx = [chirps_tx, chirp_0];
case 1
chirps_tx = [chirps_tx, chirp_1];
case 2
chirps_tx = [chirps_tx, chirp_2];
case 3
chirps_tx = [chirps_tx, chirp_3];
end
end
按我的理解,调制就是将码元变成一段信号,比如变成一段正弦波,将基带信号变为频带信号,以方便传输
现在常用OFDM等调制技术,但是本系统简便为主,就将4种码元分别调制为4种不同频段的chirp信号,chirp信号就是频率不断变化的正弦信号。
调制代码很简单,先定义4种用于调制的chirp信号,然后遍历所有码元,将每个码元用一个chirp信号代替,就调制完了
调制完的信号就可以发送了
发送与接收
maltab操纵音频的发送与接收非常方便,有 audioplayer 和 audiowriter 这两个函数可以使用
% 发送
myPlayerObj = audioplayer(chirps_tx, fs, nBits);
myPlayerObj.StartFcn = 'disp(''Start Playing.'')';
myPlayerObj.StopFcn = 'disp(''End of Playing.'')';
play(myPlayerObj);
因为 windows系统问题,一台电脑自发自收的话收到的信号幅度很小,于是我借了舍友一台电脑用来发送音频,我这边录下来然后再处理
% 接收
myRecorderObj = audiorecorder(fs, nBits, nChannels);
myRecorderObj.StartFcn = 'disp(''Start Recording.'')';
myRecorderObj.StopFcn = 'disp(''End of Recording.'')';
record(myRecorderObj, 5);
接收代码中的5代表录音5秒
同步
因为各种原因,接收端不可能第一个采样点开始就是发送端发送的信号,肯定会有一段时延,但是要做时延的话就必须要找到RX信号(在本系统中,就是5秒的录音)中接收到TX信号的第一个采样点,这可以通过使用TX信号从RX信号开头向右滑动求相关系数来实现。
对于没做过的人来说看文字可能会理解不了,代码如下:
%% 同步
% 找开头的范围定在(1:fs*2)
detectRange = fs;
% 窗滑动步长
stride = 1;
head_idx = 1;
num_move = detectRange / stride;
max_corr_array = zeros(1, num_move);
for i = 1:num_move
corr_temp = corrcoef(chirps_rx( head_idx : head_idx+length(chirps_tx)-1 ), chirps_tx);
max_corr_array(i) = corr_temp(2);
head_idx = head_idx + stride;
end
[max_value, max_idx] = max(max_corr_array);
上述代码中,detectRange 取 2*fs 是因为在本系统情境下,2秒内tx信号肯定会到达,搬运代码的话要具体情况具体分析
得到的 max_value 就是作滑动相关后得到的最大的相关系数,max_idx就是最大相关系数的索引位置,也就是TX信号的开头,也就是作解调的起点
解调
本系统采用最简单的轮流匹配相关法来解调(另一个python系统中还有更先进的数字脉冲压缩法,但是轮流匹配也够用了)
轮流匹配相关也就是从解调起始点开始,将RX信号以TX信号的长度为单位切割为一段一段,然后令TX信号的4种码元的对应调制波形分别与这些切割段求相关系数,相关系数最大的那个码元就是解调结果。
start_idx = max_idx;
validChirpData = chirps_rx(start_idx : start_idx + symbols_num * samples_per_symbol - 1);
demod_symbols_array = zeros(1, symbols_num);
max_corr_values = zeros(1, symbols_num);
% 和同步相比,stride变成了 samples_per_symbols
% 注意 validChirpData 是列向量, chirp_x是行向量,要转置
for i = 1:symbols_num
corr_array = zeros(1, 4);
corr_0 = corrcoef(validChirpData( (i-1)*samples_per_symbol+1 : i*samples_per_symbol ), chirp_0');
corr_array(1) = corr_0(2);
corr_1 = corrcoef(validChirpData( (i-1)*samples_per_symbol+1 : i*samples_per_symbol ), chirp_1');
corr_array(2) = corr_1(2);
corr_2 = corrcoef(validChirpData( (i-1)*samples_per_symbol+1 : i*samples_per_symbol ), chirp_2');
corr_array(3) = corr_2(2);
corr_3 = corrcoef(validChirpData( (i-1)*samples_per_symbol+1 : i*samples_per_symbol ), chirp_3');
corr_array(4) = corr_3(2);
[temp_max_value, temp_max_idx] = max(corr_array);
max_corr_values(i) = temp_max_value;
switch temp_max_idx
case 1
demod_symbols_array(i) = 0;
case 2
demod_symbols_array(i) = 1;
case 3
demod_symbols_array(i) = 2;
case 4
demod_symbols_array(i) = 3;
end
end
demod_symbols_array 就是解调后的结果,统计一下误码率
% 统计每种symbol的错误率
symbol0_num = length(find(symbols_tx==0));
symbol1_num = length(find(symbols_tx==1));
symbol2_num = length(find(symbols_tx==2));
symbol3_num = length(find(symbols_tx==3));
err_symbol0_num = 0;
err_symbol1_num = 0;
err_symbol2_num = 0;
err_symbol3_num = 0;
%%%%%%%%%%%%%%%%%%%%%%%%
for i = 1:symbols_num
if demod_symbols_array(i) ~= symbols_tx(i)
switch symbols_tx(i)
case 0
err_symbol0_num = err_symbol0_num + 1;
case 1
err_symbol1_num = err_symbol1_num + 1;
case 2
err_symbol2_num = err_symbol2_num + 1;
case 3
err_symbol3_num = err_symbol3_num + 1;
end
end
end
disp(['symbol 0 误码率: ', num2str(err_symbol0_num / symbol0_num)]);
disp(['symbol 1 误码率: ', num2str(err_symbol1_num / symbol1_num)]);
disp(['symbol 2 误码率: ', num2str(err_symbol2_num / symbol2_num)]);
disp(['symbol 3 误码率: ', num2str(err_symbol3_num / symbol3_num)]);
得到结果,误码率为0
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)