各小组负责短距离无线计算器的开发,所要求的产品是一系列实现无线计算器服务MATLAB函数组成。最终的产品应提供一批m文件,这些m文件可以在一台标准电脑上作为发射机或接收机运行。
一.实验环境:
2台标准个人电脑;
2副耳麦,每副耳麦各连接一台电脑;
软件平台:MATLAB
二.实验原理:
1. 算式编码:
因为考量到算式中数字与运算符的总数小于16,因此考虑编码成自然BCD码。运算符使用自然BCD码中未涉及的二进制码编码。对于算式,我们假设两个运算数都小于等于4位,这样可以分别将其补齐至4位,将所有满足条件的算式化成9位算式。这个编码方式便可以将9位的算式按位编码成36位二进制数。解码同理,按四位进行分组,分别解码即可解出算式。
2. 信道编码:
信道编码采用七四线性分组码。线性分组码中信息位和监督位是由一些线性代数方程联系着的,或者说线性码使按照一组线性方程构成的。一般来说,若码字长度为n,信息位位数为k,则监督位数r=n-k。
因此对四位二进制数进行编码,需要三位监督位。按设定好的监督关系进行编码即可。
3. 帧同步方案:
巴克码序列是相位编码信号的一种,具有理想的自相关特性。巴克码的自相关函数的主峰和旁瓣均为底边宽度为2T的等腰三角形,主瓣峰值是 旁瓣峰值 的13倍。能够找到的巴克码只有7种,子脉冲长度分别为:2,3,4,5,7,11,13。已经证明巴克码的最大长度为 13 位。为与信道编码后序列保持一致,本传输方案选择7位巴克码进行帧同步。在接收序列进行帧同步时遍历接受序列,找到与巴克码相干结果为7(或5)的情况进行匹配,匹配成功后进行信道解码与信源解码。
4. 调制与解调:
调制解调方案采用2FSK。2FSK(Frequency Shift Keying)为二进制数字频率调制(二进制频移键控),用载波的频率来传送数字信息,即用所传送的数字信息控制载波的频率。2FSK信号便是符号“0”对应于载频 f1,而符号“1”对应于载频 f2(与 f1 不同的另一载频)的已调波形,而且 f1 与 f2 之间的改变是瞬间的。传“0”信号时,发送频率为 f1 的载波; 传“1”信号时,发送频率为 f2的载波。2FSK 是用不同频率的载波来传递数字消息的。选用2FSK的原因一方面是在查阅相关文献后发现前人的方案大多使用2FSK,摒弃了2ASK与2PSK等调制解调方案,另一方面是因为2FSK适用于比特率较低的情况,估计本实验结果比特率在30b/s上下,其次经过实测发现2FSK信号在实验环境下抗噪性能好于更简单的2ASK方法,因此采用2FSK。2FSK的解调采用相干解调法。二号机接收到的信号为已调信号,该信号通过解调器再次与一个载波信号相乘,解调后的信号包含一个低频信号和一个高频成分,用一个低通滤波器把高频成分滤掉,就得到了要传递的基带信号。以上这个解调过程就是相干解调。若将接收到信号与自己直接相乘,就可以省去产生载波的过程。2FSK需要将接收到信号通过两个不同的带通滤波器将两个频率成分滤出来再分别进行相干解调,然后通过低通滤波器将滤波结果的基带信号取出。
三.实验过程及部分代码:
以下步骤为完成一个算式的发送与接收的流程。
P.s. 为便于说明,将产生算式显示结果的电脑称为‘一号机’,接收算式并发回结果的电脑称为‘二号机’。
一号机随机生成操作数在四位以下的算式,并对算式进行编码:
随机算式的产生分为在0-9999中随机生成两个操作数,再随机生成运算符,将它们转换为字符串数组拼接在一起得到一个9位字符串型算式。之后对算式进行BCD编码。
编写BCD码的过程代码如下:
BCD=zeros(1,36);
Bak=[1 1 1 0 0 1 0];
for i=1:9
j=4*(i-1)+1;
if Ex(i)=='0'
elseif Ex(i)=='1' % 编码 转为BCD码 0~9分别对应二进制0000~1001 +-*/分别对应二进制1010~1011
BCD(j+3)=1;
elseif Ex(i)=='2'
BCD(j+2)=1;
elseif Ex(i)=='3'
BCD(j+2)=1;
BCD(j+3)=1;
elseif Ex(i)=='4'
BCD(j+1)=1;
elseif Ex(i)=='5'
BCD(j+1)=1;
BCD(j+3)=1;
elseif Ex(i)=='6'
BCD(j+1)=1;
BCD(j+2)=1;
elseif Ex(i)=='7'
BCD(j+1)=1;
BCD(j+2)=1;
BCD(j+3)=1;
elseif Ex(i)=='8'
BCD(j)=1;
elseif Ex(i)=='9'
BCD(j)=1;
BCD(j+3)=1;
elseif Ex(i)=='+'
BCD(j)=1;
BCD(j+2)=1;
elseif Ex(i)=='-'
BCD(j)=1;
BCD(j+2)=1;
BCD(j+3)=1;
elseif Ex(i)=='*'
BCD(j)=1;
BCD(j+1)=1;
elseif Ex(i)=='/'
BCD(j)=1;
BCD(j+1)=1;
BCD(j+3)=1;
end
end
- 一号机对产生的36位二进制数进行信道编码,并加上巴克码:
信道编码方法为(7,4)线性分组码。先定义了生成矩阵,易得到编码结果矩阵。同样二号机的信道解码部分使用同样的生成矩阵。
信道编码核心代码:
function C = hanming(M)
G = [1 0 0 0 1 1 0;
0 1 0 0 1 0 1;
0 0 1 0 0 1 1;
0 0 0 1 1 1 1];
[k,n] = size(G);
N = size(M,2);
r = mod(-rem(N,k),k);
M_add0 = [M,zeros(1,r)];
groups = ceil(length(M_add0)/k);
M_dis = reshape(M_add0,[k,groups]).';
C = mod(M_dis*G,2);
end
本段代码在给定输入序列时可以将其按四位进行分组,最后不足四位则补零,然后按给定生成矩阵进行编码。函数输出编码结果。
将BCD码进行分组信道编码后,在BCD码前加入巴克码,以便进行帧同步,核心代码:
BCD=hanming(BCD);
BCD=BCD';
BCD = reshape(BCD,1,63);
BCD=[Bak BCD];
一号机对产生的72位二进制数进行2FSK调制。2FSK调制分别使用20Hz与100Hz的载波进行调制。先生成方波,对基带信号进行扩展,之后与余弦振荡相乘加在一起,即可调制完成。之后用sound函数发出去。
核心代码:
fs=800; %抽样频率
dt=1/fs;
f1=20; %定义两列载波的频率
f2=100;
a=BCD;
g1=a;
g2=~a;
g11=(ones(1,800))'*g1; %产生方波信号
g1a=g11(:)';
g21=(ones(1,800))'*g2;
g2a=g21(:)';
t=0:dt:72-dt;
t1=length(t);
tuf1=cos(2*pi*f1.*t);
tuf2=cos(2*pi*f2.*t);
fsk1=g1a.*tuf1;
fsk2=g2a.*tuf2;
fsk=fsk1+fsk2;
pause(0.3);%0.2
sound(fsk,80000);
- 二号机进行录音,并解调:
使用audiorecorder对发出的信号进行录音,采样率80000,与1号机保持一致。计时发现发送信号的时间大概在1.3s,因此选择录音2s以便于识别。构建FIR滤波器将两个不同的频段滤出来进行相干解调,将自己与自己相乘得到相干结果,再通过滤波器将它们中我们所需的频段滤出来。之后对它每隔800个点抽一次样,与一号机保持一致。最后对于两路信号进行组合抽样判决,考虑到有背景噪声的问题,通过观察解调后曲线,通过多次测试设定阈值为0.75时可以得到正确的解调结果。
核心代码:
Receive=audiorecorder(80000,8,1);
recordblocking(Receive,2);%1.6
Receive = getaudiodata(Receive);
Receive=abs(Receive); % 求绝对值方便以后识别1
fsk=Receive;
figure(1);
subplot(221);
stem(fsk);
fs=800; %抽样频率
N=length(Receive)/fs;
dt=1/fs;
t=0:dt:N-dt;
b1=fir1(101,[10/800 20/800]);
b2=fir1(101,[90/800 110/800]); %设置带宽参数
H1=filter(b1,1,4*fsk); %b1为分子1为分母sn为滤波器输入序列
H2=filter(b2,1,4*fsk); %噪声信号同时通过两个滤波器
sw1=H1.*H1; %相干解调乘以同频同相的载波
sw2=H2.*H2;%经过相乘器
bn=fir1(101,[2/800 10/800]); %经过低通滤波器
st1=filter(bn,1,sw1);
st2=filter(bn,1,sw2);
for i=1:length(t)
if(st1(i)>=st2(i))
st(i)=0;
else st(i)=st2(i);
end
end
st=st1+st2;
st(size(st)+1)=st(size(st));
subplot(222);
stem(st);
bfsk=zeros(1,N);
for i=801:800:800*N+1
j=fix(i/800);
bfsk(j)=st(i);
end
for i=1:length(bfsk)
if(bfsk(i)>=0.75)
bfsk(i)=1;
else bfsk(i)=0;
end
end
经过解调后信号会变成0,1序列,对0,1序列进行后续处理与操作。
- 二号机对解调后信息进行采样,并进行巴克码的检测:
检测巴克码可以靠遍历序列求相关来进行。但是因为设定门限值为7,也可以靠按位比对来进行,从而降低算法的复杂程度。
核心代码:
BCD2=zeros(1,80);
for i=1:length(bfsk)-6
if bfsk(i)==1
if bfsk(i+1)==1
if bfsk(i+2)==1
if bfsk(i+3)==0
if bfsk(i+4)==0
if bfsk(i+5)==1
if bfsk(i+6)==0
break;
end
end
end
end
end
end
end
end
k=1;
for j=i:i+71
if i>length(bfsk)-72
break;
end
BCD2(k)=bfsk(j);
k=k+1;
End
这里将帧同步后得到的带巴克码的72位二进制数存进数组。若发现巴克码的位置后面已经没有72个数字,这时直接中断写入数组,认为发送与接收同步失败。通过合理的同步策略,可以尽可能的降低出现这种问题的概率。
- 二号机对检测到巴克码之后所找到的算式序列进行信道解码与信源解码,得到最终算式:
信道解码方法:
同样在已知生成矩阵的情况下,输入n*7的矩阵,输出结果为解码结果。同时,通过对于已知错误图样的定义,可以发现一位错码位置并进行纠正。
信道解码函数核心代码:
function C_result = decode1(R)
G = [1 0 0 0 1 1 0;...
0 1 0 0 1 0 1;...
0 0 1 0 0 1 1;...
0 0 0 1 1 1 1];
[groups,lines] = size(R);
[kk,n] = size(G);
H = [G(:,kk+1:n).',eye(3)];
S = mod(R*(H.'),2);
[S_row,S_column] = size(S);
SE = {[0 0 0],[0 0 0 0 0 0 0];...
[0 0 1],[0 0 0 0 0 0 1];...
[0 1 0],[0 0 0 0 0 1 0];...
[1 0 0],[0 0 0 0 1 0 0];...
[1 1 1],[0 0 0 1 0 0 0];...
[0 1 1],[0 0 1 0 0 0 0];...
[1 0 1],[0 1 0 0 0 0 0];...
[1 1 0],[1 0 0 0 0 0 0]};
C_result = zeros(S_row,n);
[SE_row,SE_column] = size(SE);
for m=1:S_row
for n=1:SE_row
if all(S(m,:) == cell2mat(SE(n,1)))
C_result(m,:) = R(m,:)+cell2mat(SE(n,2));
C_result(m,:) = mod(C_result(m,:),2);
end
end
end
C_result = C_result(:,1:kk);
end
- 之后对于输出36位二进制数进行信源解码即可,过程类似编码过程。核心代码较长,在此不进行展示,解码之后得到9位字符串组成的算式。
- 二号机将算出结果进行信源编码与信道编码,加帧同步码,之后用2FSK调制发送。这部分实现的方法同一号机信源编码,信道编码,2FSK发送过程。不同之处为二号机发送的信号为数字,没有符号位的信息。
- 一号机将收到信号解调,将运算结果解出来。
这个过程与二号机的接收过程类似,采用同样的方法进行接收,解调,信道解码,信源解码。区别在于因为硬件设备不同,导致解调过程中判决门限值不同。
- 问题及解决方案:
关于硬件设备:
我们选择的耳机设备在收到连续的同频波时,强度会不断增高,在频率不停变化的时候,声波的强度是正常的。因此在实测阶段我们发现应该尽量避免发8位中连0数量超过5位的数字。为做到这一点,我们考虑过改变0的BCD码,但由于涉及整个信源编码较为复杂故改变方法,最终解决方案为将所有发送信号取反后发送即将‘0’信号和‘1’信号互换后发送。这样就可以避免0过多导致判断阈值难以设定的问题。
关于同步问题:
实验时发现两台电脑在用MATLAB控制录音机开始录音时,会有大约0.2秒的延迟。使用程序内tic toc计时,设定录音机录音时间为2秒,在循环的第一轮,会出现近0.7秒的延迟;后9轮中会出现0.2秒左右的延迟,另外发出声音的sound函数所占的时间不会被tic toc计入。因此最后的解决方案是对录音与发出信号的时间都取一个多次测试后证实不可超过的值,即发出信号控制在2.2秒,接收信号控制在2.8秒。控制方法代码为:
tic;
toc;
t=toc;
pause(2.2-t);
通过这个方法把每一个来回的时间控制在5s,且录音录到含有信息的部分在整段的中央,易于解调与帧同步。
- 实验结果:
一号机程序运行展示:
在最终测试中,完成10个算式的发送与接收共花费50s,平均一个算式5s,符合预期。比特率为28.8b/s,算式错误率约为2%,基本上出现在巴克码同步失败的情况。在背景随机噪声较大的情况下,错误率会提高,会出现数字与算式中某些位出错的问题。在确定高斯白噪声下,精准度可以由解调程序控制,较难被影响。
以下的部分介绍这个程序的用法。bcd2fsk.m作为1号机,产生算式并进行发送,接收结果并解调显示;fsk2bcd.m作为2号机,接收算式并解调计算,得到结果调制发送。程序在解调函数的地方要针对硬件进行调参,具体在:
for i=1:length(bfsk)
if(bfsk(i)>=0.75)
bfsk(i)=1;
else bfsk(i)=0;
end
end
这个地方要根据耳机接收声音的强度来决定(1,2号机中均有这个部分),0.75为阈值,在接收端解调后做出的图上可以看出,0与1需要给定一个阈值进行判别。这个值因硬件不同有很大差别,需要反复调试才能得到最佳效果。程序使用时,1号机与2号机需同时开启,用于发声的耳机紧贴用于接收的耳机孔,两端同理。一般第一次使用这个程序会运行的很慢,所以第一次运行可以在发出声音/麦克风开始录制时直接切断程序。调试成功后上限大概在5s传输一个来回,正确率极高。这个框架也可以更高效的传输程序,目前的状态下发送与接收的净时间大概2.92秒,在同步上浪费时间较多;如果多个算式一起编码,可以省去同步时间得到更高的效率,当然也可以在编解码以及调制解调的地方继续优化以实现更高的速度。