global IMAGE_FILE_TO_SEND_FROM_GUI;
% 定义通信信道类型:"OverTheAir" 表示通过SDR硬件进行空中传输。
channel = "OverTheAir";
% 根据信道类型设置不同的参数
if strcmpi(channel,"OverTheAir") % 检查信道是否为 "OverTheAir"
deviceName = "Pluto"; % 设置SDR设备名称 (例如 ADALM-PLUTO)
channelNumber = 10; % 设置WLAN信道号 (例如 5)
frequencyBand = 2.4; % 设置工作频段 (例如 2.4 GHz)
txGain = 0; % 设置SDR发射增益 (dB)
rxGain = 14; % 设置SDR接收增益 (dB)
elseif strcmpi(channel,"GaussianNoise") % 检查信道是否为 "GaussianNoise"
% 为仿真信道指定接收信号的信噪比 (SNR)
SNR = 20; % 设置信噪比 (dB)
end
% 设置图像绘图句柄
if ~exist('imFig','var') || ~ishandle(imFig) %#ok<SUSENS> % 检查图像窗口变量 'imFig' 是否存在或是否为有效句柄
imFig = figure; % 创建一个新的图形窗口
imFig.NumberTitle = 'off'; % 关闭窗口标题中的序号 (Figure 1)
imFig.Name = 'Image Plot'; % 设置窗口标题
imFig.Visible = 'off'; % 初始状态下隐藏窗口
else
clf(imFig); % 如果窗口已存在,则清除窗口内容
imFig.Visible = 'off'; % 隐藏窗口
end
% 设置频谱分析仪
spectrumScope = spectrumAnalyzer( ...
SpectrumType='power-density', ... % 设置频谱类型为功率谱密度
Title='Received Baseband WLAN Signal Spectrum', ... % 设置频谱仪标题
YLabel='Power spectral density', ... % 设置Y轴标签
Position=[69 376 800 450]); % 设置频谱仪窗口位置和大小 [left bottom width height]
% 设置用于显示均衡后WLAN符号的星座图查看器
refQAM = wlanReferenceSymbols('64QAM'); % 获取 64-QAM 的参考星座点
constellation = comm.ConstellationDiagram(...
Title='Equalized WLAN Symbols',... % 设置星座图标题
ShowReferenceConstellation=true,... % 显示参考星座点
ReferenceConstellation=refQAM,... % 设置参考星座点
Position=[878 376 460 460]); % 设置星座图窗口位置和大小
% --- 获取图像文件名 ---
if ~isempty(IMAGE_FILE_TO_SEND_FROM_GUI) && exist(IMAGE_FILE_TO_SEND_FROM_GUI, 'file')
fileTx = IMAGE_FILE_TO_SEND_FROM_GUI;
fprintf('SDR Script: 接收到来自GUI的文件路径: %s\n', fileTx);
else
fprintf('SDR Script: 未从GUI接收到有效文件路径,使用默认 peppers.png\n');
fileTx = 'peppers.png'; % 如果全局变量无效,则使用默认值
if ~exist(fileTx,'file')
fprintf('错误: 默认图片 peppers.png 也不存在,请检查路径。\n');
return;
end
end
clear global IMAGE_FILE_TO_SEND_FROM_GUI; % 清除全局变量,避免影响下次运行
% 输入图像文件并转换为二进制流
%fileTx = './1_compressed_jpeg_q75.jpg'; % 图像文件名
fData = imread(fileTx); % 从文件读取图像数据
scale = 0.2; % 图像缩放因子
origSize = size(fData); % 获取原始输入图像尺寸
scaledSize = max(floor(scale.*origSize(1:2)),1); % 计算新的图像尺寸 (至少为1x1)
% 计算缩放后像素对应的原始图像像素索引 (最近邻插值)
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,:); % 根据计算的索引调整图像大小
imsize = size(fData); % 存储新的图像尺寸
txImage = fData(:); % 将调整大小后的图像数据展平成列向量
% 绘制发射图像
imFig.Visible = 'on'; % 使图像窗口可见
subplot(211); % 选择第一个子图 (2行1列中的第1个)
imshow(fData); % 显示调整大小后的发射图像
title('Transmitted Image'); % 设置子图标题: "发射图像"
subplot(212); % 选择第二个子图 (2行1列中的第2个)
title('Received image appears here...'); % 设置子图标题: "接收图像将显示在此处..."
set(gca,'Visible','off'); % 隐藏坐标轴
set(findall(gca, 'type', 'text'), 'visible', 'on'); % 确保标题文本可见
% 准备数据进行传输
msduLength = 2304; % 设置MSDU (MAC Service Data Unit) 长度 (字节)
numMSDUs = ceil(length(txImage)/msduLength); % 计算需要的MSDU数量 (向上取整)
padZeros = msduLength-mod(length(txImage),msduLength); % 计算最后一个MSDU需要填充的零的数量
txData = [txImage;zeros(padZeros,1)]; % 将图像数据填充零,使其长度为 msduLength 的整数倍
txDataBits = double(int2bit(txData,8,false)); % 将填充后的图像数据 (字节) 转换为双精度比特流 (LSB first)
% 将输入数据流分割成片段,并封装成MPDU (MAC Protocol Data Unit)
bitsPerOctet = 8; % 定义每个字节的比特数
data = zeros(0,1); % 初始化用于存储所有MPDU比特的变量
for i=0:numMSDUs-1 % 遍历每个MSDU
% 提取当前MPDU的图像数据 (字节)
frameBody = txData(i*msduLength+1:msduLength*(i+1),:);
% 创建MAC帧配置对象,并配置帧类型为数据帧和序列号
cfgMAC = wlanMACFrameConfig(FrameType='Data',SequenceNumber=i);
% 生成MPDU (物理层服务数据单元 PSDU)
[psdu, lengthMPDU]= wlanMACFrame(frameBody,cfgMAC,OutputFormat='bits');
% 将生成的PSDU比特连接起来,用于后续波形生成
data = [data; psdu]; %#ok<AGROW> % 允许数组在循环中增长
end
% 配置WLAN Non-HT (非高吞吐量) 物理层参数
nonHTcfg = wlanNonHTConfig; % 创建Non-HT配置对象
nonHTcfg.MCS = 6; % 设置调制与编码方案 (MCS): 6 (64QAM, 速率 2/3)
nonHTcfg.NumTransmitAntennas = 1; % 设置发射天线数量
chanBW = nonHTcfg.ChannelBandwidth; % 获取信道带宽 (例如 'CBW20')
nonHTcfg.PSDULength = lengthMPDU; % 设置PSDU长度 (注意:这里使用了最后一个MPDU的长度,假设所有MPDU长度相同)
scramblerInitialization = randi([1 127],numMSDUs,1); % 为每个数据包生成随机的加扰器初始值 (1到127之间)
osf = 1.5; % 设置过采样因子
sampleRate = wlanSampleRate(nonHTcfg); % 获取标称采样率 (Hz)
% 生成基带Non-HT数据包,数据包之间有空闲时间
txWaveform = wlanWaveformGenerator(data,nonHTcfg, ... % 使用数据比特和配置生成波形
NumPackets=numMSDUs, ... % 指定要生成的数据包数量
IdleTime=20e-6, ... % 设置数据包之间的空闲时间 (20 微秒)
ScramblerInitialization=scramblerInitialization,... % 指定每个包的加扰器初始值
OversamplingFactor=osf); % 应用过采样因子
% 如果是通过空中传输
if strcmpi(channel,"OverTheAir")
% 发射器属性配置
sdrTransmitter = sdrtx(deviceName); % 创建SDR发射器对象
sdrTransmitter.BasebandSampleRate = sampleRate*osf; % 设置基带采样率 (包括过采样)
sdrTransmitter.CenterFrequency = wlanChannelFrequency(channelNumber,frequencyBand); % 设置中心频率
sdrTransmitter.Gain = txGain; % 设置发射增益
% 对于 USRP Embedded Series Radio,直接将SDR I/O传递给主机,跳过FPGA
if ~strcmpi(deviceName,"Pluto") % 如果设备不是Pluto
sdrTransmitter.ShowAdvancedProperties = true; % 显示高级属性
sdrTransmitter.BypassUserLogic = true; % 启用旁路用户逻辑
end
fprintf('\nGenerating WLAN transmit waveform:\n'); % 打印提示信息:正在生成WLAN发射波形
% 对归一化信号进行缩放,以避免RF前端饱和
powerScaleFactor = 0.8; % 功率缩放因子
txWaveform = txWaveform.*(1/max(abs(txWaveform))*powerScaleFactor); % 缩放波形,使其最大绝对值不超过 powerScaleFactor
% 发射RF波形 (重复发射,直到停止)
transmitRepeat(sdrTransmitter,txWaveform);
end
% 接收处理
if strcmpi(channel,"OverTheAir") % 如果是空中传输信道
sdrReceiver = sdrrx(deviceName); % 创建SDR接收器对象
sdrReceiver.BasebandSampleRate = sdrTransmitter.BasebandSampleRate; % 设置与发射器相同的基带采样率
sdrReceiver.CenterFrequency = sdrTransmitter.CenterFrequency; % 设置与发射器相同的中心频率
sdrReceiver.OutputDataType = 'double'; % 设置输出数据类型为双精度
sdrReceiver.GainSource = 'Manual'; % 设置增益控制源为手动
sdrReceiver.Gain = rxGain; % 设置接收增益
% 对于 USRP Embedded Series Radio 的配置
if ~strcmpi(deviceName,"Pluto") % 如果设备不是Pluto
sdrReceiver.ShowAdvancedProperties = true; % 显示高级属性
sdrReceiver.BypassUserLogic = true; % 启用旁路用户逻辑
end
% 配置捕获长度为发射信号长度的两倍,以确保按顺序接收PSDU。
% 接收时会移除重复的MAC片段。
sdrReceiver.SamplesPerFrame = 2*length(txWaveform); % 设置每帧捕获的样本数
fprintf('\nStarting a new RF capture.\n'); % 打印提示信息:开始新的RF捕获
rxWaveform = capture(sdrReceiver,sdrReceiver.SamplesPerFrame,'Samples'); % 捕获指定数量的样本
elseif strcmpi(channel,"GaussianNoise") % 如果是高斯噪声信道
% 在发射波形上添加高斯白噪声 (AWGN)
rxWaveform = awgn(txWaveform,SNR,'measured'); % 根据指定的SNR添加噪声,'measured'表示先测量信号功率
else % 无损伤信道 (理想信道)
rxWaveform = txWaveform; % 接收波形等于发射波形
end
% 显示接收信号的频谱
spectrumScope.SampleRate = sampleRate*osf; % 设置频谱仪的采样率 (包含过采样)
spectrumScope(rxWaveform); % 将接收波形送入频谱仪进行显示
release(spectrumScope); % 释放频谱仪对象,以便后续可以修改属性或重新使用
% 设计并应用一个FIR滤波器来移除过采样 (降采样)
aStop = 40; % 滤波器阻带衰减 (dB)
ofdmInfo = wlanNonHTOFDMInfo('NonHT-Data',nonHTcfg); % 获取Non-HT数据部分的OFDM参数
SCS = sampleRate/ofdmInfo.FFTLength; % 计算子载波间隔 (Hz)
txbw = max(abs(ofdmInfo.ActiveFrequencyIndices))*2*SCS; % 计算占用带宽 (Hz)
[L,M] = rat(1/osf); % 计算速率转换的整数因子 L/M (L=2, M=3 for osf=1.5)
maxLM = max([L M]); % L和M中的最大值
R = (sampleRate-txbw)/sampleRate; % 计算归一化半带宽
TW = 2*R/maxLM; % 计算滤波器的归一化过渡带宽
b = designMultirateFIR(L,M,TW,aStop); % 设计多速率FIR滤波器系数
firrc = dsp.FIRRateConverter(L,M,b); % 创建FIR速率转换器对象
rxWaveform = firrc(rxWaveform); % 应用滤波器进行降采样,得到标称采样率的波形
% 初始化接收处理循环变量
displayFlag = false; % 是否显示详细解码信息的标志
rxWaveformLen = size(rxWaveform,1); % 获取降采样后接收波形的长度
searchOffset = 0; % 在波形中搜索数据包的起始偏移量 (样本)
ind = wlanFieldIndices(nonHTcfg); % 获取Non-HT PPDU各字段的索引
Ns = ind.LSIG(2)-ind.LSIG(1)+1; % 一个OFDM符号的样本数 (包括循环前缀)
% 最小数据包长度定义为10个OFDM符号 (L-STF算2个,L-LTF算2个,L-SIG算1个,至少再加5个数据符号)
lstfLen = double(ind.LSTF(2)); % L-STF字段的长度 (样本数)
minPktLen = lstfLen*5; % 定义检测所需的最小数据包长度 (约为10个OFDM符号)
pktInd = 1; % 初始化检测到的数据包索引
fineTimingOffset = []; % 初始化精细定时偏移
packetSeq = []; % 初始化存储接收到的数据包序列号的数组
rxBit = {}; % 初始化存储接收到的数据比特的单元数组
% EVM (误差向量幅度) 计算器设置
evmCalculator = comm.EVM(AveragingDimensions=[1 2 3]); % 创建EVM计算器,对所有维度求平均
evmCalculator.MaximumEVMOutputPort = true; % 使能输出峰值EVM
% 循环处理接收到的波形,搜索并解码WLAN数据包
while (searchOffset+minPktLen)<=rxWaveformLen % 当剩余波形长度足够容纳一个最小数据包时继续搜索
% 1. 包检测:在当前搜索偏移之后查找WLAN包的起始位置
pktOffset = wlanPacketDetect(rxWaveform,chanBW,searchOffset,0.5); % 使用L-STF相关性检测包,阈值0.5
% 调整包偏移量,使其成为相对于整个接收波形起始点的绝对偏移
pktOffset = searchOffset+pktOffset;
% 如果未检测到包,或者检测到的包起始点太靠后以至于无法提取L-SIG字段
if isempty(pktOffset) || (pktOffset+double(ind.LSIG(2))>rxWaveformLen)
if pktInd==1 % 如果是第一次尝试且未检测到包
disp('** No packet detected **'); % 显示未检测到包的消息
end
break; % 退出循环
end
% 2. 粗略频率偏移估计与校正
% 提取 Non-HT 字段 (L-STF, L-LTF, L-SIG) 用于粗略频偏估计和符号定时
nonHT = rxWaveform(pktOffset+(ind.LSTF(1):ind.LSIG(2)),:); % 提取 L-STF 到 L-SIG 结束的部分
coarseFreqOffset = wlanCoarseCFOEstimate(nonHT,chanBW); % 使用L-STF估计粗略载波频率偏移 (CFO)
nonHT = frequencyOffset(nonHT,sampleRate,-coarseFreqOffset); % 对提取的部分进行粗略频偏校正
% 3. 符号定时估计
fineTimingOffset = wlanSymbolTimingEstimate(nonHT,chanBW); % 使用L-LTF估计符号定时偏移
% 根据符号定时偏移调整数据包的精确起始位置
pktOffset = pktOffset+fineTimingOffset;
% 检查调整后的包起始位置是否有效 (不能为负数,且包结束不能超出接收波形)
if (pktOffset<0) || ((pktOffset+minPktLen)>rxWaveformLen)
searchOffset = pktOffset+1.5*lstfLen; % 更新搜索偏移量,跳过当前检测区域
continue; % 继续下一次循环迭代
end
fprintf('\nPacket-%d detected at index %d\n',pktInd,pktOffset+1); % 打印检测到的包序号和起始索引
% 4. 精细频率偏移估计与校正
% 提取前7个OFDM符号的数据 (L-STF, L-LTF, L-SIG, 和后面几个符号),用于格式检测和L-SIG解码
nonHT = rxWaveform(pktOffset+(1:7*Ns),:);
nonHT = frequencyOffset(nonHT,sampleRate,-coarseFreqOffset); % 先应用粗略频偏校正
% 提取L-LTF字段
lltf = nonHT(ind.LLTF(1):ind.LLTF(2),:);
fineFreqOffset = wlanFineCFOEstimate(lltf,chanBW); % 使用L-LTF估计精细频率偏移
nonHT = frequencyOffset(nonHT,sampleRate,-fineFreqOffset); % 对提取的 nonHT 部分应用精细频偏校正
cfoCorrection = coarseFreqOffset+fineFreqOffset; % 计算总的频率偏移
% 5. 信道估计
lltf = nonHT(ind.LLTF(1):ind.LLTF(2),:); % 重新提取经过精细频偏校正的L-LTF
demodLLTF = wlanLLTFDemodulate(lltf,chanBW); % 解调L-LTF得到频域符号
chanEstLLTF = wlanLLTFChannelEstimate(demodLLTF,chanBW); % 使用解调后的L-LTF符号进行信道估计
% 6. 噪声估计
noiseVarNonHT = wlanLLTFNoiseEstimate(demodLLTF); % 使用解调后的L-LTF符号估计噪声方差
% 7. 格式检测
% 使用L-LTF之后的3个OFDM符号进行包格式检测
format = wlanFormatDetect(nonHT(ind.LLTF(2)+(1:3*Ns),:), ...
chanEstLLTF,noiseVarNonHT,chanBW);
disp([' ' format ' format detected']); % 显示检测到的格式
if ~strcmp(format,'Non-HT') % 如果检测到的不是 Non-HT 格式
fprintf(' A format other than Non-HT has been detected\n'); % 打印消息
searchOffset = pktOffset+1.5*lstfLen; % 更新搜索偏移量
continue; % 继续下一次循环
end
% 8. L-SIG 恢复与解码
% 提取 L-SIG 字段并恢复其比特
[recLSIGBits,failCheck] = wlanLSIGRecover( ...
nonHT(ind.LSIG(1):ind.LSIG(2),:), ... % 提取L-SIG时域波形
chanEstLLTF,noiseVarNonHT,chanBW); % 使用信道估计和噪声估计进行恢复
if failCheck % 检查L-SIG校验是否失败 (例如奇偶校验错误)
fprintf(' L-SIG check fail \n'); % 打印L-SIG校验失败
searchOffset = pktOffset+1.5*lstfLen; % 更新搜索偏移量
continue; % 继续下一次循环
else
fprintf(' L-SIG check pass \n'); % 打印L-SIG校验成功
end
% 9. L-SIG 信息解析
% 根据解码的L-SIG比特获取包参数
[lsigMCS,lsigLen,rxSamples] = helperInterpretLSIG(recLSIGBits,sampleRate); % 调用辅助函数解析MCS, 长度(字节), 包总样本数
if (rxSamples+pktOffset)>length(rxWaveform) % 检查是否有足够的样本来解码整个包
disp('** Not enough samples to decode packet **'); % 打印样本不足的消息
break; % 退出循环
end
% 10. 对整个数据包应用总频率偏移校正
rxWaveform(pktOffset+(1:rxSamples),:) = frequencyOffset(...
rxWaveform(pktOffset+(1:rxSamples),:),sampleRate,-cfoCorrection);
% 11. 创建接收 Non-HT 配置对象
rxNonHTcfg = wlanNonHTConfig;
rxNonHTcfg.MCS = lsigMCS; % 设置从L-SIG解码得到的MCS
rxNonHTcfg.PSDULength = lsigLen; % 设置从L-SIG解码得到的PSDU长度
% 获取PPDU内数据字段的索引
indNonHTData = wlanFieldIndices(rxNonHTcfg,'NonHT-Data');
% 12. Non-HT 数据恢复
% 提取数据字段波形,并使用L-LTF的信道估计进行均衡和解码
[rxPSDU,eqSym] = wlanNonHTDataRecover(rxWaveform(pktOffset+...
(indNonHTData(1):indNonHTData(2)),:), ... % 提取数据字段时域波形
chanEstLLTF,noiseVarNonHT,rxNonHTcfg); % 使用信道估计、噪声估计和接收配置恢复PSDU比特和均衡后的符号
% 13. 星座图显示
constellation(reshape(eqSym,[],1)); % 显示当前数据包的均衡后符号星座图
release(constellation); % 释放星座图对象
% 14. EVM 计算
refSym = wlanClosestReferenceSymbol(eqSym,rxNonHTcfg); % 找到每个均衡符号最近的理想参考星座点
[evm.RMS,evm.Peak] = evmCalculator(refSym,eqSym); % 计算 RMS EVM 和 Peak EVM
% 15. MPDU 解码和MSDU提取
% 尝试解码恢复的PSDU比特流,提取MAC头信息和MSDU (数据载荷)
[cfgMACRx,msduList{pktInd},status] = wlanMPDUDecode(rxPSDU,rxNonHTcfg); %#ok<*SAGROW> % 允许单元数组在循环中增长
if strcmp(status,'Success') % 如果解码成功 (包括FCS校验通过)
disp(' MAC FCS check pass'); % 打印MAC FCS校验通过
% 存储序列控制信息 (主要是序列号)
packetSeq(pktInd) = cfgMACRx.SequenceNumber; % 存储该包的序列号
% 将十六进制字符串格式的MSDU转换为二进制数据流
rxBit{pktInd} = int2bit(hex2dec(cell2mat(msduList{pktInd})),8,false); % 转换为比特流并存储
else % 如果解码失败
if strcmp(status,'FCSFailed') % 如果是FCS校验失败
disp(' MAC FCS check fail'); % 打印MAC FCS校验失败
else % 其他解码失败情况 (理论上FCS可能通过但仍有错)
disp(' MAC FCS check pass'); % 假设FCS通过了 (或不关心FCS)
end
% 由于本示例中没有建模重传,即使FCS校验失败,也尝试提取图像数据(MSDU)和序列号。
% 移除MAC头和FCS,提取MSDU。
macHeaderBitsLength = 24*bitsPerOctet; % MAC头部长度 (比特)
fcsBitsLength = 4*bitsPerOctet; % FCS长度 (比特)
msduList{pktInd} = rxPSDU(macHeaderBitsLength+1:end-fcsBitsLength); % 提取MSDU比特 (可能包含错误)
% 提取并存储序列号
sequenceNumStartIndex = 23*bitsPerOctet+1; % 序列控制字段的起始比特索引 (假设为标准数据帧)
sequenceNumEndIndex = 25*bitsPerOctet-4; % 序列控制字段的结束比特索引 (前12比特是序列号)
conversionLength = sequenceNumEndIndex-sequenceNumStartIndex+1; % 序列号字段长度
packetSeq(pktInd) = bit2int(rxPSDU(sequenceNumStartIndex:sequenceNumEndIndex),conversionLength,false); % 从比特恢复序列号
% 将提取的 (可能有误的) MSDU 比特流存储起来
rxBit{pktInd} = double(msduList{pktInd});
end
% 16. 显示解码信息 (如果 displayFlag 为 true)
if displayFlag
fprintf(' Estimated CFO: %5.1f Hz\n\n',cfoCorrection); %#ok<*UNRCH> % 显示估计的总CFO
disp(' Decoded L-SIG contents: '); % 显示解码的L-SIG内容
fprintf(' MCS: %d\n',lsigMCS); % 显示MCS
fprintf(' Length: %d\n',lsigLen); % 显示长度
fprintf(' Number of samples in packet: %d\n\n',rxSamples); % 显示包样本数
fprintf(' EVM:\n'); % 显示EVM信息
fprintf(' EVM peak: %0.3f%% EVM RMS: %0.3f%%\n\n', ...
evm.Peak,evm.RMS); % 显示峰值和RMS EVM
fprintf(' Decoded MAC Sequence Control field contents:\n'); % 显示解码的MAC序列控制字段内容
fprintf(' Sequence number: %d\n\n',packetSeq(pktInd)); % 显示序列号
end
% 17. 更新搜索偏移量,准备搜索下一个包
searchOffset = pktOffset+double(indNonHTData(2)); % 将搜索偏移设置为当前包数据字段的末尾之后
% 18. 检查是否检测到重复的数据包
% 如果检测到重复包 (序列号出现重复),则完成处理。
% 恢复的数据将包含重复帧的比特。
% 移除重复帧的数据比特。
if length(unique(packetSeq)) < length(packetSeq) % 如果唯一序列号的数量小于已记录的序列号总数
rxBit = rxBit(1:length(unique(packetSeq))); % 保留不重复部分的数据比特
packetSeq = packetSeq(1:length(unique(packetSeq))); % 保留不重复部分的序列号
break % 退出while循环
end
pktInd = pktInd+1; % 增加已处理的数据包计数器
end % 结束 while 循环 (数据包搜索和解码)
% 释放SDR硬件资源 (如果使用了空中传输)
if strcmpi(channel,"OverTheAir")
release(sdrTransmitter); % 释放发射器对象
release(sdrReceiver); % 释放接收器对象
end
% 后处理:图像重建和性能评估
if ~(isempty(fineTimingOffset) || isempty(pktOffset)) % 确保至少成功检测并处理了一个包
% 将解码后的比特从单元数组转换为单列向量
rxData = cat(1,rxBit{:}); % 垂直连接所有包的比特数据
% 移除可能多余的比特 (对齐到 msduLength*8 的倍数)
% 这步可能不必要,取决于前面填充和提取的逻辑是否精确
% rxData = rxData(1:end-(mod(length(rxData),msduLength*8)));
% 重新整形比特数据,使得每列包含一个MSDU的比特 (msduLength*8 比特)
numBitsPerMSDU = msduLength * bitsPerOctet;
numRxMSDUs = floor(length(rxData) / numBitsPerMSDU); % 计算完整接收的MSDU数量
rxData = rxData(1 : numRxMSDUs * numBitsPerMSDU); % 截断末尾不足一个MSDU的比特
rxData = reshape(rxData, numBitsPerMSDU, numRxMSDUs);% 重塑为矩阵,每列是一个MSDU
% 如果存在重复包,则移除它们。重复包位于 rxData 的末尾。
% (注意:之前的逻辑应该在while循环中已经移除了重复包,这里可能是双重保险或处理特殊情况)
if length(packetSeq)>numMSDUs
numDupPackets = size(rxData,2)-numMSDUs; % 计算多余的包数量
rxData = rxData(:,1:end-numDupPackets); % 只保留预期的 numMSDUs 个包的数据
end
% 初始化变量用于寻找起始序列号
startSeqIdx = []; % 起始序列号在 packetSeq 数组中的索引
startSeqNum = -1; % 起始序列号的值
% 只有当至少一个数据包的序列号被正确解码时才执行重排序
if any(packetSeq<numMSDUs) % 检查是否存在有效的序列号 (小于总MSDU数)
% 寻找接收到的最小序列号及其索引
[minSeq, minIdx] = min(packetSeq);
if minSeq < numMSDUs % 确保找到的最小序号是有效的
startSeqIdx = minIdx;
startSeqNum = minSeq;
end
% 如果找到了有效的起始包
if ~isempty(startSeqIdx)
% 对接收到的数据进行循环移位,以便按正确顺序重建图像。
% 假设起始包之后的所有包都按顺序接收 (与发射顺序一致)。
shiftAmount = -(startSeqIdx - 1); % 计算需要左移的位数 (使起始包成为第一列)
rxData = circshift(rxData,[0, shiftAmount]); % 对列进行循环移位
fprintf('\nReordered received packets starting from sequence number %d.\n', startSeqNum);
else
fprintf('\nCould not find a valid starting sequence number. Image might be incorrectly ordered.\n');
end
% Reshape rxData matrix back into a single column vector for BER calculation
rxDataColVector = rxData(:); % 将重排后的矩阵 rxData 转换回列向量
% Perform bit error rate (BER) calculation on reordered data
bitErrorRate = comm.ErrorRate; % 创建误比特率计算对象
% Ensure we compare the same number of bits from Tx and Rx vectors
numBitsToCompare = min(length(rxDataColVector), length(txDataBits)); % 确定要比较的比特数
fprintf('Comparing %d bits for BER calculation.\n', numBitsToCompare); % 添加调试信息
% Compare the column vectors
err = bitErrorRate(double(rxDataColVector(1:numBitsToCompare)), ... % 接收比特列向量
txDataBits(1:numBitsToCompare)); % 原始发射比特列向量
fprintf(' \nBit Error Rate (BER):\n');
fprintf(' Bit Error Rate (BER) = %0.5f\n',err(1));
fprintf(' Number of bit errors = %d\n',err(2));
fprintf(' Number of compared bits = %d\n\n',err(3)); % err(3) is the number of bits compared
else
fprintf('\nNo valid packet sequence numbers decoded. Cannot reorder packets or calculate BER reliably.\n');
end
% 将重排 (或未重排) 后的比特转换回字节 (整数)
decData = bit2int(reshape(rxData(:),bitsPerOctet,[]),bitsPerOctet,false)'; % 每8比特转为一个字节
% 如果接收到的数据少于原始图像数据,则用 NaN 填充缺失部分
if length(decData)<length(txImage)
numMissingData = length(txImage)-length(decData); % 计算缺失的数据量
decData = [decData;NaN(numMissingData,1)]; % 用NaN填充
else
% 如果接收数据过多 (理论上不应发生,除非原始图像很小或有错误),则截断
decData = decData(1:length(txImage));
end
% 从接收到的数据重建图像
fprintf('\nConstructing image from received data.\n'); % 打印信息:正在从接收数据构建图像
% 将数据向量重新塑造成原始 (缩放后) 图像的尺寸,并转换为uint8类型
receivedImage = uint8(reshape(decData,imsize));
% 绘制接收到的图像
if exist('imFig','var') && ishandle(imFig) % 如果发射图像窗口存在且有效
figure(imFig); subplot(212); % 切换到该窗口的第二个子图
else
figure; subplot(212); % 否则,创建新窗口并在第二个子图绘制 (如果只有一个子图,则会创建)
end
imshow(receivedImage); % 显示重建的图像
title(sprintf('Received Image')); % 设置标题: "接收图像"
end % 结束图像重建和后处理
最新发布