记录采用Xilinx ZYNQ系列板卡+AD9361实现简单2x2 MIMO通信过程


2020-06-19

写在最前,本文对MATLAB提供的例程做了一些检索和解读,并在自己的需求上做了更改。

最近一段时间老师不知道在研究些啥,突然之间对硬件产生了浓厚的兴趣,并且为我们20级的新生购置了几块SDR设备。虽然不知道为什么,但是拿着这些板子也可以作为无聊的宅家消遣,毕竟我已经半年没有去过学校了。

软件/硬件 准备

硬件

本次使用到的硬件为ZedBoard,是一块Xilinx ZYNQ 7000系列的开发板,该开发板的特点是成本低,操作简单,ARM处理器附带一块ZYNQ-7000系列的FPGA。我个人而言对硬件不甚了解,在与同学的探讨中认为这块板子上的FPGA核是作为辅助运算而存在的。在本次实验中,主要采用MATLAB作为编程平台,通过一个Linux系统驱动ARM核心,并且由ARM核调用FPGA进行部分运算。这块板子可以采用Vivado HLS那一套工具做相对低层次一些的开发,而我作为不太懂得硬件描述语言的人而言只能采用MATLAB工具作为主要编程工具。

除了Zedboard以外,在扩展接口上我们连接了一块FMCOMMS3的板卡,是一块基于AD9361的射频卡,这将作为本次通信实验的收发机。

软件

PC上的操作需要全程依赖MATLAB进行,因此首先安装对应的支持包。在附加功能中找到获取硬件支持包,在其中搜索Communications Toolbox Support Package for Xilinx Zynq-Based Radio即可找到对应支持,按提示安装即可。下载比较慢的情况可以酌情使用科学 上网工具。安装过程比较久。

实际上安装持续了将近一个小时

安装后开始简单的硬件测试阶段,点击配置选项按步骤进行硬件的配置,按照右侧对话框的内容配置zedboard,最后可以运行一个小脚本验证。

这个配置过程主要在于给SD卡写入一个小型Linux系统用于ARM控制。zynq系列板卡本质上相当于ARM板携带了一块高性能FPGA,采用SD卡的启动方式也是为ARM芯片服务。在默认配置中,板卡的IP地址会被设置为192.168.3.2,此时计算机网卡对应有线网的IP地址会被MATLAB自动设置为192.168.3.1,当我们使用网络时可能需要恢复为之前的设置,因此建议先对原来的IPv4配置进行保存。处理后根据要求断开板卡的电源开关,将跳线帽设置为正确位置,插入SD卡与网线,网线连接计算机之后接通电源开始启动。当板卡完全启动后,MATLAB会给出一个简单的SDR脚本,测试板卡的各个通道是否可以正常工作。这里采用的AD9361支持两路发射两路接收,在通信中相当于全数字\(2\times 2\) MIMO系统,而测试脚本则每次只激活一个收发RF链路进行测试。该测试主要是单音测试,代码比较简单,设置了单音信号参数后会在I路Q路分别发送正交的单音信号,并重复发射到结束,接收机则进行接收,并在分析窗口展示接收到的波形、星座图与频谱图。

需要额外说明的是,信号处理与分析的主要工作是在PC端完成的。

在实验前也还需要确保MATLAB中的LTE Toolbox以及Communication Toolbox已经被正确安装。

1x1 MIMO系统的建立与测试

MATLAB的官方给了相当多的例程,这些有空可以自己去研究,我的第一个目标是实现1t1r系统的搭建,需要满足

  1. 可以发送指定的数据
  2. 可以接收到发射的数据并统计误码率
  3. 可以借助信道估计技术得到正确的信道。
  4. 可以观察星座图和频谱图

能够实现这些功能,这个开发板平台就可以替代一些成品售卖的SDR系统了,穷科研的目标就可以达到了(笑)。首先实例化一个频谱图模块和一个星座图模块,

clear all;
spectrumScope = dsp.SpectrumAnalyzer( ...
    'SpectrumType',    'Power density', ...
    'SpectralAverages', 10, ...
    'YLimits',         [-130 -40], ...
    'Title',           'Received Baseband LTE Signal Spectrum', ...
    'YLabel',          'Power spectral density');
constellation = comm.ConstellationDiagram('Title','Equalized PDSCH Symbols',...
                                'ShowReferenceConstellation',false);

然后我们为AD9361射频卡做一个发射机配置

txsim = struct;
txsim.SDRDeviceName = 'AD936x';
radio = sdrdev(txsim.SDRDeviceName); % Create SDR device object

接下来进行详细的系统设计。根据MATLAB中例程Transmit and Receive LTE MIMO Using a Analog Devices AD9361/AD9364的系统设计方案,完整流程为①输入图像并转化为二进制数据流②生成基带LTE信号(LTE工具箱),将二进制数据流封装为不同传输块③为硬件准备合适的传输形式④在合适的中心载波上发射。首先对发射机进行合适的配置

txsim.RC = 'R.7';       % Base RMC configuration, 10 MHz bandwidth
txsim.NCellID = 88;     % Cell identity
txsim.NFrame = 700;     % Initial frame number
txsim.TotFrames = 1;    % Number of frames to generate
txsim.DesiredCenterFrequency = 2.45e9; % Center frequency in Hz
txsim.NTxAnts = 1;
txsim.Gain = -10;
sdrTransmitter = sdrtx(txsim.SDRDeviceName);

其中txsim.RC参数给定了一个参考测量信道的参数,R.7则对应(Port0, 50 RB, 64QAM, CellRefP=1, R=3/4)参数。将发射机增益降低可以观察不同信噪比条件下的效果,因为距离等参数是固定的,可以通过发射功率来灵活改变发射或接收信噪比。最后通过sdrtx函数进行发射机的实例化。
然后读取数据进行预处理。采用matlab中预置的图像pepper进行发送,此时

