**实验介绍:**本次实验应用软件无线电模块ADALM-PLUTO和MATLAB软件,并结合现代无线数字通信原理,利用MATLAB设计出完整的 QPSK无线通信系统,利用ADALM-PLUTO实现完整的QPSK无线通信系统端到端通信,并对实际的QPSK无线通信系统进行分析。
一、QPSK调制解调原理
(一)QPSK信号
一个MPSK信号码元可以表示为:
S_k (t)=A cos(ω_0 t+θ_k ),k=1,2,…,M
式中:A为常数;θ_k为一组间隔均匀的受调制相位;通常M取2的某次幂
不失一般性,我们可以令A=1,然后将上式MPSK信号码元表达式展开写成:
S_k (t)=A cos(ω_0 t+θ_k )=a_k cos〖ω_0 t〗-b_k sin〖ω_0 t〗
式中:a_k=cosθ_k,b_k=〖sinθ〗_k
令M=4,对4PSK信号作进一步分析。4PSK常称为正交相移键控(Quadrature Phase Shift Keying,QPSK)。他的每个码元含有2比特的信息,现用ab表示这两个比特。发送码元序列在编码时需要先将每两个比特分成一个双比特组ab。ab有4种排列,即00、01、10、11。然后用4种相位之一去表示每种排列。各种排列的相位之间的关系通常按格雷码安排。
图1 QPSK信号的矢量图
(二)QPSK调制
QPSK信号的产生方法有两种方法。第一种是用相乘电路,如图2所示。图中输入基带信号A(t)是二进制不归零双极性码元,它被“串/并变换”电路变成两路码元a和b。变成并行码元a和b后,其每个码元的持续时间是输入码元的2倍。这两路并行码元序列分别用以和两路正交载波相乘。
图2 QPSK信号产生方法
(三)QPSK解调
QPSK信号的解调原理方框图示于图3中。由于QPSK信号可以看作是两个正交2PSK信号的叠加,所以用两路正交的相干载波去解调,可以很容易地分离这两路正交的2PSK信号。相干解调后的两路并行码元a和b,经过并/串变换后,成为串行数据输出。
三、使用MATLAB编程实现QPSK相位映射原理及过程
(一)相位映射原理
实际编程里选择的角度对应关系为
角度-3π/4 、 3π/4、π/4 、-π/4分别对应格雷码向量00、01、11、10
二进制码元 00 01 11 10
映射相位 -3π/4 3π/4 -π/4 -π/4
图4表1 QPSK相位映射关系
(二)实现步骤
①创建一个大小为4的一维数组table,储存QPSK向量:ⅇ^jθ,
其中θ=-3π/4 、 3π/4、π/4 、-π/4
代码:table=exp(j*[-3/4pi 3/4pi 1/4pi -1/4pi]);
table=table([0 1 3 2]+1); % 实现格雷码的对应
②将输入数据进行串并转换,原大小为1×length的数据,变成2×(length/2)的二维数组
代码:inp=reshape(bits_in,2,full_len/2);
%这一步是为了后面将格雷码变换成数组下标的数组运算符合矩阵乘法
③将格雷码变换成数组下标,根据table的规则进行相位映射
代码:mod_symbols=table([2 1]*inp+1); % 根据对应的table进行相位映射
实际代码效果检验:
(三)完整matlab代码
~isempty(findstr(modulation, 'QPSK'))
table=exp(j*[-3/4*pi 3/4*pi 1/4*pi -1/4*pi]);
% generates QPSK symbols
table=table([0 1 3 2]+1);
% Gray code mapping pattern for QPSK symbols
inp=reshape(bits_in,2,full_len/2);
mod_symbols=table([2 1]*inp+1);
% maps transmitted bits into QPSK symbols
四、通信过程及协议分析
(一)通信过程
总体功能描述:发送一定长度内任意长度文本内容(汉字、英文),接收端显示发送内容。适当改动程序,可自发自收,也可两个用户进行相互通信。
(127位)帧同步信息 数据部分 (32位)CRC校验码
图5 帧结构图
本程序代码里一帧的长度固定为703位,这里的数据部分指的是经过处理之后的数据.
1.发送
①输入信息:根据UTF-8编码原则,汉字编码按两个字节计算,字符按一个字节,该实验代码限制了数据部分转为bit的位数最大为992bit。
即如果发送的是纯汉字的话,最多发送的个数为992/8/2=64个,如果发送纯字符的话,最多发送的个数为992/8=124个
②由于固定了一帧的长度,原始数据也就固定是992bit,如果输入的数据长度不足992,那么在后面补零到992位。
③生成帧同步的帧头并进行相位映射,最终长度为127bit
④crc32校验(CRC校验码长度32 bits)
⑤加扰(不改变数据位数)
⑥QPSK调制,相位映射(数据经过QPSK映射后会减半,(992bit+32bit)变为512bit)
⑦插入导频,每64bits插入8位导频信息(前面的数据映射后是512bit,则512/64=8,即会插入8次导频信息,共64bits,最终输出512+64=576bits)
⑧插入帧同步(帧同步是127bits,则一帧的长度变为(576+127=703)bits)
⑨上采样和滚降滤波
⑩将数据乘以2^14,增大DAC输出,根据DAC数据总长度为10万bits,将DAC数据多次复制,将数据长度变为DAC数据长度在10万bits内最大倍数,再通过补零将数据变为10万bits,将10万bits数据传给ADALM-PLUTO模块
程序流程图如下:
2.接收
匹配滤波->数据归一化->时钟同步->搜索帧同步,获取有效帧信息->三次频率同步->相位估计->删除帧同步(导频+信息码元+CRC码)->相位追踪->删除导频(只剩信息码元+循环校验码)->时域均衡->信号解调->循环码校验,同时除去循环校验码->将二进制转换为文本信息,显示接收到的信息。
(二)通信协议分析
%%%%%%%%%
timer_id = timer;
timer_id.StartDelay = 1.0;
timer_id.Period = 0.1;
timer_id.ExecutionMode = 'fixedSpacing';
timer_id.TimerFcn = @timer_handler;
%
start(timer_id);
function timer_handler(~,~)
global s
output=readRxData(s);
I = output{1};
Q = output{2};
Rx = I+1i*Q;
bpsk_rx_func(Rx(end/2:end));
end
五、实验中遇到的困难和解决方法
(1)如何实现任意长度的信息发送?
答: 因为每一次发送的数据长度设为固定的,所以如果长度小于设定值,可以通过补0到指定长度,如果长度大于设定值,可以采取分组发送的方式,分几次发送数据.
(2)如何发送中文信息?
答:我们小组通过查找资料发现,采用UTF-8编码,汉字就能被编码成两个字节的二进制数据。
(3)如何实现双机通信?
答:改动硬件初始化,实现
(4)QPSK的帧同步信号
答:未解决。本系统采用QPSK的帧同步信号
(5)如何实现全双工?
答:因为发送模式需要调用input函数占用程序进程,使程序停在这一句,而且matlab没有多线程的实现,解决办法是调用matlab的定时器,固定时间去接收解调信号,以此实现全双工
六、参考代码
1、产生帧同步信号:
tx_gen_m_seq([1 0 0 0 0 0 1]);
2、调制函数与解调函数
tx_modulate(seq_sync, 'BPSK');
rx_qpsk_demod(out_signal8);
mod_symbols=tx_modulate(sym_bits, 'QPSK');
QPSK调制:
if ~isempty(findstr(modulation, 'QPSK'))
% Angle [pi/4 3*pi/4 -3*pi/4 -pi/4] corresponds to
% Gray code vector [00 10 11 01], respectively.
table=exp(j*[-3/4*pi 3/4*pi 1/4*pi -1/4*pi]); % generates QPSK symbols
table=table([0 1 3 2]+1); % Gray code mapping pattern for QPSK symbols
inp=reshape(bits_in,2,full_len/2);
mod_symbols=table([2 1]*inp+1); % maps transmitted bits into QPSK symbols
QPSK解调:
rx_symbols=rx_symbols./sqrt(2);
soft_bits = zeros(2,length(rx_symbols));
evm_real=zeros(1,length(rx_symbols));
evm_image=zeros(1,length(rx_symbols));
bit0 = real(rx_symbols);
for i=1:length(rx_symbols)
if(bit0(i)>0)
evm_real(i)=bit0(i)-1/sqrt(2);
bit0(i)=1;
else
evm_real(i)=bit0(i)-(-1/sqrt(2));
bit0(i)=0;
end
end
bit1 = imag(rx_symbols);
for j=1:length(rx_symbols)
if(bit1(j)>0)
evm_image(j)=bit1(j)-1/sqrt(2);
bit1(j)=1;
else
evm_image(j)=bit1(j)-(-1/sqrt(2));
bit1(j)=0;
end
end
evm=(evm_real.^2+evm_image.^2).^0.5;
evm=sum(evm)/length(evm);
soft_bits(1,:) = bit0;
soft_bits(2,:) = bit1;
soft_bits_out = soft_bits(:)';
2、实现中文编码函数
function msg_bits = str_to_bits(msgStr)
msgBin = de2bi(int8(msgStr),8,'left-msb');
len = size(msgBin,1).*size(msgBin,2);
msg_bits = reshape(double(msgBin).',len,1).';
end
3、crc校验函数
crc32(mst_bits);
4、白噪声化函数与去白噪声化
scramble([1,1,0,1,1,0,0], inf_bits);
descramble(soft_bits_out(i),Si)
5、插入导频函数和删除导频
insert_pilot(mod_symbols);
rx_delete_pilot(out_signal6)
6、滚降滤波,上采函数
rcosdesign(1,128,4);
upfirdn(trans_symbols,fir,4);
7、将传输数据复制多份,补零,达到10万位长度
txdata = bpsk_tx_func;
txdata = round(txdata .* 2^15);
txdata=repmat(txdata, floor(send_and_receive_length/length(txdata)),1);
if length(txdata))),abs(imag(rx_sig_filter.'))]);
rx_sig_norm=rx_sig_filter ./c1;
14、位同步
rx_timing_recovery(rx_sig_norm.');
15、频率同步函数
rx_freq_sync(coarse_sync_seq,4,rx_frame)
16、相位估计
rx_phase_sync(out_signal3,local_sync)
17、删除帧同步信号和相位跟踪
out_signal4(length(local_sync)+1:end);
rx_phase_track(rx_no_syn_seq);
18、时域均衡
rx_time_equalize(out_signal7)
19、显示接收字符
disp(native2unicode(b,'UTF-8'));
20.主函数
while(flag==1)
% if(partern=='1')
disp('ÊäÈë¿ò×î¶à124¸öÎı¾');
msgStr=input('ÇëÊäÈë´«Ë͵ÄÐÅÏ¢£º','s');%ÊäÈëÐÅÏ¢
% mst_bits_first=str_to_bits(msgStr);
mst_bits_first=chinese_to_bits(msgStr);
o=length(mst_bits_first);
k=o/max_bits;
% %% string to bits
if k<1||k==1
mst_bits=[mst_bits_first zeros(1,max_bits-o)];
end
txdata = bpsk_tx_func(mst_bits);
txdata = round(txdata .* 2^14);
txdata=repmat(txdata, 8,1);
% fprintf('Transmitting Data Block %i ...\n',i);
Input{1} = real(txdata);
Input{2} = imag(txdata);
writeTxData(s, Input);
prompt = 'Do you want continue? Y/N [Y]: ';
str = input(prompt,'s');
if str == 'Y'|| str == 'y'
flag=1;
else
flag=0;
end
end
21.定时器部分
%%%%%%%%%
timer_id = timer;
timer_id.StartDelay = 1.0;
timer_id.Period = 0.1;
timer_id.ExecutionMode = 'fixedSpacing';
timer_id.TimerFcn = @timer_handler;
%
start(timer_id);
function timer_handler(~,~)
global s
output=readRxData(s);
I = output{1};
Q = output{2};
Rx = I+1i*Q;
bpsk_rx_func(Rx(end/2:end));
end
希望获得整个工程代码的同学也可以私信联系我!!