fileTx = 'peppers.png';            % Image file name
fData = imread(fileTx);            % Read image data from file
scale = 0.5;                       % Image scaling factor
origSize = size(fData);            % Original input image size
scaledSize = max(floor(scale.*origSize(1:2)),1); % Calculate new image size
heightIx = min(round(((1:scaledSize(1))-0.5)./scale+0.5),origSize(1));
widthIx = min(round(((1:scaledSize(2))-0.5)./scale+0.5),origSize(2));
fData = fData(heightIx,widthIx,:); % Resize image
imsize = size(fData);              % Store new image size
binData = dec2bin(fData(:),8);     % Convert to 8 bit unsigned binary
trData = reshape((binData-'0').',1,[]).'; % Create binary stream

首先对图像做了\(0.5\)倍缩放,降低传输压力。这些代码实际上就是每两个像素点选择一个,最终实现25%压缩比的有损压缩。RGB图像中均为uint8数据类型,十进制数据范围0-255,将十进制转化为8位二进制后得到了二进制数据流,重排之后形成数据流(此处binData为char型,实际上对于每一个位仍然是一个位存储,而不采用char保存)。对于不必要的中间变量可以进行clear处理,节约内存消耗,一个参考为8G内存在开启MATLAB与Chrome后内存即满。

figure(imFig);
imFig.Visible = 'on';
subplot(211); 
    imshow(fData);
    title('Transmitted Image');
subplot(212);
    title('Received image will appear here...');
    set(gca,'Visible','off'); % Hide axes
    set(findall(gca, 'type', 'text'), 'visible', 'on'); % Unhide title

pause(1); % Pause to plot Tx image

进行图片显示,方便后期进行对比。
然后是实际传输中很重要的一个环节,即传输波形设计。首先进行rmc生成

rmc = lteRMCDL(txsim.RC);
% Calculate the required number of LTE frames based on the size of the
trBlkSize = rmc.PDSCH.TrBlkSizes;
txsim.TotFrames = ceil(numel(trData)/sum(trBlkSize(:)));
% Customize RMC parameters
rmc.NCellID = txsim.NCellID;
rmc.NFrame = txsim.NFrame;
rmc.TotSubframes = txsim.TotFrames*10; % 10 subframes per frame
rmc.CellRefP = txsim.NTxAnts; % Configure number of cell reference ports
rmc.PDSCH.RVSeq = 0;

lteRMCDL用于产生LTE协议相关的参考信道参数,rmc.PDSCH.TrBlkSizes是根据协议分配的每一帧传输的数据量,据此可以计算出总共需要的帧数(向上取整)。本次传输中计算的总帧数txsim.TotFrames = 5CellID这个参数应该是没有什么意义,TotSubframes是子帧数,根据协议,每一帧中包含10个子帧;根据前面的单天线假设,此处的CellRefP也为1。另外,我们可以从rmc.DuplexMode的输出得到,本次实验是基于FDD

% Fill subframe 5 with dummy data
rmc.OCNGPDSCHEnable = 'On';
rmc.OCNGPDCCHEnable = 'On';
fprintf('\nGenerating LTE transmit waveform:\n')
fprintf('  Packing image data into %d frame(s).\n\n', txsim.TotFrames);
% Pack the image data into a single LTE frame
[eNodeBOutput,txGrid,rmc] = lteRMCDLTool(rmc,trData);% Generate downlink RMC waveform

这里采用lteRMCDLTool生成合适传输的波形。该函数运行结束后会得到一个输出波形和一个时频资源块的分配。例如此处计算需要5帧才能实现传输,我们绘制txGrid即可得到

这时完整的五个帧,而对于其中的每一帧,我们可以单独绘制为

这个图中包含的信息有,采用600子载波传输,每一子帧传输12个数据符号和两个特殊符号,总计14个符号,一帧总共有10个子帧。但是很显然每一个子帧传输的数据量并不相同,例如第一个子帧的数据量较少(部分时频资源块传输了特殊信息),第六个子帧完全没有传输数据。将五帧叠加在一起就可以得到传输的资源块。除了txGrid之外,函数还返回了eNodeBOutput作为传输波形,这个传输波形已经按照信道特征做了合适的重采样、滚降滤波,截取片段的功率谱如图所示

另外需要注意的是,对于多天线而言,生成的波形的第二个维度将是天线数量。经过处理的数据将用于数据发射,后面继续配置发射机。

sdrTransmitter.BasebandSampleRate = rmc.SamplingRate; % 15.36 Msps for default RMC (R.7) 
                                          % with a bandwidth of 10 MHz  
sdrTransmitter.CenterFrequency = txsim.DesiredCenterFrequency;
sdrTransmitter.ShowAdvancedProperties = true;
sdrTransmitter.Gain = txsim.Gain;
fprintf('Setting channel map to ''1''.\n\n'); 
sdrTransmitter.ChannelMapping = 1;
powerScaleFactor = 0.8;
eNodeBOutput = eNodeBOutput.*(1/max(abs(eNodeBOutput))*powerScaleFactor);
% Cast the transmit signal to int16 --- 
% this is the native format for the SDR hardware. 
eNodeBOutput = int16(eNodeBOutput*2^15);

基带采样率是\(15.36\)MHz,发射机射频中心频率是前面设置的\(2.45\)GHz,这个具体数值可以根据需要自由配置。发射机增益也是前面配置的\(-10\)dB,然后将单天线映射加入配置。功率因数是限制了最大值的取值范围,为方便发射。处理后将发射向量映射到16位整数。16位整数的范围是\(-32768\sim 32767\)
一句话来循环发射信号

transmitRepeat(sdrTransmitter,eNodeBOutput);

这个命令会让发射机sdrTransmitter按照预定配置循环发送波形eNodeBOutput


接收机的物理设计比发射机更为复杂,在SDR过程中就显得稍微简单一些,主要步骤为

  1. 捕获合适数量的帧
  2. 修正频率偏移
  3. 同步LTE帧
  4. OFDM解调得到时频资源块
  5. 信道估计
  6. 解码PDSCH 和DL-SCH 以便观测观测block内的其他数据信息
  7. 重组接收信息组成接收图像
rxsim = struct;
rxsim.RadioFrontEndSampleRate = sdrTransmitter.BasebandSampleRate; % Configure for same sample rate
                                                       % as transmitter
rxsim.RadioCenterFrequency = txsim.DesiredCenterFrequency;
rxsim.NRxAnts = txsim.NTxAnts;
rxsim.FramesPerBurst = txsim.TotFrames+1; % Number of LTE frames to capture in each burst.
                                          % Capture 1 more LTE frame than transmitted to  
                                          % allow for timing offset wraparound...
rxsim.numBurstCaptures = 1; % Number of bursts to capture
% Derived parameters
samplesPerFrame = 10e-3*rxsim.RadioFrontEndSampleRate; % LTE frames period is 10 ms

首先定义接收机参数为一个结构,基带采样频率、射频频率、天线数与发射机相同,采用突发接收模式,每次捕获6帧(比发射时多一帧,便于实现定时偏移的回绕),另外由于同步的问题比较复杂,这里并没有考虑同步的情况,只在重复发送中突发捕获一次。每一帧的采样数可以计算,协议要求每一帧10ms,相比基带采样率15.36M,则每帧需要153600点。同理,我们发送5帧,因此waveform有768000点。

rxsim.SDRDeviceName = txsim.SDRDeviceName;
sdrReceiver = sdrrx(rxsim.SDRDeviceName);
sdrReceiver.BasebandSampleRate = rxsim.RadioFrontEndSampleRate;
sdrReceiver.CenterFrequency = rxsim.RadioCenterFrequency;
sdrReceiver.SamplesPerFrame = samplesPerFrame;
sdrReceiver.OutputDataType = 'double';
sdrReceiver.EnableBurstMode = true;
sdrReceiver.NumFramesInBurst = rxsim.FramesPerBurst;

% Configure RX channel map
sdrReceiver.ChannelMapping = 1:rxsim.NRxAnts;

% burstCaptures holds sdrReceiver.FramesPerBurst number of consecutive 
% frames worth of baseband LTE samples. Each column holds one LTE frame 
% worth of data.
burstCaptures = zeros(samplesPerFrame,rxsim.NRxAnts,rxsim.FramesPerBurst);

以上继续进行接收机配置。实例化接收机后配置基带采样率、中心频率、每一帧的采样数、输出类型、突发接收及每次接受帧数。然后预分配接收数据的内存。

enb.PDSCH = rmc.PDSCH;
enb.DuplexMode = 'FDD';
enb.CyclicPrefix = 'Normal';
enb.CellRefP = 4; 

本次实验是假设收发机完全已知对方的发射模式等信息,例如FDD模式,CP模式等。直接在接收机处将这些信息预置。enb在LTE中一般代表基站。

SampleRateLUT = [1.92 3.84 7.68 15.36 30.72]*1e6;
NDLRBLUT = [6 15 25 50 100];
enb.NDLRB = NDLRBLUT(SampleRateLUT==rxsim.RadioFrontEndSampleRate);
if isempty(enb.NDLRB)
    error('Sampling rate not supported. Supported rates are %s.',...
            '1.92 MHz, 3.84 MHz, 7.68 MHz, 15.36 MHz, 30.72 MHz');
end
fprintf('\nSDR hardware sampling rate configured to capture %d LTE RBs.\n',enb.NDLRB);

从查找表中选取合适的数值。实际上前面已经定义了rxsim.RadioFrontEndSampleRate,所以后面操作比较傻瓜。这里附加注释RB,全称为Resource Block,一个子帧时长中12个子载波为一组被称为是一个RB,这里是600子载波的系统,因此说接收50个RB

% Channel estimation configuration structure
cec.PilotAverage = 'UserDefined';  % Type of pilot symbol averaging
cec.FreqWindow = 9;                % Frequency window size in REs
cec.TimeWindow = 9;                % Time window size in REs
cec.InterpType = 'Cubic';          % 2D interpolation type
cec.InterpWindow = 'Centered';     % Interpolation window type
cec.InterpWinSize = 3;             % Interpolation window size

配置合适的信道估计。其中导频平均是一种导频计算方法,这种方法中导频将在时间上进行平均,在这个过程中自然是假设信道不发生改变,在实际通信场景中信道在这种时间量级中的变化大多数情况下是微乎其微的,且估计算法不能保证任何时刻都是实时的。除此之外,由于导频插入时并非在频域每一个点都插入,而往往是梳状插入,这样估计的信道也不是所有频率点上的信道,而是部分点上的。由于信道的特征,假如信道在时域上只有4个tap,那么频域上只需要取4个导频就可以重建时域信道,因此部分点的估计信道可以推出完整信道(DFT插值)。此处采用的插值为立方差值法,具体参数都已经给出。
接下来的部分为接收机部分代码,这部分比较长,但由于是一段循环内容,因此未进行分割。

enbDefault = enb;

while rxsim.numBurstCaptures
    % Set default LTE parameters
    enb = enbDefault;
    
    % SDR Capture
    fprintf('\nStarting a new RF capture.\n\n')
    len = 0;
    for frame = 1:rxsim.FramesPerBurst
        while len == 0
            % Store one LTE frame worth of samples
            [data,len,lostSamples] = sdrReceiver();
            burstCaptures(:,:,frame) = data;
        end
        if lostSamples && frame > 1
            warning(message('sdrpluginbase:zynqradioExamples:DroppedSamples'));
        end
        len = 0;
    end    
    rxWaveform = burstCaptures(:);
    % Show power spectral density of captured burst
    spectrumScope.SampleRate = rxsim.RadioFrontEndSampleRate;
    spectrumScope(rxWaveform);
    
    % Perform frequency offset correction for known cell ID
    frequencyOffset = lteFrequencyOffset(enb,rxWaveform);
    rxWaveform = lteFrequencyCorrect(enb,rxWaveform,frequencyOffset);
    fprintf('\nCorrected a frequency offset of %i Hz.\n',frequencyOffset)
    
    % Perform the blind cell search to obtain cell identity and timing offset
    %   Use 'PostFFT' SSS detection method to improve speed
    cellSearch.SSSDetection = 'PostFFT'; cellSearch.MaxCellCount = 1;
    [NCellID,frameOffset] = lteCellSearch(enb,rxWaveform,cellSearch);
    fprintf('Detected a cell identity of %i.\n', NCellID);
    enb.NCellID = NCellID; % From lteCellSearch
    
    % Sync the captured samples to the start of an LTE frame, and trim off
    % any samples that are part of an incomplete frame.
    rxWaveform = rxWaveform(frameOffset+1:end,:);
    tailSamples = mod(length(rxWaveform),samplesPerFrame);
    rxWaveform = rxWaveform(1:end-tailSamples,:);
    enb.NSubframe = 0;
    fprintf('Corrected a timing offset of %i samples.\n',frameOffset)
    
    % OFDM demodulation
    rxGrid = lteOFDMDemodulate(enb,rxWaveform);
    
    % Perform channel estimation for 4 CellRefP as currently we do not
    % know the CellRefP for the eNodeB.
    [hest,nest] = lteDLChannelEstimate(enb,cec,rxGrid);
    
    sfDims = lteResourceGridSize(enb);
    Lsf = sfDims(2); % OFDM symbols per subframe
    LFrame = 10*Lsf; % OFDM symbols per frame
    numFullFrames = length(rxWaveform)/samplesPerFrame;
    
    rxDataFrame = zeros(sum(enb.PDSCH.TrBlkSizes(:)),numFullFrames);
    recFrames = zeros(numFullFrames,1);
    rxSymbols = []; txSymbols = [];
    
    % For each frame decode the MIB, PDSCH and DL-SCH
    for frame = 0:(numFullFrames-1)
        fprintf('\nPerforming DL-SCH Decode for frame %i of %i in burst:\n', ...
            frame+1,numFullFrames)
        
        % Extract subframe #0 from each frame of the received resource grid
        % and channel estimate.
        enb.NSubframe = 0;
        rxsf = rxGrid(:,frame*LFrame+(1:Lsf),:);
        hestsf = hest(:,frame*LFrame+(1:Lsf),:,:);
               
        % PBCH demodulation. Extract resource elements (REs)
        % corresponding to the PBCH from the received grid and channel
        % estimate grid for demodulation.
        enb.CellRefP = 4;
        pbchIndices = ltePBCHIndices(enb); 
        [pbchRx,pbchHest] = lteExtractResources(pbchIndices,rxsf,hestsf);
        [~,~,nfmod4,mib,CellRefP] = ltePBCHDecode(enb,pbchRx,pbchHest,nest);
        
        % If PBCH decoding successful CellRefP~=0 then update info
        if ~CellRefP
            fprintf('  No PBCH detected for frame.\n');
            continue;
        end
        enb.CellRefP = CellRefP; % From ltePBCHDecode
        
        % Decode the MIB to get current frame number
        enb = lteMIB(mib,enb);

        % Incorporate the nfmod4 value output from the function
        % ltePBCHDecode, as the NFrame value established from the MIB
        % is the system frame number modulo 4.
        enb.NFrame = enb.NFrame+nfmod4;
        fprintf('  Successful MIB Decode.\n')
        fprintf('  Frame number: %d.\n',enb.NFrame);
        
        % The eNodeB transmission bandwidth may be greater than the
        % captured bandwidth, so limit the bandwidth for processing
        enb.NDLRB = min(enbDefault.NDLRB,enb.NDLRB);
        
        % Store received frame number
        recFrames(frame+1) = enb.NFrame;
               
        % Process subframes within frame (ignoring subframe 5)
        for sf = 0:9
            if sf~=5 % Ignore subframe 5
                % Extract subframe
                enb.NSubframe = sf;
                rxsf = rxGrid(:,frame*LFrame+sf*Lsf+(1:Lsf),:);

                % Perform channel estimation with the correct number of CellRefP
                [hestsf,nestsf] = lteDLChannelEstimate(enb,cec,rxsf);

                % PCFICH demodulation. Extract REs corresponding to the PCFICH
                % from the received grid and channel estimate for demodulation.
                pcfichIndices = ltePCFICHIndices(enb);
                [pcfichRx,pcfichHest] = lteExtractResources(pcfichIndices,rxsf,hestsf);
                [cfiBits,recsym] = ltePCFICHDecode(enb,pcfichRx,pcfichHest,nestsf);

                % CFI decoding
                enb.CFI = lteCFIDecode(cfiBits);
                
                % Get PDSCH indices
                [pdschIndices,pdschIndicesInfo] = ltePDSCHIndices(enb, enb.PDSCH, enb.PDSCH.PRBSet); 
                [pdschRx, pdschHest] = lteExtractResources(pdschIndices, rxsf, hestsf);

                % Perform deprecoding, layer demapping, demodulation and
                % descrambling on the received data using the estimate of
                % the channel
                [rxEncodedBits, rxEncodedSymb] = ltePDSCHDecode(enb,enb.PDSCH,pdschRx,...
                                               pdschHest,nestsf);

                % Append decoded symbol to stream
                rxSymbols = [rxSymbols; rxEncodedSymb{:}]; %#ok<AGROW>

                % Transport block sizes
                outLen = enb.PDSCH.TrBlkSizes(enb.NSubframe+1);  

                % Decode DownLink Shared Channel (DL-SCH)
                [decbits{sf+1}, blkcrc(sf+1)] = lteDLSCHDecode(enb,enb.PDSCH,...
                                                outLen, rxEncodedBits);  %#ok<SAGROW>

                % Recode transmitted PDSCH symbols for EVM calculation                            
                %   Encode transmitted DLSCH 
                txRecode = lteDLSCH(enb,enb.PDSCH,pdschIndicesInfo.G,decbits{sf+1});
                %   Modulate transmitted PDSCH
                txRemod = ltePDSCH(enb, enb.PDSCH, txRecode);
                %   Decode transmitted PDSCH
                [~,refSymbols] = ltePDSCHDecode(enb, enb.PDSCH, txRemod);
                %   Add encoded symbol to stream
                txSymbols = [txSymbols; refSymbols{:}]; %#ok<AGROW>

                release(constellation); % Release previous constellation plot
                constellation(rxEncodedSymb{:}); % Plot current constellation
                pause(0); % Allow constellation to repaint
            end
        end
        
        % Reassemble decoded bits
        fprintf('  Retrieving decoded transport block data.\n');
        rxdata = [];
        for i = 1:length(decbits)
            if i~=6 % Ignore subframe 5
                rxdata = [rxdata; decbits{i}{:}]; %#ok<AGROW>
            end
        end
        
        % Store data from receive frame
        rxDataFrame(:,frame+1) = rxdata;

        % Plot channel estimate between CellRefP 0 and the receive antennae
        focalFrameIdx = frame*LFrame+(1:LFrame);
        figure(hhest);
        hhest.Visible = 'On';
        surf(abs(hest(:,focalFrameIdx,1,1)));
        shading flat;
        xlabel('OFDM symbol index'); 
        ylabel('Subcarrier index');
        zlabel('Magnitude');   
        title('Estimate of Channel Magnitude Frequency Repsonse');                
    end
    rxsim.numBurstCaptures = rxsim.numBurstCaptures-1;
end

最外层的循环为接收次数,每接收一次就会自减1,本次突发接收仅一次,因此一次接收之后就会跳出。前面的配置中计划进行 总帧数+1=6 次的接收,第二次循环就以6位循环次数指标,开始一次接收。接收时接收机会返回接收数据、接收长度以及丢失帧数,如果出现丢失帧数则会警告。循环接收6帧内容后,我们接收到的波形rxWaveform就是前面每一帧的组合。将此信号输入频谱分析模块,我们就可以得到一个频谱图(这里我就不知道它是采用什么算法得到的了)。
载波频偏很小,在此系统中是否修正都不会对系统产生严重的影响,但是这个问题在实际系统中是不能忽略的。由于前面设置帧数是从700开始的,因此后面捕获帧数的时候都会以700位第一帧的序号,可以自己改。在LTE中我们采用PSS和SSS进行同步检测,此处我们通过SSS检测得到采样点偏移,本次收发的偏移为125188采样点。因此我们将去除接收波形中的这部分,除此之外,我们还要在末尾去除多余部分。由于我们接收了6帧,这样就可以去除影响因素获取其中的5帧。
接收波形通过OFDM解调得到了rxGrid

接收波形的边缘子载波似乎经历了比较严重的fading,原因尚不知晓。利用接收的Grid进行信道估计,我们得到了信道hest

lteResourceGridSize可以根据基站的工作模式提取时频资源块的形状,我们这里输出为[600 14 4],我们取第二个维度14,这个维度代表的是每一个子帧内有14个符号。由于采样速率为15.36M,容易通过裁剪后的rxWaveform的长度得到总共接收5帧,而每一帧内的数据量为272944,因此可以配置接收帧矩阵为\(272944\times 5\)维度。

接下来进行每一帧的解码,循环解码MIB, PDSCH 和 DL-SCH。每一帧的第一个子帧中包含较多的信息,因此从第一子帧开始解码,前14个符号解出后,配合估计出的这14个符号的信道。后面通过LTE中我们指定的传输方案,可以直接提取出中间指定区域\(240\times 4\)的物理广播信道块,提取后就可以从这个块中解码我们需要的信息。
由于LTE进行传输时必须指定小区编号等信息,因此虽然这些信息在仿真中没有实际意义,但是解码的部分要到位。
PBCH占用的时频资源固定,在每个帧上的子帧0的第2个slot的0,1,2,3个符号上传输,占用1.25M频率资源,72个资源块(RE)。PBCH传输的就是MIB(只传输MIB),MIB在40ms内重复发送4次,接收机在这四次中成功解出其中的一个就可以实现MIB的解码,如果信道条件不佳,就会在10ms后与下一次的MIB进行软合并,再次尝试数据解调。
然后进行数据的解调,在10个子帧中,除了第六个子帧外(空帧),均进行适当的信道估计和资源块的解码,每一个子帧内信道可能发生改变,因此每一个子帧都进行信道的估计。估计后首先在每一个子帧中获取控制信息。前面我们看见,虽然一个子帧内传输14个slot,但是容易知道其中前面的若干slot并没有传输数据,而是传输一些控制信号与预留区域,这部分区域实际上就是物理格式控制信道。PCFICH承载的是CFI(Control Format Indicatior),用来指明PDCCH在子帧内所占用的符号个数。我们接下来就将CFI获取。有了CFI信息后,我们就可以实现最终的数据解码。

EVM是LTE中常用的性能指标,用来衡量传输中信号与理想信号在同一时刻的向量差,此处的M代表幅度,因此为误差矢量的幅度。为了衡量这个误差,需要将得到的正确结果重编码调制后与接收到的进行对比,这个数值可以一定程度上反应信噪比,误差越大信噪比也越低。LTE一般要求这个误差在17.5%。该性能指标可以通过工具箱直接绘制。

每一个子帧解码最后都可以绘制星座图。将所有比特进行合并之后,我们得到了总接收数据,将其重新排列为图像就可以得到接收的图像结果。

采用空时编码实现 2x2 MIMO系统

前面一部分的主要问题是代码冗长,需要对协议有比较深入的了解。我们的主要工作还是集中在所谓学术,对这些协议上的内容在研究中可以等效地忽略。在后面的实现中,我们简化系统为普通的OFDM系统,不再考虑LTE中的复杂协议要求,而是考虑较为理想的传输模型。首先我们进行参数配置与内存的预分配

clear
%%
NT      = 2;
N_sym   = 12;       %  6 symbols -> 12 symbols (STBC)
N_frame = 10;       %  a single frame
fc      = 2.45e9;
fs      = 15.36e6;
N_subcarrier    =   1024;
synSeqLength    =   1024;
Idx_used        =   N_subcarrier/2-300+1:N_subcarrier/2+300;
pilot_idx       =   Idx_used(1):16:Idx_used(end);
data_idx        =   setdiff(Idx_used,pilot_idx);
N_data          =   length(data_idx);
CPlength        =   [178 170 170 170 170 170 170 170 170 170 170 170];% frame 12
samplesPerFrame =   N_sym*N_subcarrier+synSeqLength+sum(CPlength);
frameTime       =   samplesPerFrame/fs;
bit_per_sym =   N_data; %bpsk
total_bit_cnt   =   bit_per_sym * N_sym*N_frame;
user_bit        =   rand (  total_bit_cnt ,1 ) > 0.5 ;
bit_to_mod      =   reshape(user_bit,1,total_bit_cnt);
sym             =   2*bit_to_mod-1;                               % BPSK mod
txGrid          =   zeros(N_subcarrier,(N_sym+1)*N_frame);
avaliableSlot   =   setdiff(1:N_frame*(1+N_sym),1:N_sym+1:N_frame*(1+N_sym));

然后进行bpsk数据的填充,以及波形的准备。

txGrid(data_idx,avaliableSlot)  =   reshape(sym,N_data,N_sym*N_frame);
txGrid(pilot_idx,avaliableSlot)  =   ones(numel(pilot_idx),numel(avaliableSlot));
txDataGrid      =   txGrid(:,avaliableSlot);
txWaveform=zeros(samplesPerFrame*N_frame,NT);
preAmble=zadoffChuSeq(1,1023);
pre_Amble=[preAmble;preAmble(1)];
txWaveform(1:1024,1)=[preAmble;preAmble(1)];
txWaveform(1:1024,2)=[preAmble;preAmble(1)];

上述的preamble的主要作用是进行数据帧的同步。然后我们在OFDM之前对数据进行空时编码,这里的简单编码就是Alamouti code。

%% STBC
% in STBC only half of the symbols are coded. A bug of the program.
STBCGrid=zeros(N_subcarrier,(N_sym+1)*N_frame,NT);
for f=1:N_frame
    % ofdm_frame = sqrt(N_subcarrier*N_subcarrier/numel(Idx_used)) * ifft( fftshift( txGrid(:,(f-1)*(N_sym+1)+2:f*(N_sym+1),1), 1 ) );%IFFT
    ofdm_frame = txGrid(:,(f-1)*(N_sym+1)+2:f*(N_sym+1),1);
    for s=1:N_sym/2
        input = ofdm_frame( :,s*2-1:s*2 );
        X1=input(:,1);
        X2=input(:,2);
        coded_tmp=[X1 X2;-conj(X2) conj(X1)];   % 2t Alamouti code
        for ant = 1:2
            tmp = reshape(coded_tmp(:,ant), N_subcarrier, 2);
            %st_coded(:, s*2-1:s*2 ,ant) = tmp;
            STBCGrid(:,(f-1)*(N_sym+1)+2+s*2-2:(f-1)*(N_sym+1)+2+s*2-1,ant)=tmp;
        end
    end
end

然后产生发射机波形,顺便配置一个频谱分析仪,一个星座图分析。

endPointer=1025;
for ant = 1:2
    endPointer=1025;
    for f=1:N_frame
        ofdm_frame = sqrt(N_subcarrier*N_subcarrier/numel(Idx_used)) * ifft( fftshift( STBCGrid(:,(f-1)*(N_sym+1)+2:f*(N_sym+1),ant), 1 ) );%IFFT
        for s=1:N_sym
            cpOFDM_frame=[ofdm_frame(end-CPlength(s)+1:end,s);ofdm_frame(:,s)];
            txWaveform(endPointer:endPointer+N_subcarrier+CPlength(s)-1,ant)=cpOFDM_frame;
            endPointer=endPointer+N_subcarrier+CPlength(s);
        end
        endPointer=endPointer+synSeqLength;
    end
end
%%
% Setup Spectrum viewer
spectrumScope = dsp.SpectrumAnalyzer( ...
    'SpectrumType',    'Power density', ...
    'SpectralAverages', 10, ...
    'YLimits',         [-130 -30], ...
    'Title',           'Received Baseband LTE Signal Spectrum', ...
    'YLabel',          'Power spectral density');

% Setup the constellation diagram viewer for equalized PDSCH symbols
constellation = comm.ConstellationDiagram('Title','Equalized PDSCH Symbols',...
                                'ShowReferenceConstellation',false);
spectrumScope.SampleRate = 15.36e6;

接下来进行一段经典的收发机配置

%% Tx
txsim = struct; % Create empty structure for transmitter
txsim.SDRDeviceName = 'AD936x'; % Set SDR Device
radio = sdrdev(txsim.SDRDeviceName); % Create SDR device object
txsim.DesiredCenterFrequency = fc; % Center frequency in Hz
txsim.NTxAnts = NT;
txsim.Gain = -3;
sdrTransmitter = sdrtx(txsim.SDRDeviceName);
sdrTransmitter.BasebandSampleRate = fs; % 15.36 Msps for default RMC (R.7) 
                                          % with a bandwidth of 10 MHz  
sdrTransmitter.CenterFrequency = txsim.DesiredCenterFrequency;
sdrTransmitter.ShowAdvancedProperties = true;
sdrTransmitter.Gain = txsim.Gain;
sdrTransmitter.ChannelMapping = [1,2];
powerScaleFactor = 0.8;
for i =1:NT
    txWaveform(:,i) = txWaveform(:,i).*(1/max(abs(txWaveform(:,i)))*powerScaleFactor);
end
spectrumScope.SampleRate = 15.36e6;
% spectrumScope(txWaveform);
txWaveform2=txWaveform;
txWaveform = int16(txWaveform*2^15);
transmitRepeat(sdrTransmitter,txWaveform);
pause(0.5)
%%
rxsim = struct;
rxsim.RadioFrontEndSampleRate = fs; % Configure for same sample rate
                                                       % as transmitter
rxsim.RadioCenterFrequency = fc;
rxsim.NRxAnts = 2;
rxsim.FramesPerBurst = 2; % Number of LTE frames to capture in each burst.
                                          % Capture 1 more LTE frame than transmitted to  
                                          % allow for timing offset wraparound...
rxsim.numBurstCaptures = 1; % Number of bursts to capture
samplesPerFrame = 10e-3*rxsim.RadioFrontEndSampleRate;
%%
rxsim.SDRDeviceName = txsim.SDRDeviceName;
sdrReceiver = sdrrx(rxsim.SDRDeviceName);
sdrReceiver.BasebandSampleRate = rxsim.RadioFrontEndSampleRate;
sdrReceiver.CenterFrequency = rxsim.RadioCenterFrequency;
sdrReceiver.SamplesPerFrame = samplesPerFrame;
sdrReceiver.OutputDataType = 'double';
sdrReceiver.EnableBurstMode = true;
sdrReceiver.NumFramesInBurst = rxsim.FramesPerBurst;
% Configure RX channel map
sdrReceiver.ChannelMapping = 1:rxsim.NRxAnts;
burstCaptures = zeros(samplesPerFrame,rxsim.NRxAnts,rxsim.FramesPerBurst);

接收机经过一次接收就会获取两个帧长度的采样,然后我们通过同步确定帧头部出现的位置,并以这个头部为起点切除周围不完整的一帧。

rxWaveform_frame=zeros(samplesPerFrame,2);
%  release(spectrumScope)
while rxsim.numBurstCaptures
    % SDR Capture
    fprintf('\nStarting a new RF capture.\n\n')
    len = 0;
    for frame = 1:rxsim.FramesPerBurst
        while len == 0
            % Store one LTE frame worth of samples
            [data,len,lostSamples] = sdrReceiver();
            burstCaptures(:,:,frame) = data;
        end
        if lostSamples && frame > 1
            warning(message('sdrpluginbase:zynqradioExamples:DroppedSamples'));
        end
        len = 0;
    end 
    
    rxWaveform = reshape(permute(burstCaptures,[1 3 2]), ...
        rxsim.FramesPerBurst*samplesPerFrame,rxsim.NRxAnts);
    spectrumScope.ShowLegend = true; % Turn on legend for spectrum analyzer
    spectrumScope.ChannelNames = cellfun(@(x) ['SDR Channel ' num2str(x)], num2cell(1:4), 'UniformOutput', false);
    
    % rxWaveform = burstCaptures(:);
    spectrumScope(rxWaveform);
    
    corr1=abs(xcorr(rxWaveform(:,1),pre_Amble));
    [val,corr1_index]=max(corr1(1:round(0.75*numel(corr1))))
    % corr1_index=find(corr1>50);
    offset_1=corr1_index-round(numel(corr1)/2)-2
    
    corr2=abs(xcorr(rxWaveform(:,2),pre_Amble));
    [val,corr2_index]=max(corr2(1:round(0.75*numel(corr2))))
    % corr1_index=find(corr1>50);
    offset_2=corr2_index-round(numel(corr2)/2)-2
    
    rxWaveform_frame(:,1)=rxWaveform(1+offset_1:samplesPerFrame+offset_1,1);
    rxWaveform_frame(:,2)=rxWaveform(1+offset_2:samplesPerFrame+offset_2,1);
    rxsim.numBurstCaptures = rxsim.numBurstCaptures-1;
end
release(sdrTransmitter);
release(sdrReceiver);
release(spectrumScope);

完成同步之后,我们将接收波形重新整形为Grid形式(即“时频资源块”的形式),然后就可以逐帧进行OFDM解调与STBC解码的过程。值得注意的是,由于采用双天线接收,因此得到的接收矩阵有两个维度,分别是两个天线的波形采样,这两个天线的采样可能存在些许不同。该射频板天线距离极近,此处的处理方案是直接将两个天线的采样叠加。经过同步我们容易发现两个天线的同步偏移是相同的,直接相加有利于等效信噪比增加,这也被称为是阵列增益。
合并方式很多,考虑到这两根天线的情况相似,则此处EGC和MRC为相似的处理,即直接1:1合并。

%% MRC combining
rxWaveform_fr=(rxWaveform_frame(:,1)+rxWaveform_frame(:,2))/2;
%% recv reshape
endPointer=1025;
rxGrid=zeros(N_subcarrier,N_sym*N_frame);
for f=1:N_frame
    for s=1:N_sym
        ofdm_frame = rxWaveform_fr(endPointer+CPlength(s):endPointer+N_subcarrier+CPlength(s)-1);
        % spectrumScope(ofdm_frame)
        % pause(0.05)
        % ofdm_frame_f=fftshift(fft(ofdm_frame));
        rxGrid(:,s+(f-1)*N_sym)=fftshift(fft(ofdm_frame))/(sqrt(N_subcarrier*N_subcarrier/numel(Idx_used)));
        endPointer=endPointer+N_subcarrier+CPlength(s);
    end
    endPointer=endPointer+synSeqLength;
end
%% channel estimation
rxPilotSeq=rxGrid(pilot_idx,:);
channel_f=rxPilotSeq./ones(numel(pilot_idx),numel(avaliableSlot));
channel_f_acc=zeros(numel(pilot_idx),1);
channel_t_acc=zeros(numel(pilot_idx),1);
figure(37);
for i=1:numel(avaliableSlot)
    if mod(i,2)==0
        continue;
    else
        % continue;
    end
    subplot(411)
    plot(abs(channel_f(:,i)));axis([1 numel(channel_f(:,i)) 0 0.5]);title('Instant freq-D channel')
    subplot(412)
    stem(abs(ifft(channel_f(:,i))));axis([1 numel(channel_f(:,i)) 0 0.5]);title('Instant time-D channel')
    % ==================================
    channel_f_acc=channel_f_acc+channel_f(:,i);
    channel_t_acc=channel_t_acc+ifft(channel_f(:,i));
    % ==================================
    subplot(413)
    plot(abs(channel_f_acc/i*2));axis([1 numel(channel_f(:,i)) 0 0.5]);title('Accumulate freq-D channel')
    subplot(414)
    stem(abs(channel_t_acc/i*2));axis([1 numel(channel_f(:,i)) 0 0.5]);title('Accumulate time-D channel')
    pause(0.001)
end
channel_f_acc=channel_f_acc/numel(avaliableSlot)*2;
channel_t_acc=channel_t_acc/numel(avaliableSlot)*2;
%% De STBC
st_decoded = zeros(N_subcarrier, N_frame*N_sym);
Xe=zeros(600,1);
Xo=zeros(600,1);
aveCh=channel_f_acc;
channel_f1=fft(ifft(aveCh),numel(Idx_used));
channel_f2=fft(ifft(aveCh),numel(Idx_used));

for f=1:N_frame
    for n = 1:N_sym/2
%         channel_f1=fft(ifft(channel_f(:,(f-1)*N_sym+n*2)),numel(Idx_used));
%         channel_f2=channel_f1;
        R = [];
        R = [R  rxGrid(Idx_used,(n-1)*2+1+(f-1)*N_sym:n*2+(f-1)*N_sym) ];
        R1=R(:,1);
        R2=R(:,2);
        for i=1:numel(Idx_used)
            Xe(i)= conj(channel_f1(i))*R1(i)+channel_f2(i)*conj(R2(i));
            Xo(i)= conj(channel_f2(i))*R1(i)-channel_f1(i)*conj(R2(i));% if SC-FDMA , need MMSE equalization here
        end
        output=[Xe Xo];
        st_decoded(Idx_used,(n-1)*2+1+(f-1)*N_sym:n*2+(f-1)*N_sym) = output;
        tmp=reshape(output,[numel(output),1]);
        constellation(output)
        pause(0.05)
        
    end
end
release(constellation)
%% Detection
for f=1:N_frame
    for n = 1:N_sym
        for ii=Idx_used
            if st_decoded(ii,(f-1)*N_sym+n)>=0
                st_decoded(ii,(f-1)*N_sym+n)=1;
            else
                st_decoded(ii,(f-1)*N_sym+n)=-1;
            end
        end
    end
end
%% BER
ber=biterr(txDataGrid(data_idx,:)+1,st_decoded(data_idx,:)+1)/numel(txDataGrid(data_idx,:))

经过OFDM解调,STBC解码之后,原数据得以解出,我们因此可以计算误码率、观测星座图等。

关于性能对比的分析

OFDM系统是依靠不同子载波传输来提高系统的传输带宽,从发射/接收频谱我们都容易看出,传输带宽可以轻易拉到10MHz水平,并且可以有效实现信道估计。这个应该可以说是频率上的分集(Diversity),提高了系统的有效性,我们同样可以在时间上进行分集,从而提高系统的可靠性。时间上的分集借助天线实现,这种编码方式也被称为是空时编码。在期望性能上,我们认为应该是\(2\times 2\) OFDM + STBC + MRC \(\geq\) \(2\times 1\) OFDM + STBC \(\geq\) \(1\times 1\) OFDM,首先我们在AWGN信道条件下验证性能。

AWGN信道仿真分析

理论到实践的距离很大,我们首先来测试理论上的误码性能。在此次假设中我们认为接收机完美同步,信噪比从-10dB到10dB每隔2.5dB取值一次。在AWGN信道下,OFDM传输50帧,最终计算误码率曲线为

根据曲线图我们可以看出,在AWGN信道下进行STBC编码对最终性能不会产生任何影响,此时的收发机分集并不会引发增益。这是由于AWGN信道的抽头系数为1,此时进行误码率上下界推导时,计算结果为BER随SNR指数下降,无论外界的分集系数如何,最终结果不会产生影响。但是当我们采用瑞利信道建模时,根据瑞利信道的统计特性我们可以推算瑞利信道下的BER随着分集阶数增加,BER曲线的斜率是发生明显变化的。因此在AWGN信道的测试中,我们无法观测到分集增益。实际仿真我们证实了这个结论,接下来我们可以测试瑞利信道下的通信过程。

由于后面的仿真结果受到了天线互耦的影响,因此经过调试后决定延期进行,结果将会在调试结束后发布
2020-7-15 14:07:13


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM