commit 02f55955d5433064bdb06abeb2cd6dabdc137419 Author: NightPoem <1057539971@qq.com> Date: Sat Mar 30 16:35:40 2024 +0800 鏂板缓 diff --git a/Band_Mono_Parameter.m b/Band_Mono_Parameter.m new file mode 100644 index 0000000..cca6224 --- /dev/null +++ b/Band_Mono_Parameter.m @@ -0,0 +1,72 @@ +%%************************【频带干扰输出参数函数】****************************%% +%%=============================参数说明===================================%% +% J 检测出来的干扰点集合 +% Power_mono 多音干扰功率集合 +% f_mono 频带干扰频率集合 +% Power_band 频带干扰功率 +% f_wide 频带干扰带宽 +% f_start频带起始频率 +% f_end频带截止频率 +% Num_band 频带干扰个数 +% data 频域数据 +% fs 采样频率 +%%========================================================================%% +function [f_mono,Power_mono,Num_band,f_start,f_end,f_wide,Power_band] = Band_Mono_Parameter(data,J,fs,NFFT) + J = sort(J,'ascend')-1; + len_J = length(J); + f_start = []; + f_end = []; + c1=1; + mono_d = cell(0,0); + mono_s = cell(0,0); + mono = []; + f_d = []; + f_s = []; + power_m = []; + power_d = []; + power_s = []; + band = cell(0,0); + while c1 < len_J + c2 = 0; + while (c1 + c2 + 1 <= len_J && J(c1) + c2 + 1==J(c1 + c2 + 1)) + c2 = c2 + 1; + end + if(c2 > 2) + band = [band;(J(c1:1:c1+c2))]; %%连续点超过3,记为频带干扰 + end + if c2 == 2 + mono_d = [mono_d;(J(c1:1:c1+c2))]; %%频谱扩散的单音 + end + if c2 == 1 + mono_s = [mono_s;(J(c1:1:c1+c2))]; + end + if c2 == 0 + mono = [mono,J(c1)]; + end + c1 = c1 + c2 +1; + if c1 == len_J && J(len_J-1) + 1 ~= J(len_J) + mono = [mono,J(len_J)]; + end + end + for k = 1:numel(mono_d) + f_d(k) = mono_d{k}(2)*fs/NFFT/10^6; + power_d(k) = sum(abs(data(mono_d{k}+1)).^2)/NFFT^2; + end + for m = 1:numel(mono_s) + f_s(m) = 0.5*(mono_s{m}(1)+mono_s{m}(2))*fs/NFFT/10^6; + power_s(m) = sum(abs(data(mono_s{m}+1)).^2)/NFFT^2; + end + power_m = abs(data(mono+1)).^2/NFFT^2; + mono = mono*fs/NFFT/10^6; + power_mono1 = [power_m,power_d,power_s]; + f_mono1 = [mono,f_d,f_s]; + [f_mono,index] = sort(f_mono1,'ascend'); + Power_mono = power_mono1(index); + for k = 1 : numel(band) + f_start(k) = band{k}(1)*fs/NFFT/10^6; + f_end(k) = band{k}(end)*fs/NFFT/10^6; + end + f_wide = sum(abs(f_start - f_end)); + Power_band = 2*sum(abs(data(J+1)).^2)/NFFT^2; + Num_band = length(f_end); +end \ No newline at end of file diff --git a/Band_Multi_Parameter.m b/Band_Multi_Parameter.m new file mode 100644 index 0000000..836d41a --- /dev/null +++ b/Band_Multi_Parameter.m @@ -0,0 +1,74 @@ +%%************************【频带干扰输出参数函数】****************************%% +%%=============================参数说明===================================%% +% J 检测出来的干扰点集合 +% Power_multi 多音干扰功率集合 +% f_multi 频带干扰频率集合 +% Num_multi 多音干扰个数 +% Power_band 频带干扰功率 +% f_wide 频带干扰带宽 +% f_start频带起始频率 +% f_end频带截止频率 +% Num_band 频带干扰个数 +% data 频域数据 +% fs 采样频率 +%%========================================================================%% +function [Num_multi,f_multi,Power_multi,Num_band,f_start,f_end,f_wide,Power_band] = Band_Multi_Parameter(data,J,fs,NFFT) + J = sort(J,'ascend')-1; + len_J = length(J); + f_start = []; + f_end = []; + c1=1; + mono_d = cell(0,0); + mono_s = cell(0,0); + mono = []; + f_d = []; + f_s = []; + power_m = []; + power_d = []; + power_s = []; + band = cell(0,0); + while c1 < len_J + c2 = 0; + while (c1 + c2 + 1 <= len_J && J(c1) + c2 + 1==J(c1 + c2 + 1)) + c2 = c2 + 1; + end + if(c2 > 2) + band = [band;(J(c1:1:c1+c2))]; %%连续点超过3,记为频带干扰 + end + if c2 == 2 + mono_d = [mono_d;(J(c1:1:c1+c2))]; %%频谱扩散的单音 + end + if c2 == 1 + mono_s = [mono_s;(J(c1:1:c1+c2))]; + end + if c2 == 0 + mono = [mono,J(c1)]; + end + c1 = c1 + c2 +1; + if c1 == len_J && J(len_J-1) + 1 ~= J(len_J) + mono = [mono,J(len_J)]; + end + end + for k = 1:numel(mono_d) + f_d(k) = mono_d{k}(2)*fs/NFFT/10^6; + power_d(k) = sum(abs(data(mono_d{k}+1)).^2)/NFFT^2; + end + for m = 1:numel(mono_s) + f_s(m) = 0.5*(mono_s{m}(1)+mono_s{m}(2))*fs/NFFT/10^6; + power_s(m) = sum(abs(data(mono_s{m}+1)).^2)/NFFT^2; + end + power_m = abs(data(mono+1)).^2/NFFT^2; + mono = mono*fs/NFFT/10^6; + power_multi1 = [power_m,power_d,power_s]; + f_multi1 = [mono,f_d,f_s]; + [f_multi,index] = sort(f_multi1,'ascend'); + Power_multi =power_multi1(index); + Num_multi = length(f_multi1); + for k = 1 : numel(band) + f_start(k) = band{k}(1)*fs/NFFT/10^6; + f_end(k) = band{k}(end)*fs/NFFT/10^6; + end + f_wide =sum( abs(f_start - f_end)); + Power_band = 2*sum(abs(data(J+1)).^2)/NFFT^2; + Num_band = length(f_end); +end \ No newline at end of file diff --git a/Band_Parameter.m b/Band_Parameter.m new file mode 100644 index 0000000..00a13ab --- /dev/null +++ b/Band_Parameter.m @@ -0,0 +1,37 @@ +%%************************【频带干扰输出参数函数】****************************%% +%%=============================参数说明===================================%% +% J 检测出来的干扰点集合 +% data 频域数据 +% fs 采样频率 +% NFFT FFT点数 +% Power_band 频带干扰功率 +% f_wide 频带干扰带宽 +% f_start频带起始频率 +% f_end频带截止频率 +% Num_band 频带干扰个数 +%%========================================================================%% +function [Num_band,f_start,f_end,f_wide,Power_band] = Band_Parameter(data,J,fs,NFFT) + J = sort(J,'ascend')-1; + len_J = length(J); + f_start = []; + f_end = []; + c1=1; + band = cell(0,0); + while c1 < len_J + c2 = 0; + while (c1 + c2 + 1 <= len_J && J(c1) + c2 + 1==J(c1 + c2 + 1)) + c2 = c2 + 1; + end + if(c2 > 2) + band = [band;(J(c1:1:c1+c2))]; %%连续点超过3,记为频带干扰 + end + c1 = c1 + c2 +1; + end + for k = 1 : numel(band) + f_start(k) = band{k}(1)*fs/NFFT/10^6; + f_end(k) = band{k}(end)*fs/NFFT/10^6; + Power_band(k) = 2*sum(abs(data(band{k}(1:end)+1)).^2)/NFFT^2; + end + f_wide = sum(abs(f_end - f_start)); + Num_band = length(f_end); +end diff --git a/Corrcoef_Calculate.m b/Corrcoef_Calculate.m new file mode 100644 index 0000000..2d59d79 --- /dev/null +++ b/Corrcoef_Calculate.m @@ -0,0 +1,84 @@ +function RU_avai = Corrcoef_Calculate(staFeedback,Type, jam_data) +numUsers = length(staFeedback); +[Nst,numRx,numSTS] = size(staFeedback{1}); +% combine_matrix = zeros(Nst,numRx*numUsers,numSTS); +% 灏嗗弽棣堢煩闃垫寜鐢ㄦ埛绾靛悜鎷兼帴 +combine_matrix = cell(1,numRx); +Nru = 4; +RU_avai = zeros(1,Nru); +for rxIdx = 1:numRx + combine_matrix{rxIdx} = zeros(Nst,numUsers,numSTS); + for userIdx = 1:numUsers + combine_matrix{rxIdx}(:,userIdx,:) = staFeedback{userIdx}(:,rxIdx,:); + end + + ru1 = combine_matrix{rxIdx}(1:52,:,:); + ru2 = combine_matrix{rxIdx}(54:105,:,:); + ru3 = combine_matrix{rxIdx}(138:190,:,:); + ru4 = combine_matrix{rxIdx}(191:end,:,:); + RU_matrix = {ru1,ru2,ru3,ru4}; + + for ruIdx = 1:Nru + fprintf('绗%d涓猂U锛歕n',ruIdx); + % 璁$畻姣忎釜绌洪棿娴佸湪涓嶅悓鎺ユ敹澶╃嚎涓嬬殑鐩稿叧绯绘暟 + Coefs = cell(1,numSTS); + num_corr = zeros(1,numSTS); + disp('姣忎釜绌洪棿娴佺殑淇¢亾鐩稿叧绯绘暟鐭╅樀涓猴細'); + for stsIdx = 1:numSTS + Coef = corrcoef(abs(RU_matrix{ruIdx}(:,:,stsIdx))); + Coef(logical(eye(size(Coef)))) = -1; + Coefs{stsIdx} = Coef; + disp(Coef); + num_corr(stsIdx) = sum(Coef>0.5,'all'); + end + disp('鐩稿叧绯绘暟澶т簬0.5鐨勫ぉ绾夸负锛'); + disp(num_corr); + if sum(num_corr) == 0 + RU_avai(ruIdx) = RU_avai(ruIdx)+1; + end + end +end +% figure(); +% for rxIdx = 1:numRx +% for stsIdx = 1:numSTS +% for userIdx = 1:numUsers +% plot(abs(combine_matrix{rxIdx}(:,userIdx,stsIdx))); +% hold on; +% end +% end +% end +% hold off; +% pause(0.5); +% close; + + +% 鍒ゆ柇棰戣氨骞叉壈瀵筊U鐨勫奖鍝 +if strcmp(Type,'multi') + for Idx = 1:length(jam_data) + f_idx = jam_data{1}(Idx)/10*Nst; + if 1 < f_idx < 52 + RU_avai(1) = RU_avai(1)-0.5; + elseif 54 < f_idx < 105 + RU_avai(2) = RU_avai(2)-0.5; + elseif 138 < f_idx < 190 + RU_avai(3) = RU_avai(3)-0.5; + elseif 191 < f_idx < Nst + RU_avai(4) = RU_avai(4)-0.5; + end + end +elseif strcmp(Type,'mono') + f_idx = jam_data/10*Nst; + if 1 < f_idx < 52 + RU_avai(1) = RU_avai(1)-0.5; + elseif 54 < f_idx < 105 + RU_avai(2) = RU_avai(2)-0.5; + elseif 138 < f_idx < 190 + RU_avai(3) = RU_avai(3)-0.5; + elseif 191 < f_idx < Nst + RU_avai(4) = RU_avai(4)-0.5; + end +end + +fprintf('4涓猂U涓彲鐢ㄧ殑RU涓猴細'); +disp(RU_avai); +end \ No newline at end of file diff --git a/DataDecoding.m b/DataDecoding.m new file mode 100644 index 0000000..22c8614 --- /dev/null +++ b/DataDecoding.m @@ -0,0 +1,99 @@ +function [] = DataDecoding(rx) + +%% HE-Data Decoding +% The updated +% object for each user can then be used to recover the PSDU bits for each +% user in the HE-Data field. + +cfgDataRec = trackingRecoveryConfig; +cfgDataRec.PilotTracking = pilotTracking; + +fprintf('Decoding HE-Data...\n'); +for iu = 1:numUsers + % Get recovery configuration object for each user + user = cfgUsers{iu}; + if strcmp(pktFormat,'HE-MU') + fprintf(' Decoding User:%d, STAID:%d, RUSize:%d\n',iu,user.STAID,user.RUSize); + else + fprintf(' Decoding RUSize:%d\n',user.RUSize); + end + + heInfo = wlanHEOFDMInfo('HE-Data',chanBW,user.GuardInterval,[user.RUSize user.RUIndex]); + + % HE-LTF demodulation and channel estimation + rxHELTF = rx(pktOffset+(ind.HELTF(1):ind.HELTF(2)),:); + heltfDemod = wlanHEDemodulate(rxHELTF,'HE-LTF',chanBW,user.GuardInterval, ... + user.HELTFType,[user.RUSize user.RUIndex]); + [chanEst,pilotEst] = heLTFChannelEstimate(heltfDemod,user); + + % Number of expected data OFDM symbols + symLen = heInfo.FFTLength+heInfo.CPLength; + numOFDMSym = (ind.HEData(2)-ind.HEData(1)+1)/symLen; + + % HE-Data demodulation with pilot phase and timing tracking + % Account for extra samples when extracting data field from the packet + % for sample rate offset tracking. Extra samples may be required if the + % receiver clock is significantly faster than the transmitter. + maxSRO = 120; % Parts per million + Ne = ceil(numRxSamples*maxSRO*1e-6); % Number of extra samples + Ne = min(Ne,rxWaveLen-numRxSamples); % Limited to length of waveform + numRxSamplesProcess = numRxSamples+Ne; + rxData = rx(pktOffset+(ind.HEData(1):numRxSamplesProcess),:); + if user.RUSize==26 + % Force CPE only tracking for 26-tone RU as algorithm susceptible + % to noise + cfgDataRec.PilotTracking = 'CPE'; + else + cfgDataRec.PilotTracking = pilotTracking; + end + [demodSym,cpe,peg] = heTrackingOFDMDemodulate(rxData,chanEst,numOFDMSym,user,cfgDataRec); + + % Estimate noise power in HE fields + demodPilotSym = demodSym(heInfo.PilotIndices,:,:); + nVarEst = heNoiseEstimate(demodPilotSym,pilotEst,user); + + % Equalize + [eqSym,csi] = heEqualizeCombine(demodSym,chanEst,nVarEst,user); + + % Discard pilot subcarriers + eqSymUser = eqSym(heInfo.DataIndices,:,:); + csiData = csi(heInfo.DataIndices,:); + + % Demap and decode bits + rxPSDU = wlanHEDataBitRecover(eqSymUser,nVarEst,csiData,user,'LDPCDecodingMethod','layered-bp'); + + % Deaggregate the A-MPDU + [mpduList,~,status] = wlanAMPDUDeaggregate(rxPSDU,wlanHESUConfig); + if strcmp(status,'Success') + fprintf(' A-MPDU deaggregation successful \n'); + else + fprintf(' A-MPDU deaggregation unsuccessful \n'); + end + + % Decode the list of MPDUs and check the FCS for each MPDU + for i = 1:numel(mpduList) + [~,~,status] = wlanMPDUDecode(mpduList{i},wlanHESUConfig,'DataFormat','octets'); + if strcmp(status,'Success') + fprintf(' FCS pass for MPDU:%d\n',i); + else + fprintf(' FCS fail for MPDU:%d\n',i); + end + end + + % Plot equalized constellation of the recovered HE data symbols for all + % spatial streams per user + hePlotEQConstellation(eqSymUser,user,ConstellationDiagram,iu,numUsers); + + % Measure EVM of HE-Data symbols + release(EVM); + EVM.ReferenceConstellation = wlanReferenceSymbols(user); + rmsEVM = EVM(eqSymUser(:)); + fprintf(' HE-Data EVM:%2.2fdB\n\n',20*log10(rmsEVM/100)); + + % Plot EVM per symbol of the recovered HE data symbols + hePlotEVMPerSymbol(eqSymUser,user,EVMPerSymbol,iu,numUsers); + + % Plot EVM per subcarrier of the recovered HE data symbols + hePlotEVMPerSubcarrier(eqSymUser,user,EVMPerSubcarrier,iu,numUsers); +end +end \ No newline at end of file diff --git a/DataGenerate.m b/DataGenerate.m new file mode 100644 index 0000000..ee9d874 --- /dev/null +++ b/DataGenerate.m @@ -0,0 +1,81 @@ +function txPSDU = DataGenerate(cfgUI,numPkt,SEED,arg,txPSDUByteUser) +%*********************************lql03.21淇敼********************************* +numUsers = cfgUI.numUsers; +DataType = cfgUI.DataType; +if isnumeric(arg) + if cfgUI.numUsers == 1 + cfgHE = wlanHESUConfig; + else + cfgHE = wlanHEMUConfig(193); + end + psduLength = ones(1,numUsers)*arg; +else + if isfield(arg,'InterleavLen') + psduLength = arg.PacketSize; + else + cfgHE = arg; + psduLength = getPSDULength(cfgHE); % PSDU length in bytes + end +end +% psduLength = getPSDULength(cfgHE); % PSDU length in bytes +%*********************************lql03.21淇敼********************************* + + + +if numUsers==1 + txPSDU = cell(1,1); +else + allocInfo = ruInfo(cfgHE); + txPSDU = cell(1,allocInfo.NumUsers); +end +% [len,width] = size(txPSDUByteUser(:,:,1)); + +if strcmp(cfgUI.DataType,'photo') + txPSDUBin = ones(length(txPSDUByteUser(:,1))*8,numUsers); + for userIdx = 1:numUsers + txPSDUBin(:,userIdx) = reshape(int2bit(txPSDUByteUser(:,userIdx),8),1,[]); + end +end +for i = 1:numPkt + if numUsers==1 + rng(i+SEED); + switch DataType + case 'test' + txPSDU{1}(:,i) = randi([0 1],psduLength*8,1); + case 'text' + case 'photo' + % 灏嗙i涓寘瀵瑰簲鐨勫唴瀹硅祴鍊肩粰绗琲涓寘 zjd3.26 + if i*psduLength*8 <= length(txPSDUBin) + txPSDU{1}(:,i) = txPSDUBin((i-1)*psduLength*8+1:i*psduLength*8,:); + elseif i*psduLength*8 > length(txPSDUBin) && (i-1)*psduLength*8 < length(txPSDUBin) + padLength = psduLength*8-(length(txPSDUBin)-(i-1)*psduLength*8); + txPSDU{1}(:,i) = [txPSDUBin((i-1)*psduLength*8+1:length(txPSDUBin),:);randi([0 1],padLength,1)]; + else + txPSDU{1}(:,i) = randi([0 1],psduLength*8,1); + end + end + else + switch DataType + case 'test' + for ii = 1:allocInfo.NumUsers + rng(i+ii+SEED); + txPSDU{ii}(:,i) = randi([0 1],psduLength(ii)*8,1); % Generate random PSDU + end + case 'text' + case 'photo' + for ii = 1:allocInfo.NumUsers + % 灏嗙i涓寘瀵瑰簲鐨勫唴瀹硅祴鍊肩粰绗琲涓寘 zjd3.26 + if i*psduLength(ii)*8 <= length(txPSDUBin) + txPSDU{ii}(:,i) = txPSDUBin((i-1)*psduLength(ii)*8+1:i*psduLength(ii)*8,ii); + elseif (i*psduLength(ii)*8 > length(txPSDUBin)) && ((i-1)*psduLength(ii)*8 < length(txPSDUBin)) + padLength = psduLength(ii)*8-(length(txPSDUBin)-(i-1)*psduLength(ii)*8); % 璁$畻闇瑕佽ˉ澶氬皯闅忔満鏁 + txPSDU{ii}(:,i) = [txPSDUBin((i-1)*psduLength(ii)*8+1:length(txPSDUBin),ii);randi([0 1],padLength,1)]; + else + txPSDU{ii}(:,i) = randi([0 1],psduLength(ii)*8,1); + end + end + end + end +end +end + diff --git a/FCME.m b/FCME.m new file mode 100644 index 0000000..d47160b --- /dev/null +++ b/FCME.m @@ -0,0 +1,45 @@ +%%************************【FCME检测算法函数】****************************%% +%%=============================参数说明===================================%% +% data 待检测的频域信号 +% Pf 虚警概率 +% J 检测出来的干扰点集合 +%%========================================================================%% +function J = FCME(data,Pf) + T = sqrt(-1*(4/pi)*log(Pf)); %%门限因子 + J = []; + L = length(data); + fi = []; + [y_sort,index] = sort(abs(data),'ascend'); %%按幅度值升序排列 + %%求每个频点的幅值 + for k = 1:L + fi(k)=abs(data(k)); + end + %%%%初始化两个集合 + J = index(round(L/10)+1:end); %%剩余有干扰集合 + I = index(1:round(L/10)); %%无干扰集合\ + S = mean(fi(I)); + %%迭代求出干扰点集合 + for m = 1:50 %%FCME迭代次数 + length_J = length(J); %%J集合的长度 + ii = 1; + jj = 1; + new_I = []; + new_J = []; + for j1 = 1:length_J + J1 = J(j1); %%J集合的索引 + if fi(J1) < T*S %%如果小于门限,则移到I集合,从J集合剔除 + new_I(ii) = J1; + ii = ii +1; + else %%如果大于于门限,则继续留在J集合 + new_J(jj) = J1; + jj = jj + 1; + end + end + if isempty(new_I) %%如果new_I为空,直接跳出 + break; + end + J = new_J; + I = union(I,new_I); + S = mean(fi(I)); %%干扰频点 + end +end diff --git a/FCME_pro.m b/FCME_pro.m new file mode 100644 index 0000000..3129f07 --- /dev/null +++ b/FCME_pro.m @@ -0,0 +1,25 @@ +%%***********************【改进FCME检测算法函数】*************************%% +%%=============================参数说明===================================%% +% data 待检测的频域信号 +% Pf 虚警概率 +% J 检测出来的干扰点集合 +%%========================================================================%% +function J = FCME_pro(data_Freuq,Pf) + T = sqrt(-1*(4/pi)*log(Pf)); %%门限因子 + th = 3.432; %%倍数关系 + L = length(data_Freuq); + fi = []; + [y_sort,index] = sort(abs(data_Freuq),'ascend'); + %%求每个频点的幅值 + fi = abs(data_Freuq); + I = index(1:round(L/2)); %%无干扰集合 + E = mean(fi(I)); + Aaim = T*th*E; %%门限 + J = []; + %%比较求得干扰集合 + for mm = 1:L + if fi(mm) > Aaim + J = [J,mm]; + end + end +end diff --git a/HESU_gen.m b/HESU_gen.m new file mode 100644 index 0000000..802bc23 --- /dev/null +++ b/HESU_gen.m @@ -0,0 +1,24 @@ +function byte = HESU_gen(byteIn,psduLength,userIdx) +PSDULength = psduLength(userIdx); +%Create a sequence of data bits and generate an HE SU waveform. +byteInLen=size(byteIn,1); + +bits = zeros(8*PSDULength,1,'double'); +%bits = randi([0 1],8*PSDULength,1,'int8'); + +if(byteInLen > 1) + if(byteInLen <= PSDULength) + bits(1:8*byteInLen) = int8(int2bit(byteIn,8)); + else + bits = int8(int2bit(byteIn(1:PSDULength),8)); + end +end + +byte = int16(bit2int(bits,8)); + +%sr = wlanSampleRate(cfgHESU); % Sample rate +% TxWaveform = wlanWaveformGenerator(bits,cfgHESU); +%TxWaveform = wlanWaveformGenerator(bits,cfgHESU); +% TxWaveform = 0.25.*TxWaveform; +%Rx = TxWaveform; +end \ No newline at end of file diff --git a/JudgeTypeJam.m b/JudgeTypeJam.m new file mode 100644 index 0000000..ff4a3ee --- /dev/null +++ b/JudgeTypeJam.m @@ -0,0 +1,71 @@ +%%************************【判断干扰类型函数】****************************%% +%%=============================参数说明===================================%% +% J: 检测出来的干扰点集合 +%Type: 干扰类型 +%%========================================================================%% +function Type = JudgeTypeJam(J) +J = sort(J,'ascend'); +len_J = length(J); +c1 = 1; +band = cell(0,0); +mono_d = cell(0,0); +mono = []; +if len_J == 0 + Type = 'No jam'; +elseif len_J <= 3 %&& len_J > 0 %% 检测点数小于3,判断是离散还是连续(单音或者多音) + if len_J == 1 + Type = 'mono'; + else + if len_J == 2 + if J(2)-J(1) == 1 + Type = 'mono'; + else + Type = 'multi'; + end + else + if J(3) - J(1) == 2 + Type = 'mono'; + else + Type = 'multi'; + end + end + end +else + while c1 < len_J + c2 = 0; + while (c1 + c2 + 1 <= len_J && J(c1) + c2 + 1== J(c1 + c2 + 1)) + c2 = c2 + 1; + end + if(c2 > 2) + band = [band;(J(c1:1:c1+c2))]; %% 连续点超过3,记为频带干扰 + end + if (c2 == 2 || c2 == 1) + mono_d = [mono_d;(J(c1:1:c1+c2))]; %% 频谱扩散的单音 + end + if c2 == 0 + mono = [mono,J(c1)]; + end + c1 = c1 + c2 +1; + if c1 == len_J && J(len_J-1) + 1 ~= J(len_J) + mono = [mono,J(len_J)]; + end + end + num_band = numel(band); + num_mono_d = numel(mono_d); + num_mono = length(mono); + num_multi = num_mono + num_mono_d; + if num_band ~= 0 + if num_multi == 0 + Type = 'band'; + else + if num_multi >1 + Type = 'band_multi'; + else + Type = 'band_mono'; + end + end + else + Type = 'multi'; + end +end +%% end \ No newline at end of file diff --git a/MCS_sel.m b/MCS_sel.m new file mode 100644 index 0000000..16c94f3 --- /dev/null +++ b/MCS_sel.m @@ -0,0 +1,26 @@ +function [MCS, mcs_no] = MCS_sel(ModulationMode,CodeRate) + switch ModulationMode + case 'BPSK' + MCS = 0; + mcs_no = [2,2]; + case 'QPSK' + if(CodeRate==1/2) + MCS = 1;mcs_no = [4,2]; end + if CodeRate==3/4 + MCS = 2;mcs_no = [4,4]; end + case '16QAM' + if CodeRate==1/2 + MCS = 3;mcs_no = [16,2]; end + if CodeRate==2/3 + MCS = 4;mcs_no = [16,3]; end + if CodeRate==3/4 + MCS = 4;mcs_no = [16,4]; end + case '64QAM' + if CodeRate==2/3 + MCS = 5;mcs_no = [64,3]; end + if CodeRate==3/4 + MCS = 6;mcs_no = [64,4]; end + if CodeRate==5/6 + MCS = 7;mcs_no = [64,6]; end + end +end diff --git a/MU_NDP.m b/MU_NDP.m new file mode 100644 index 0000000..d472410 --- /dev/null +++ b/MU_NDP.m @@ -0,0 +1,9 @@ +function txNDP = MU_NDP(cfgMUMIMO,guardInterval,ChannelBandwidth) +% 浜х敓NDP +cfgNDP = wlanHESUConfig('APEPLength',0,'GuardInterval',guardInterval); % No data in an NDP +cfgNDP.ChannelBandwidth = ChannelBandwidth; +cfgNDP.NumTransmitAntennas = cfgMUMIMO.NumTransmitAntennas; +cfgNDP.NumSpaceTimeStreams = cfgMUMIMO.NumTransmitAntennas; +txNDP = wlanWaveformGenerator([],cfgNDP); +txNDP = [txNDP; zeros(50,size(txNDP,2))]; +end \ No newline at end of file diff --git a/Mono_Parameter.m b/Mono_Parameter.m new file mode 100644 index 0000000..ca05629 --- /dev/null +++ b/Mono_Parameter.m @@ -0,0 +1,23 @@ +%%************************【单音干扰输出参数函数】****************************%% +%%=============================参数说明===================================%% +% J 检测出来的干扰点集合 +% data 频域数据 +% fs 采样频率 +% NFFT FFT点数 +% Power_mono 单音干扰功率 +% f_mono 单音干扰频率 +%%========================================================================%% +function [f_mono,Power_mono] = Mono_Parameter(data,J,fs,NFFT) + J = sort(J,'ascend')-1; + len_J = length(J); + if len_J == 1 + f_mono = J*fs/NFFT/10^6; + else + if len_J == 2 + f_mono = 0.5*(J(1)+J(2))*fs/NFFT/10^6; + else + f_mono = J(2)*fs/NFFT/10^6; + end + end + Power_mono =sum(abs(data(J+1)).^2)/NFFT^2; +end \ No newline at end of file diff --git a/Multi_Parameter.m b/Multi_Parameter.m new file mode 100644 index 0000000..c966dd4 --- /dev/null +++ b/Multi_Parameter.m @@ -0,0 +1,57 @@ +%%************************【多音干扰输出参数函数】****************************%% +%%=============================参数说明===================================%% +% J 检测出来的干扰点集合 +% data 频域数据 +% fs 采样频率 +% NFFT FFT点数 +% Power_multi 多音干扰功率集合 +% f_multi 多音干扰频率集合 +% Num_multi 多音干扰个数 +%%========================================================================%% +function [Num_multi,f_multi,Power_multi] = Multi_Parameter(data,J,fs,NFFT) +J = sort(J,'ascend')-1; +len_J = length(J); +mono_d = cell(0,0); +mono_s = cell(0,0); +mono = []; +f_d = []; +f_s = []; +power_m = []; +power_d = []; +power_s = []; +c1 = 1; + while c1 < len_J + c2 = 0; + while (c1 + c2 + 1 <= len_J && J(c1) + c2 + 1==J(c1 + c2 + 1)) + c2 = c2 + 1; + end + if c2 == 2 + mono_d = [mono_d;(J(c1:1:c1+c2))]; %%频谱扩散的单音 + end + if c2 == 1 + mono_s = [mono_s;(J(c1:1:c1+c2))]; + end + if c2 == 0 + mono = [mono,J(c1)]; + end + c1 = c1 + c2 +1; + if c1 == len_J && J(len_J-1) + 1 ~= J(len_J) + mono = [mono,J(len_J)]; + end + end + for k = 1:numel(mono_d) + f_d(k) = mono_d{k}(2)*fs/NFFT/10^6; + power_d(k) = sum(abs(data(mono_d{k}+1)).^2)/NFFT^2; + end + for m = 1:numel(mono_s) + f_s(m) = 0.5*(mono_s{m}(1)+mono_s{m}(2))*fs/NFFT/10^6; + power_s(m) = sum(abs(data(mono_s{m}+1)).^2)/NFFT^2; + end + power_m = abs(data(mono+1)).^2/NFFT^2; + mono = mono*fs/NFFT/10^6; + power_multi1 = [power_m,power_d,power_s]; + f_multi1 = [mono,f_d,f_s]; + [f_multi,index] = sort(f_multi1,'ascend'); + Power_multi = power_multi1(index); + Num_multi = length(f_multi1); +end \ No newline at end of file diff --git a/PSD_plot.m b/PSD_plot.m new file mode 100644 index 0000000..e3f8b75 --- /dev/null +++ b/PSD_plot.m @@ -0,0 +1,125 @@ +%% PSD Comparison of ADMM1, ADMM2, CFR and seperated OICF +%%% 经过SSPA后的性能比较 +clc;clear; +%% System Parameter +% load system_parameter.mat K1 K2 deltaK2 B N1 N2 G L Finv1 Finv2 F1 F2 + +global X1 X2 x_mixed +% load Index4CCDF_Comparison.mat Index X1 X2 x_mixed + +M = 4; +modu = [0 0 1 1; 0 1 0 1]; +% load CCDF_Comparison_ADMM_P5_5000.mat zmixed_m_A1 zmixed_m_A2 +% load CCDF_Comparison_OICF_P5_5000.mat zmixed_m_O +% load CCDF_Comparison_ICF_P5_5000.mat zmixed_m_R +% save PSD_comparison.mat... +% x_mixed zmixed_m_A1 zmixed_m_A2 zmixed_m_R zmixed_m_O +% load PSD_comparison.mat... +% x_mixed zmixed_m_A1 zmixed_m_A2 zmixed_m_R zmixed_m_O +load(['TXPackets\rx_for_User' num2str(1) '.mat'],'rx'); % 加载用户数据 +x_mixed = rx; +symNum = length(x_mixed); +%% 取出PAPR最大的100个数据 +[PAPR_dex,MeanPower_Orignal] = calculate_PAPR(x_mixed); +symNum = 1000; +for i = 1:symNum + [a(i),b(i)] = max(PAPR_dex); + PAPR_dex(b(i)) = 0; +end +% b = randi([1 5000],1,symNum); +x_mixed = x_mixed(:,b);[PAPR_dex,~] = calculate_PAPR(x_mixed);10*log10(mean(PAPR_dex)); +% zmixed_m_A1=zmixed_m_A1(:,b);[PAPR_dex,~] = calculate_PAPR(zmixed_m_A1);10*log10(mean(PAPR_dex)) +% zmixed_m_A2=zmixed_m_A2(:,b);[PAPR_dex,~] = calculate_PAPR(zmixed_m_A2);10*log10(mean(PAPR_dex)) +% zmixed_m_R=zmixed_m_R(:,b); [PAPR_dex,~] = calculate_PAPR(zmixed_m_R);10*log10(mean(PAPR_dex)) +% zmixed_m_O=zmixed_m_O(:,b); [PAPR_dex,~] = calculate_PAPR(zmixed_m_O);10*log10(mean(PAPR_dex)) +%% SSPA Model +p = 3;IBO = 5;L=4; +% Original symbol with SSPA +x_origin_out = SSPA_model(p,IBO,x_mixed); +[~,MeanPower] = calculate_PAPR(x_origin_out); +x_origin_out = x_origin_out./sqrt(MeanPower*L); +% % ADMM1 symbol with SSPA +% x_A1_out = SSPA_model(p,IBO,zmixed_m_A1); +% [~,MeanPower] = calculate_PAPR(x_A1_out); +% x_A1_out = x_A1_out./sqrt(MeanPower*L); +% % ADMM2 symbol with SSPA +% x_A2_out = SSPA_model(p,IBO,zmixed_m_A2); +% [~,MeanPower] = calculate_PAPR(x_A2_out); +% x_A2_out = x_A2_out./sqrt(MeanPower*L); +% % CFR symbol with SSPA +% x_R_out = SSPA_model(p,IBO,zmixed_m_R); +% [~,MeanPower] = calculate_PAPR(x_R_out); +% x_R_out = x_R_out./sqrt(MeanPower*L); +% % OICF symbol with SSPA +% x_O_out = SSPA_model(p,IBO,zmixed_m_O); +% [~,MeanPower] = calculate_PAPR(x_O_out); +% x_O_out = x_O_out./sqrt(MeanPower*L); +%% PSD plot +mode = 1; % 1.周期图法:periodogram 2.周期图法加汉明窗 2.文氏窗法:pwelch +N = (512+36)*2;shift = -N/8; + +[pxx,freq] = sprectrum(x_origin_out,mode,N); +pxx = circshift(pxx,shift); +plot(freq/pi,10*log10(pxx),'k','LineWidth',1);hold on;grid on + +% [pxx,freq] = sprectrum(x_A1_out,mode,N); +% pxx = circshift(pxx,shift); +% plot(freq/pi,10*log10(pxx),'r','LineWidth',1); +% +% [pxx,freq] = sprectrum(x_A2_out,mode,N); +% pxx = circshift(pxx,shift); +% plot(freq/pi,10*log10(pxx),'b','LineWidth',1); +% +% [pxx,freq] = sprectrum(x_R_out,mode,N); +% pxx = circshift(pxx,shift); +% plot(freq/pi,10*log10(pxx),'.-r','LineWidth',1); +% +% [pxx,freq] = sprectrum(x_O_out,mode,N); +% pxx = circshift(pxx,shift); +% plot(freq/pi,10*log10(pxx),'g','LineWidth',1); +% +% xlim([-1 1]);ylim([-38 0.5]) +% legend('Origin','ADMM1','ADMM2','CFR','OICF') +% xlabel('Normalized Frequency') +% ylabel('PSD (dB)') + +%% APPENDIX FUNCTIONS +function [x_out] = SSPA_model(p,IBO,x) + [~,Mean_Power] = calculate_PAPR(x); +% PAPR_out_O = 10*log10(PAPR_dex); + C = sqrt(Mean_Power)*10^(IBO/20); + x_out = x./ (1+(abs(x)./C).^(2*p)).^(1/(2*p)); +end + +function [pxx,freq] = sprectrum(x,mode,N) + switch mode + case 1 + [pxx,freq] = periodogram(x *sqrt(2*pi),[],N,'centered'); + case 2 + [pxx,freq] = periodogram(x *sqrt(2*pi),hamming(length(x(:,1))),N,'centered'); + case 3 + [pxx,freq] = pwelch(x *sqrt(2*pi),[],[],N,'centered'); + end + pxx = mean(pxx,2); +end + +function [PAPR_dex,Mean_Power] = calculate_PAPR(x) +% PAPR_dex 计算得到所有符号各自的PAPR +% Mean_Power 符号的平均功率 + Signal_Power = abs(x).^2; + Peak_Power = max(Signal_Power,[],1); % norm(x,Inf)^2 + Mean_Power = mean(Signal_Power,1); % norm(x,2)^2/(K*IF) + PAPR_dex = Peak_Power./Mean_Power; +end + +function [EVM1,EVM2,EVM3,EVM_sum] = calculate_EVM(s1,s2,s3,symNum) + % 计算得到所有符号各自的EVM + global X1 X2_1 X2_2 + EVM1=zeros(1,symNum);EVM2=zeros(1,symNum);EVM3=zeros(1,symNum);EVM_sum=zeros(1,symNum); + for i = 1:symNum + EVM1(i) = norm(X1(:,i)-s1(:,i),2)/norm(X1(:,i),2); + EVM2(i) = norm(X2_1(:,i)-s2(:,i),2)/norm(X2_1(:,i),2); + EVM3(i) = norm(X2_2(:,i)-s3(:,i),2)/norm(X2_2(:,i),2); + EVM_sum(i) = EVM1(i) + EVM2(i) + EVM3(i); + end +end diff --git a/PassChannel.m b/PassChannel.m new file mode 100644 index 0000000..26edf2c --- /dev/null +++ b/PassChannel.m @@ -0,0 +1,121 @@ + +load('TXPackets\transmit_data.mat','tx_data','cfgUI'); + +snr = 30; + +if cfgUI.numUsers == 1 + CommMode = 'HE-SU'; +else + CommMode = 'HE-MU'; +end + +%***************************************** +% number of (real + virtual) users +RU_index = RU_alloc(cfgUI); +allocInfo = wlan.internal.heAllocationInfo(RU_index); +cfgUI.numUsers = allocInfo.NumUsers; +%***************************************** + +rx_data = chanPass(tx_data,cfgUI,snr,CommMode); +for i =1:cfgUI.numUsers + matName = ['rx_for_User',num2str(i),'.mat']; + if cfgUI.numUsers == 1 + rx = rx_data; + save(['TXPackets\',matName],'rx'); + else + rx = rx_data{i}; + save(['TXPackets\',matName],'rx'); + end +end + +%% + +function rx = chanPass(txPad,cfgUI,snr,CommMode) +numTx = cfgUI.numTx; +numRx = cfgUI.numRx; +guardInterval = 0.8;% Guard interval in Microseconds +cfgNDP = wlanHESUConfig('APEPLength',0,'GuardInterval',guardInterval); % No data in an NDP +cfgNDP.ChannelBandwidth = cfgUI.ChannelBandwidth; +cfgNDP.NumTransmitAntennas = numTx; +cfgNDP.NumSpaceTimeStreams = numRx; +if strcmp(CommMode,'HE-SU') + % Create and configure the TGax channel + chanBW = cfgNDP.ChannelBandwidth; + tgaxChannel = wlanTGaxChannel; + tgaxChannel.DelayProfile = 'Model-B'; + tgaxChannel.NumTransmitAntennas = cfgNDP.NumTransmitAntennas; + tgaxChannel.NumReceiveAntennas = numRx; + tgaxChannel.TransmitReceiveDistance = 5; % Distance in meters for NLOS + tgaxChannel.ChannelBandwidth = chanBW; + tgaxChannel.LargeScaleFadingEffect = 'None'; + fs = wlanSampleRate(cfgNDP); + tgaxChannel.SampleRate = fs; + tgaxChannel.RandomStream = 'mt19937ar with seed'; + tgaxChannel.Seed = 5; + % Get occupied subcarrier indices and OFDM parameters + ofdmInfo = wlanHEOFDMInfo('HE-Data',cfgNDP); + + stream = RandStream('combRecursive','Seed',99); + % stream.Substream = snr; + RandStream.setGlobalStream(stream); + + % Create an instance of the AWGN channel per SNR point simulated + awgnChannel = comm.AWGNChannel; + awgnChannel.NoiseMethod = 'Signal to noise ratio (SNR)'; + awgnChannel.SignalPower = 1/tgaxChannel.NumReceiveAntennas; + % Account for noise energy in nulls so the SNR is defined per + % active subcarrier + awgnChannel.SNR = snr-10*log10(ofdmInfo.FFTLength/ofdmInfo.NumTones); + + % Pass through a fading indoor TGax channel + % reset(tgaxChannel); % Reset channel for different realization + rx = tgaxChannel(txPad); + % Pass the waveform through AWGN channel + rx = awgnChannel(rx); +elseif strcmp(CommMode,'HE-MU') + tgaxBase = wlanTGaxChannel; + tgaxBase.DelayProfile = 'Model-D'; % Delay profile + tgaxBase.NumTransmitAntennas = cfgNDP.NumTransmitAntennas; % Number of transmit antennas + tgaxBase.NumReceiveAntennas = numRx; % Each user has two receive antennas + tgaxBase.TransmitReceiveDistance = 10; % Non-line of sight distance + tgaxBase.ChannelBandwidth = cfgNDP.ChannelBandwidth; + tgaxBase.SampleRate = wlanSampleRate(cfgNDP); + % Set a fixed seed for the channel + tgaxBase.RandomStream = 'mt19937ar with seed'; + + % Generate per-user channels + numUsers = cfgUI.numUsers; % Number of users simulated in this example + tgax = cell(1,numUsers); + for userIdx = 1:numUsers +% tgaxBase.Seed = randi(100); + tgaxBase.Seed = userIdx; + tgax{userIdx} = clone(tgaxBase); + tgax{userIdx}.UserIndex = userIdx; % Set unique user index + end + + awgnChannel = comm.AWGNChannel; + awgnChannel.NoiseMethod = 'Signal to noise ratio (SNR)'; + awgnChannel.SignalPower = 1/numRx; + % Account for noise energy in nulls so the SNR is defined per + % active subcarrier + awgnChannel.SNR = snr; + rxUsers = cell(1,numUsers); +% allocInfo = ruInfo(cfgNDP); + % 浣挎暟鎹粡杩囦俊閬 + for userIdx = 1:numUsers + tgaxClone = cloneChannels(tgax); + rxUsers{userIdx} = tgaxClone{userIdx}(txPad); + rxUsers{userIdx} = awgnChannel(rxUsers{userIdx}); + end + rx = rxUsers; +end +end +function tgaxClone = cloneChannels(tgax) +% Clone the channels before running to ensure the same realization can be +% used in the next simulation + tgaxClone = cell(size(tgax)); + numUsers = numel(tgax); + for i=1:numUsers + tgaxClone{i} = clone(tgax{i}); + end +end \ No newline at end of file diff --git a/RU_Alloc.png b/RU_Alloc.png new file mode 100644 index 0000000..12bfbce Binary files /dev/null and b/RU_Alloc.png differ diff --git a/RU_alloc.m b/RU_alloc.m new file mode 100644 index 0000000..dd38249 --- /dev/null +++ b/RU_alloc.m @@ -0,0 +1,59 @@ +function RU_index = RU_alloc(cfgUI) +if cfgUI.numUsers == 1 + f_occupy = strcat(num2str((cfgUI.user1.f)')); + switch f_occupy.' + case '1111' + RU_index = 192;% 绌哄垎 + case '1100' + RU_index = 96;% 106 棰戝垎 + case '0011' + RU_index = 96;% 242 绌哄垎 + case '1000' + RU_index = 112;% 52 棰戝垎 + case '0100' + RU_index = 112;% 52 棰戝垎 + case '0010' + RU_index = 112;% 52 棰戝垎 + case '0001' + RU_index = 112;% 52 棰戝垎 + otherwise + RU_index = 192; + end + RU_index = 192; % 涓轰繚璇佷唬鐮佹甯歌繍琛岀殑鏉冨疁涔嬭 +elseif cfgUI.numUsers == 2 + f_occupy = strcat(num2str((cfgUI.user1.f + cfgUI.user2.f)')); + switch f_occupy.' + case '2222' + RU_index = 193;% 242 绌哄垎 + case '1111' + RU_index = 96;% 106 棰戝垎 + case '2200' + RU_index = 100;% 242 绌哄垎 + case '0022' + RU_index = 97;% 52 棰戝垎 + case '1110' + RU_index = 24;% 52 棰戝垎 + case '1101' + RU_index = 24;% 52 棰戝垎 + case '0011' + RU_index = 24;% 52 棰戝垎 + case '1011' + RU_index = 16;% 52 绌哄垎 + case '0111' + RU_index = 16;% 52 棰戝垎 + case '1100' + RU_index = 16;% 3鐢ㄦ埛锛屽墠106绌哄垎 + case '1001' + RU_index = 112;% 52 棰戝垎 + case '1010' + RU_index = 112;% 52 绌哄垎 + case '0101' + RU_index = 112;% 52 棰戝垎 + case '0110' + RU_index = 112;% 3鐢ㄦ埛锛屽墠106绌哄垎 + otherwise + RU_index = 193; + end + RU_index = 193; % 涓轰繚璇佷唬鐮佹甯歌繍琛岀殑鏉冨疁涔嬭 +end +end \ No newline at end of file diff --git a/RU_for_cfgUI.m b/RU_for_cfgUI.m new file mode 100644 index 0000000..44b9f9d --- /dev/null +++ b/RU_for_cfgUI.m @@ -0,0 +1,45 @@ +function cfgUI = RU_for_cfgUI(RU_avai,cfgUI) +mode = strrep(num2str(RU_avai),' ',''); +if cfgUI.numUsers == 1 + switch mode + case {'1111','1100','0011','1000','0100','0010','0001'} + cfgUI.user1.f = RU_avai; + case {'0111','1011'} + cfgUI.user1.f = [0,0,1,1]; + case {'1101','1110'} + cfgUI.user1.f = [1,1,0,0]; + case {'1001','1010'} + cfgUI.user1.f = [1,0,0,0]; + case {'0110','0101'} + cfgUI.user1.f = [0,1,0,0]; + end +elseif cfgUI.numUsers == 2 + switch mode + case '1111' + cfgUI.user1.f = [1,1,1,1]; + cfgUI.user2.f = [1,1,1,1]; + case {'0111','1011','0011'} + cfgUI.user1.f = [0,0,1,1]; + cfgUI.user2.f = [0,0,1,1]; + case {'1101','1110','1100'} + cfgUI.user1.f = [1,1,0,0]; + cfgUI.user2.f = [1,1,0,0]; + case {'0001','0010','0100','1000'} + cfgUI.user1.f = RU_avai; + cfgUI.user2.f = RU_avai; + case '0101' + cfgUI.user1.f = [0,1,0,0]; + cfgUI.user2.f = [0,0,0,1]; + case '0110' + cfgUI.user1.f = [0,1,0,0]; + cfgUI.user2.f = [0,0,1,0]; + case '1001' + cfgUI.user1.f = [1,0,0,0]; + cfgUI.user2.f = [0,0,0,1]; + case '1010' + cfgUI.user1.f = [1,0,0,0]; + cfgUI.user2.f = [0,0,1,0]; + end +end +end + \ No newline at end of file diff --git a/RX.m b/RX.m new file mode 100644 index 0000000..b03276f --- /dev/null +++ b/RX.m @@ -0,0 +1,27 @@ +clear; +close all; +clc; +%% LabVIEW鐣岄潰鍙傛暟閰嶇疆1-鎺ユ敹妯″紡-鐣岄潰閫夋嫨閮ㄥ垎 +cfgUI.DataType = 'photo'; % 'test''text''photo' +cfgUI.FeedbackMode = 'feedback'; % 'openloop','feedback'锛涘搴旇彍鍗曟爮鐨勨樻墜鍔ㄢ欏拰鈥樻櫤鑳解 + +cfgUI.numTx = 2; +cfgUI.numRx = 1; + +cfgUI.ChannelBandwidth = 'CBW20'; + +cfgUI.JamDetect = 'FCME_pro';%'AI' +cfgUI.syncSel = 'default';%'AI' +cfgUI.Estchan = 'default';%'AI-1''AI-2''AI-3' +cfgUI.decode = 'default';%'AI' +cfgUI.demod = 'default';%'AI' +cfgUI.semantic = 0; % 1 杩涜璇箟鏍¢獙 +userIdx = 1; % 瑙g敤鎴1 or 2鐨勬暟鎹 + +cfgUI.numUsers = 2; % 鐢ㄦ埛鏁扮洰 + + +SEED = 1; % 鏀跺彂绾﹀畾鐨勭瀛愶紝鐢ㄤ簬鐢熸垚姣旂壒璁$畻BER +load(['TXPackets\rx_for_User' num2str(userIdx) '.mat'],'rx'); % 鍔犺浇鐢ㄦ埛鏁版嵁 + +[rxPSDU,bitErrorRate,pktFormat,pktOffset,eqSymPlot,Jam_pos,feedback_data,evm,errorMessage] = RX_function(rx,userIdx,cfgUI,SEED); diff --git a/RX_CRC32.m b/RX_CRC32.m new file mode 100644 index 0000000..3d6297b --- /dev/null +++ b/RX_CRC32.m @@ -0,0 +1,9 @@ +function [rx,frmError] = RX_CRC32(dataPacketBits) + poly = [32,26,23,22,16,12,11,10,8,7,5,4,2,1,0]; + crcdetector = comm.CRCDetector(... + 'Polynomial', poly, ... + 'InitialConditions', 1, ... + 'DirectMethod', true, ... + 'FinalXOR', 1); + [rx,frmError] = crcdetector(dataPacketBits); +end \ No newline at end of file diff --git a/RX_NDPCal.m b/RX_NDPCal.m new file mode 100644 index 0000000..9f02867 --- /dev/null +++ b/RX_NDPCal.m @@ -0,0 +1,61 @@ + +function [staFeedback,PDP,rmsDelay,DopplerShift,rssi_dbm,SNR] = RX_NDPCal(rx,cfgHE,ind,pktOffset,gain) + addpath(genpath('.\sounding')); + % ****鎵嬪姩璋冩暣NDP鐨勫昂瀵 + % rxNDP = txNDP(:,1:2); + % 璁$畻寮曞鐭╅樀 + numTx = cfgHE.numTx; + cfgNDP = wlanHESUConfig('APEPLength',0,'GuardInterval',0.8); % No data in an NDP + cfgNDP.NumTransmitAntennas = numTx;%?? + cfgNDP.NumSpaceTimeStreams = numTx;%?? + txNDP = wlanWaveformGenerator([],cfgNDP); + len_NDP = length(txNDP); + % 鍔犲叆PN搴忓垪锛屾浜ゅ簭鍒楋紝閲嶅涓夋宸﹀彸 + K = 255; + soundingSeqMod_1 = zadoffChuSeq(29,K); + soundingSeqMod_2 = zadoffChuSeq(47,K); + % Concatenate multiple sequences to sound the channel periodically. + numSeq = 10; % Number of transmitted sequences in a WSS period + soundingSeqMod = [soundingSeqMod_1, soundingSeqMod_2]; + txSignal = repmat(soundingSeqMod,numSeq,1); + len_sd = length(txSignal); +% for userIdx = 1:numUsers + % NDP琚帴鏀剁鍒╃敤璁$畻鍙嶉淇℃伅 + % Get the full-band beamforming feedback for a user + staFeedback = heUserBeamformingFeedback(rx,cfgNDP); + sounding_seq = rx(len_NDP+1:len_NDP+len_sd); + [PDP,rmsDelay,DopplerShift] = rxChannelSounding(sounding_seq,cfgHE,numSeq,K); +% end + + + + % RSSI + rxLSTF = rx(pktOffset+(ind.LSTF(1):ind.LSTF(2)),:)./(gain); + rssi_legancy = mean(abs(rxLSTF).^2); + rssi_legancy_dbm = 10*log10(rssi_legancy/1e-3); + + rxHETF = rx(pktOffset+(ind.HESTF(1):ind.HELTF(2)),:)./(gain); + rssi = mean(abs(rxHETF).^2); + rssi_dbm = 10*log10(rssi/1e-3); + fprintf(' rssi_legancy: %f dBm\n rssi: %f dBm\n', rssi_legancy_dbm, rssi_dbm); + + % SNR浼拌 + LLTF = rx(ind.LLTF(1):ind.LLTF(2)); + LLTF_length = length(LLTF); + LLTF1 = LLTF(1+LLTF_length/5:LLTF_length*3/5); + LLTF2 = LLTF(1+LLTF_length*3/5:end); + N = 64; + LLTF1_fft = ((abs(fftshift(fft(LLTF1)))).^2)/N; + LLTF2_fft = ((abs(fftshift(fft(LLTF2)))).^2)/N; + + for i = 0:LLTF_length/160-1 + power_signal = sum(LLTF1_fft(i*N+(7:32))) + sum(LLTF1_fft(i*N+(34:59))) + ... + sum(LLTF2_fft(i*N+(7:32))) + sum(LLTF2_fft(i*N+(34:59))); + power_noise = sum(LLTF1_fft(i*N+(1:6))) + sum(LLTF1_fft(i*N+(33))) + sum(LLTF1_fft(i*N+(end-4:end))) + ... + sum(LLTF2_fft(i*N+(1:6))) + sum(LLTF2_fft(i*N+(33))) + sum(LLTF2_fft(i*N+(end-4:end))); + end + SNR = 10*log10(power_signal/(power_noise*64/12)); + fprintf(' SNR: %f dB\n', SNR); + + rmpath(genpath('.\sounding')); +end \ No newline at end of file diff --git a/RX_function.m b/RX_function.m new file mode 100644 index 0000000..85f72d3 --- /dev/null +++ b/RX_function.m @@ -0,0 +1,95 @@ +function [rxPSDU,bitErrorRate,pktFormat,pktOffset,eqSymPlot,Jam_pos,feedback_data,evm,errorMessage] = RX_function(rx,userIdx,cfgUI,SEED) + +cfgRx = wlanHERecoveryConfig; +cfgRx.ChannelBandwidth = cfgUI.ChannelBandwidth; +ind = wlanFieldIndices(cfgRx); +% 鍒濆鍖栵紝浠ラ槻鍚庣画娌℃湁璧嬪--3.26 +rxPSDU = []; +feedback_data = struct(); +feedback_data.staFeedback = []; +feedback_data.PDP = []; +feedback_data.rmsDelay = 0; +feedback_data.DopplerShift = 0; +feedback_data.rssi = 0; +feedback_data.SNR = 0; +feedback_data.JamType = ''; +feedback_data.JamData = []; + +frameIdx = 1; +bitErrorRate = []; +eqSymPlot = []; +Jam_pos = []; +pktFormat = ''; +pktOffset = 0; +evm = 0; + +try +[Type,jam_data,Jam_pos] = SpectrumSense(rx,cfgUI); % 杩涜棰戣氨妫娴 + +rxWaveLen = size(rx,1); +pktOffset = wlanPacketDetect(rx,cfgRx.ChannelBandwidth);% 妫娴嬫槸鍚︽湁甯у埌鏉 +% Adjust packet offset +if isempty(pktOffset) || (pktOffset + ind.LSIG(2) > rxWaveLen) + feedback_stat = 0; +else + feedback_stat = 1; +end + +if feedback_stat==1 + % 鍓嶇澶勭悊 + [rx,pktOffset,gain] = rxFrontEnd(rx,pktOffset,ind,cfgRx); + % 鍒ゆ柇鎺ユ敹鏁版嵁鐨勫寘鏍煎紡 + [cfgRx,~,ind] = pktFormatDetect(ind,pktOffset,rx,cfgRx); + %-------0324-----涓存椂鍒ゆ柇鏄痭onHT杩樻槸HE----------- + if ~isfield(ind,'HEData') + pktFormat = 'non-HT'; + fprintf('%s packet detected\n\n',pktFormat); + [rxPSDU,bitErrorRate,eqSymPlot]=heSUspecial(rx,cfgRx,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot); + + else + pktFormat = cfgRx.PacketFormat; + fprintf('%s packet detected\n\n',pktFormat); + % 瑙f暟鎹寘 + if strcmp(pktFormat,'HE-SU') + flag = isDataEmpty(rx,cfgUI); % 鍒ゆ柇鏁版嵁闀垮害鏄惁涓0锛屼负0鍒欐槸NDP + if flag == 1 % 鏁版嵁闀垮害涓0锛岃鏄庢槸NDP + % *******璁$畻NDP鍙嶉鐨勭粨鏋******* + pktFormat = 'NDP'; + %-------------------------------- + [staFeedback,PDP,rmsDelay,DopplerShift,rssi_dbm,SNR] = RX_NDPCal(rx,cfgUI,ind,pktOffset,gain); +% feedback_data = struct(); + feedback_data.staFeedback = staFeedback; + feedback_data.PDP = PDP; + feedback_data.rmsDelay = rmsDelay; + feedback_data.DopplerShift = DopplerShift; + feedback_data.rssi = rssi_dbm; + feedback_data.SNR = SNR; + feedback_data.JamType = Type; + feedback_data.JamData = jam_data; + save(['TXPackets\NDPfeedback_for_User' num2str(userIdx) '.mat'],'feedback_data'); + elseif flag == 0 % 鏁版嵁闀垮害涓嶄负闆讹紝璇存槑鏄疭U鏁版嵁鍖 + [rxPSDU,bitErrorRate,eqSymPlot] = heSURx(rx,cfgRx,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot); + end + elseif strcmp(pktFormat,'HE-MU') + [rxPSDU,bitErrorRate,eqSymPlot,evm]=heMURx(rx,cfgRx,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot); + end + end +end +rxPSDU = int16(rxPSDU); +bitErrorRate = double(bitErrorRate); +pktOffset = double(pktOffset); +eqSymPlot = double(eqSymPlot); +Jam_pos = double(Jam_pos); +feedback_data.staFeedback = double(feedback_data.staFeedback); +feedback_data.PDP = double(feedback_data.PDP); +feedback_data.DopplerShift = double(feedback_data.DopplerShift); +feedback_data.rssi = double(feedback_data.rssi); +feedback_data.SNR = double(feedback_data.SNR); +feedback_data.JamData = double(feedback_data.JamData); +evm = double(evm); + +errorMessage = ''; +catch ErrorInfo + errorMessage = ErrorInfo.message; +end +end \ No newline at end of file diff --git a/Select_Detection.m b/Select_Detection.m new file mode 100644 index 0000000..361161d --- /dev/null +++ b/Select_Detection.m @@ -0,0 +1,25 @@ +%%************************【选择检测算法函数】*****************************%% +%%=============================参数说明===================================%% +% data_Freuq 待检测的频域信号 +% Pf 虚警概率(双门限代表低门限虚警概率) +% PfH 高门限虚警概率 (单门限时设置为零) +% operator 选择使用的检测算法 +% JJ 检测出来的干扰点集合 +%%========================================================================%% +function JJ = Select_Detection(data_Freuq,Pf,PfH,Num_step,operator) + if strcmp(operator,'CME')==1 + JJ = CME(data_Freuq,Pf); + elseif strcmp(operator,'FCME')==1 + JJ = FCME(data_Freuq,Pf); + elseif strcmp(operator,'FCME_hard')==1 + JJ = FCME_hard(data_Freuq,Pf); + elseif strcmp(operator,'Double_Th_FCME')==1 + JJ = Double_Th_FCME(data_Freuq,Pf,PfH); + elseif strcmp(operator,'FCME_pro')==1 + JJ = FCME_pro(data_Freuq,Pf); + elseif strcmp(operator,'DiffFCME')==1 + JJ = DiffFCME_B(data_Freuq,Pf,Num_step); + else + disp('Error,please enter the correct mode of operation!'); + end +end \ No newline at end of file diff --git a/SpectrumSense.m b/SpectrumSense.m new file mode 100644 index 0000000..b8118d4 --- /dev/null +++ b/SpectrumSense.m @@ -0,0 +1,57 @@ +function [Type,Jam_data,Jam_pos] = SpectrumSense(rx_format,cfgUI) +addpath(genpath('.\JamDetect')); + +NFFT = 2048; +PfH = 0.0001; +Pf = 0.001; +fs = 20e6; +operator = cfgUI.JamDetect; +% 骞叉壈妫娴 +y = fft(rx_format(1:NFFT,:)); +data_Freuq = y(1:NFFT/2,:); +J = Selecdt_Detection(data_Freuq,Pf,PfH,32,operator); +Type = JudgeTypeJam(J); + +% struct jam_data; +jam_data.Num_multi = 0; +jam_data.f_multi = 0; +jam_data.Power_multi = []; +jam_data.Num_band = []; +jam_data.f_start = []; +jam_data.f_end = []; +jam_data.f_wide = []; +jam_data.Power_band = []; + +if strcmp(Type,'multi') + [jam_data.Num_multi,jam_data.f_multi,jam_data.Power_multi] = Multi_Parameter(data_Freuq.',J,fs,NFFT); + elseif strcmp(Type,'mono') + [jam_data.f_multi,jam_data.Power_multi] = Mono_Parameter(data_Freuq.',J,fs,NFFT); + elseif strcmp(Type,'band') + [jam_data.Num_band,jam_data.f_start,jam_data.f_end,jam_data.f_wide,jam_data.Power_band] = Band_Parameter(data_Freuq.',J,fs,NFFT); + elseif strcmp(Type,'band_mono') + [jam_data.f_mono,jam_data.Power_mono,jam_data.Num_band,jam_data.f_start,jam_data.f_end,jam_data.f_wide,jam_data.Power_band] = Band_Mono_Parameter(data_Freuq.',J,fs,NFFT); + elseif strcmp(Type,'band_multi') + [jam_data.Num_multi,jam_data.f_multi,jam_data.Power_multi,jam_data.Num_band,jam_data.f_start,jam_data.f_end,jam_data.f_wide,jam_data.Power_band] = Band_Multi_Parameter(data_Freuq.',J,fs,NFFT); + else + disp('No jam!'); +end +Jam_data = jam_data.f_multi; +dt=1/fs; +data_abs = 10*log(abs(data_Freuq(J))); +J_freuq=(J-1)/(dt*NFFT)/10^6; % 閲囨牱闂撮殧 +ssf=(0:NFFT/2-1)/(dt*NFFT); % 棰戠巼鐭㈤噺 +data_db=10*log(abs(data_Freuq)); +Jam_pos = J_freuq*10^6; +% 鍙栧鏁 +%浣滃浘 +% plot(ssf,data_db); % 鍋氬箙搴﹂璋卞浘 +% grid on; +% hold on; +% stem(Jam_pos,data_abs,'m--','filled','MarkerSize',2) +% xlabel('frequency'); +% ylabel('magnitude'); +% hold off; +% pause(0.5); +% close; +rmpath(genpath('.\JamDetect')); +end diff --git a/TX.m b/TX.m new file mode 100644 index 0000000..d459f35 --- /dev/null +++ b/TX.m @@ -0,0 +1,227 @@ +clear; +close all; +clc; + +%% LabVIEW鐣岄潰鍙傛暟閰嶇疆-鍙戦佹ā寮-鐣岄潰閫夋嫨閮ㄥ垎 + +cfgUI.ChannelBandwidth = 'CBW20'; % 涓嬫媺閫夋嫨锛'CBW20''CBW40''CBW80' +cfgUI.NTX2_enabled = 1; % 涓庣涓夋柟纭欢杩炴帴 +max_run_time = 60; %鏈澶у厑璁哥殑绛夊緟鏃堕棿 +SEED = 1; + +cfgUI.numUsers = 1; % 褰撳墠鐗堟湰鐢ㄦ埛鏁伴噺锛1-2 +cfgUI.DataType = 'test'; % 涓嬫媺閫夋嫨锛'test''text''photo'image.png +numPkt = 5; %鏃堕殭鍐呭寘鐨勬暟閲忥紝褰撳墠鐗堟湰1~5 +pktLength = 1001; % 鍖呴暱搴﹁瀹氫笅鎷夐夋嫨锛坰hort,medium,long锛夊搴斿嚑涓暟瀛1001,7007,12012,璧嬪肩粰APEPLength +if strcmp(cfgUI.DataType,'photo') + txPSDUByte = imread('image.png'); + I_gray = im2gray(txPSDUByte); + [len,width] = size(I_gray); + binary = dec2bin(I_gray(:),8); + bitstream = reshape(binary',1,[]);%灏嗕簩杩涘埗鏁拌繛鎺ヨ捣鏉ュ舰鎴愭瘮鐗规祦 + byte_array = reshape(bitstream',8,[])';%灏嗘瘮鐗规祦杞崲涓哄瓧鑺傛祦 + txPSDUByte = bin2dec(byte_array); + % 鍖呴暱搴︿笌鍖呬釜鏁拌嚜閫傚簲鍒ゆ柇 zjd3.26 + if numPkt*pktLength >= length(txPSDUByte) % 濡傛灉褰撳墠鍖呴暱搴﹀湪5涓寘鍐呰兘浼犺緭褰撳墠鏁版嵁閲 + for pktNum = 1:numPkt % 鍒ゆ柇褰撳墠鍖呴暱搴︿笅鏈灏戠敤鍑犱釜鍖呬紶鏁版嵁 + if pktNum*pktLength >= length(txPSDUByte) + numPkt = pktNum; + break + end + end + else % 褰撳墠鍖呴暱搴︿笉鑳藉湪5涓寘鍐呰兘浼犺緭褰撳墠鏁版嵁閲忥紝鑷姩璋冩暣 + if numPkt*7007 >= length(txPSDUByte) + for pktNum = 1:numPkt % 鍒ゆ柇褰撳墠鍖呴暱搴︿笅鏈灏戠敤鍑犱釜鍖呬紶鏁版嵁 + if pktNum*7007 >= length(txPSDUByte) + numPkt = pktNum; + pktLength = 7007; + break + end + end + else + for pktNum = 1:numPkt % 鍒ゆ柇褰撳墠鍖呴暱搴︿笅鏈灏戠敤鍑犱釜鍖呬紶鏁版嵁 + if pktNum*12012 >= length(txPSDUByte) + numPkt = pktNum; + pktLength = 12012; + break + end + end + end + end + txPSDUByteUser = zeros(len*width+3,cfgUI.numUsers); % 灏嗗浘鐗囧垎缁欎袱涓敤鎴 + for i = 1:cfgUI.numUsers + byteLen = length(txPSDUByte); + byteLenBit = int2bit(byteLen,16); + Len1 = bit2int(byteLenBit(1:8),8); + Len2 = bit2int(byteLenBit(9:16),8); + txPSDUByteUser(1:len*width+2,i) = [Len1;Len2;txPSDUByte]; + txPSDUByteUser(end,i) = mod(sum(txPSDUByteUser(:,i)),256); + end + txPSDUByteUser = uint8(txPSDUByteUser); + save('TXPackets\txPSDUByteUser.mat','txPSDUByteUser'); +elseif strcmp(cfgUI.DataType,'text') +else + txPSDUByteUser = 0; +end + +cfgUI.UserMode = 'SU'; % 浣跨敤妯″紡涓嬫媺閫夋嫨锛'MU','SU'锛孲U鎯呭喌涓嬪彧鏈夊崟鐢ㄦ埛銆佸彲鎵╁睍40MHz銆丼TBC绛夛紝MU妯″紡鍙互瀵瑰崟鐢ㄦ埛杩涜璧勬簮鍒嗛厤锛坙xr锛 +cfgUI.FeedbackMode = 'openloop'; % 涓嬫媺閫夋嫨锛 'openloop','feedback'锛屽搴旇彍鍗曟爮鐨勨樻墜鍔ㄢ欏拰鈥樻櫤鑳解 +SoundingMode = 0; % 鐜鎰熺煡妯″紡锛屼綔涓轰竴涓寜閽 + +cfgUI.numTx = 1; % 鍙戝皠澶╃嚎鏁帮紝褰撳墠鐗堟湰1~2 +cfgUI.numRx = 1; % 鎺ユ敹澶╃嚎鏁帮紝褰撳墠鐗堟湰1~2 + +cfgUI.AI.PAPR = 'PAPR'; % 涓嬫媺閫夋嫨锛'default','PAPR-Net' +cfgUI.PhySecur = 'default'; % 涓嬫媺閫夋嫨锛'鏃','CP闅忔満鍖','淇¢亾瀵嗛挜' + +%----------------浠ヤ笅鏄彲浠ユ墜鍔ㄩ厤缃殑閮ㄥ垎--------------------- +%---璧勬簮鐭╅樀閮ㄥ垎 +cfgUI.user1.s = 1; +cfgUI.user1.f = [1,1,1,1]; % 鏄剧ず涓4涓柟鏍硷紝鍙敤鍒欎负榛戣壊涓1锛屼笉鍙敤鍒欎负鐧借壊涓0 +cfgUI.user1.t = ones(1,numPkt); % 鏄剧ず涓簄umPkt涓柟鏍硷紝鍙敤鍒欎负榛戣壊涓1锛屼笉鍙敤鍒欎负鐧借壊涓0 +cfgUI.user1.p = 0.5; % +cfgUI.user2.s = 1; +cfgUI.user2.f = [1,1,1,1]; +cfgUI.user2.t = ones(1,numPkt); +cfgUI.user2.p = 0.5; +%---娉㈠舰鍙傛暟閮ㄥ垎---user1 +cfgUI.user1.ChannelCode = 'LDPC'; % 涓嬫媺閫夋嫨缂栫爜鏂瑰紡锛欱CC or LDPC +cfgUI.user1.CodeRate = 3/4; % 缂栫爜鐮佺巼涓嬫媺閫夋嫨锛1/2 or 2/3 or 3/4 or 5/6 +cfgUI.user1.CodeLengt = 1600; % 缂栫爜鐮侀暱锛200*8=1600,800 +cfgUI.user1.InterWeaveMode = 'random'; % 浜ょ粐鏂瑰紡涓嬫媺閫夋嫨锛歳andom,horizontal +cfgUI.user1.InterWeaveDepth = 1024; % 浜ょ粐娣卞害锛2048,1024 +cfgUI.user1.ScrambleLength = 6; % 鎵扮爜闀垮害锛32 +cfgUI.user1.CPLength = '1/4'; % CP闀垮害涓嬫媺閫夋嫨锛1/16 or 1/8 or 1/4 +cfgUI.user1.FFTSize = 256; % FFT灏哄锛128 or 256 or 512 +cfgUI.user1.NumSubcarrier = 242; % 瀛愯浇娉㈡暟锛128 or 256 or 512 +cfgUI.user1.ModulationMode = '16QAM'; % 璋冨埗鏂瑰紡涓嬫媺閫夋嫨锛4QAM or 8QAM or 16QAM or 32QAM or 64QAM +cfgUI.user1.MultiAntennaMode = 'STBC'; % 澶氬ぉ绾挎柟寮忎笅鎷夐夋嫨锛'ZF-BF','MMSE-BF', SU妯″紡涓嬫墠鑳戒娇鐢⊿TBC'STBC' +cfgUI.user1.SpatialMapping = 'Hadamard'; % 绌洪棿鏄犲皠鏂瑰紡涓嬫媺閫夋嫨锛'Direct','Hadamard','Fourier','Custom'----------zjd 3.21淇敼 +cfgUI.user1.APEPlength = pktLength; % 鍖呴暱搴﹁瀹氫笅鎷夐夋嫨锛坰hort,medium,long锛夊搴斿嚑涓暟瀛1001,7007,12012 +cfgUI.user1.pilotInterval = 5; +%---娉㈠舰鍙傛暟閮ㄥ垎---user2 +cfgUI.user2.ChannelCode = 'LDPC'; % 缂栫爜鏂瑰紡锛欱CC or LDPC +cfgUI.user2.CodeRate = 3/4; % 缂栫爜鐮佺巼锛1/2 or 2/3 or 3/4 or 5/6 +cfgUI.user2.CodeLengt = 1600; % 缂栫爜鐮侀暱锛200*8=1600,800 +cfgUI.user2.InterWeaveMode = 'random'; % 浜ょ粐鏂瑰紡锛歳andom,horizontal +cfgUI.user2.InterWeaveDepth = 1024; % 浜ょ粐娣卞害锛2048,1024 +cfgUI.user2.ScrambleLength = 6; % 鎵扮爜闀垮害锛32 +cfgUI.user2.CPLength = '1/4'; % CP闀垮害锛1/16 or 1/8 or 1/4 +cfgUI.user2.FFTSize = 256; % FFT灏哄锛128 or 256 or 512 +cfgUI.user2.NumSubcarrier = 242; % 瀛愯浇娉㈡暟锛128 or 256 or 512 +cfgUI.user2.ModulationMode = '16QAM'; % 璋冨埗鏂瑰紡锛4QAM or 8QAM or 16QAM or 32QAM or 64QAM +cfgUI.user2.MultiAntennaMode = 'ZF-BF'; % 澶氬ぉ绾挎柟寮忥細'ZF-BF','MMSE-BF','STBC' +cfgUI.user2.SpatialMapping = 'Direct'; % 绌洪棿鏄犲皠鏂瑰紡锛'Direct','Hadamard','Fourier' +cfgUI.user2.APEPlength = pktLength; % 鍖呴暱搴﹁瀹 +cfgUI.user2.pilotInterval = 5; +%% TX杩愯 +if SoundingMode %%% 鍒ゆ柇TX宸ヤ綔鐘舵 + tic; + feedback_stat = false; + while feedback_stat==false && toc < max_run_time % 娌℃帴鏀跺埌鍙嶉鎴栬呰秴杩囨渶澶х瓑寰呮椂闂 + txNDP = channelSounding(cfgUI); % 寮濮嬪彂閫佹帰娴嬪拰NDP搴忓垪 + tx_data = txNDP; + save('TXPackets\transmit_data.mat','tx_data','cfgUI');% 鍙戦丯DP + pause(1); % 鏆傚仠5s绛夊緟鍙嶉 + numUsers = cfgUI.numUsers; + feedback_data = struct(); % 鍒濆鍖栫粨鏋勪綋 + stat = zeros(1,numUsers); + for uid = 1:numUsers + filename = ['TXPackets/NDPfeedback_for_User' num2str(uid) '.mat']; + stat(uid) = 1; + if exist(filename,'file') ~= 2 % 寰呰鍙栫殑鍙嶉鏂囦欢涓嶅瓨鍦 + filename = ['TXPackets/NDPfeedback_Default_for_User' num2str(uid) '.mat']; % 璇诲彇榛樿鐨勫弽棣堟枃浠 + stat(uid) = 0; % 姝ゆ椂鍙嶉鍙傛暟涓0 + end + feedback_data = setfield(feedback_data,['user' num2str(uid)],loadfeedback(filename)); % 鍔犺浇姣忎釜鐢ㄦ埛瀵瑰簲鐨勫弽棣堟枃浠---闇琛ュ厖snr绛夛紙lxr锛 + end + feedback_stat = all(stat == 1); % 鍒ゆ柇鍙嶉鐘舵 + end + % 瓒呮椂鍋滄 + if feedback_stat==false + error("Time Out !!!"); + else + disp("feedback received"); + end + +elseif strcmp(cfgUI.FeedbackMode,'openloop') + while(1) + % 杩涘叆鏁版嵁浼犺緭姝ラ + numUsers = cfgUI.numUsers; % 璇诲彇鐢ㄦ埛鏁 + feedback_data = struct(); % 鍒濆鍖栫粨鏋勪綋 + for uid = 1:numUsers + filename = ['TXPackets/NDPfeedback_for_User' num2str(uid) '.mat']; + if exist(filename,'file') ~= 2 % 寰呰鍙栫殑鍙嶉鏂囦欢涓嶅瓨鍦 + filename = ['TXPackets/NDPfeedback_Default_for_User' num2str(uid) '.mat']; % 璇诲彇榛樿鐨勫弽棣堟枃浠 + end + feedback_data = setfield(feedback_data,['user' num2str(uid)],loadfeedback(filename)); % 鍔犺浇姣忎釜鐢ㄦ埛瀵瑰簲鐨勫弽棣堟枃浠 + if eval(['isnan(sum(feedback_data.user' num2str(uid) '.staFeedback,"all")) || isempty(feedback_data.user' num2str(uid) '.staFeedback)']) % 鍒ゆ柇staFeedback鏄惁涓虹┖鎴朜aN + filename = ['TXPackets/NDPfeedback_Default_for_User' num2str(uid) '.mat']; % 鏀逛负璇诲彇榛樿鐨勫弽棣堟枃浠 + feedback_data = setfield(feedback_data,['user' num2str(uid)],loadfeedback(filename)); + end + end + [paraCal,tx_data,cfgUI,errorMessage] = TX_transmit(numPkt,SEED,cfgUI,feedback_data,txPSDUByteUser); + save('TXPackets\transmit_data.mat','tx_data','cfgUI'); + if cfgUI.AI.PAPR == 'PAPR' + % PAPR DPD + end + end + +elseif strcmp(cfgUI.FeedbackMode,'feedback') + + while(1) + tic; + feedback_stat = false; + while feedback_stat==false && toc < max_run_time + txNDP = channelSounding(cfgUI); % 寮濮嬪彂閫佹帰娴嬪拰NDP搴忓垪 + tx_data = txNDP; + save('TXPackets\transmit_data.mat','tx_data','cfgUI');% 鍙戦丯DP + pause(0); % 鏆傚仠5s绛夊緟鍙嶉 + numUsers = cfgUI.numUsers; % 璇诲彇鐢ㄦ埛鏁 + stat = zeros(1,numUsers); % 鍒濆鍖杝tat鍙傛暟 + feedback_data = struct(); % 鍒濆鍖栫粨鏋勪綋 + for uid = 1:numUsers + filename = ['TXPackets/NDPfeedback_for_User' num2str(uid) '.mat']; + stat(uid) = 1; + if exist(filename,'file') ~= 2 % 寰呰鍙栫殑鍙嶉鏂囦欢涓嶅瓨鍦 + filename = ['TXPackets/NDPfeedback_Default_for_User' num2str(uid) '.mat']; % 璇诲彇榛樿鐨勫弽棣堟枃浠 + stat(uid) = 0; % 姝ゆ椂鍙嶉鍙傛暟涓0 + end + feedback_data = setfield(feedback_data,['user' num2str(uid)],loadfeedback(filename)); + if eval(['isnan(sum(feedback_data.user' num2str(uid) '.staFeedback,"all")) || isempty(feedback_data.user' num2str(uid) '.staFeedback)']) % 鍒ゆ柇staFeedback鏄惁涓虹┖鎴朜aN + filename = ['TXPackets/NDPfeedback_Default_for_User' num2str(uid) '.mat']; % 鏀逛负璇诲彇榛樿鐨勫弽棣堟枃浠 + feedback_data = setfield(feedback_data,['user' num2str(uid)],loadfeedback(filename)); + end + end + feedback_stat = all(stat == 1); % 鍒ゆ柇鍙嶉鐘舵 + end + % 瓒呮椂鍋滄 + if feedback_stat==false + error("Time Out !!!"); + else + disp("feedback received"); + end + % + + % 杩涘叆鍙傛暟閰嶇疆姝ラ锛屾暟鎹紶杈撴楠 + ack_feedback_stat=true; + stat = 1; + while ack_feedback_stat==true && stat==1 + load('TXPackets\feedback.mat'); + feedback_data = feedback; + [paraCal,tx_data,cfgUI,errorMessage] = TX_transmit(numPkt,SEED,cfgUI,feedback_data,txPSDUByteUser); + save('TXPackets\transmit_data.mat','tx_data','cfgUI'); + if cfgUI.AI.PAPR == 'PAPR' + % PAPR+DPD + end + pause(1); % 鏆傚仠 绛夊緟鍙嶉 + ACK_feedback = cell(1,numUsers); + for uid = 1:numUsers + load(['TXPackets\ACKfeedback_for_User' num2str(uid) '.mat']) + ACK_feedback{uid} = ACK; + end + % 鏄剧ず绌哄彛娉㈠舰鍙傛暟锛孭APR锛屼互鍙夾I绠楁硶鐨剅eward + show2LabVIEW(); %锛侊紒锛 + end + end +end diff --git a/TXPackets/ACKfeedback_for_User1.mat b/TXPackets/ACKfeedback_for_User1.mat new file mode 100644 index 0000000..c55c52f Binary files /dev/null and b/TXPackets/ACKfeedback_for_User1.mat differ diff --git a/TXPackets/ACKfeedback_for_User2.mat b/TXPackets/ACKfeedback_for_User2.mat new file mode 100644 index 0000000..1669eaf Binary files /dev/null and b/TXPackets/ACKfeedback_for_User2.mat differ diff --git a/TXPackets/NDPfeedback_Default_for_User1.mat b/TXPackets/NDPfeedback_Default_for_User1.mat new file mode 100644 index 0000000..a06a1ea Binary files /dev/null and b/TXPackets/NDPfeedback_Default_for_User1.mat differ diff --git a/TXPackets/NDPfeedback_Default_for_User2.mat b/TXPackets/NDPfeedback_Default_for_User2.mat new file mode 100644 index 0000000..cb7f561 Binary files /dev/null and b/TXPackets/NDPfeedback_Default_for_User2.mat differ diff --git a/TXPackets/NDPfeedback_for_User1.mat b/TXPackets/NDPfeedback_for_User1.mat new file mode 100644 index 0000000..c74b9eb Binary files /dev/null and b/TXPackets/NDPfeedback_for_User1.mat differ diff --git a/TXPackets/NDPfeedback_for_User2.mat b/TXPackets/NDPfeedback_for_User2.mat new file mode 100644 index 0000000..f4a280f Binary files /dev/null and b/TXPackets/NDPfeedback_for_User2.mat differ diff --git a/TXPackets/feedback.mat b/TXPackets/feedback.mat new file mode 100644 index 0000000..fe9cb89 Binary files /dev/null and b/TXPackets/feedback.mat differ diff --git a/TXPackets/rx_for_User1.mat b/TXPackets/rx_for_User1.mat new file mode 100644 index 0000000..5b8a9b4 Binary files /dev/null and b/TXPackets/rx_for_User1.mat differ diff --git a/TXPackets/rx_for_User2.mat b/TXPackets/rx_for_User2.mat new file mode 100644 index 0000000..97d2b15 Binary files /dev/null and b/TXPackets/rx_for_User2.mat differ diff --git a/TXPackets/rx_for_User3.mat b/TXPackets/rx_for_User3.mat new file mode 100644 index 0000000..e6fd75f Binary files /dev/null and b/TXPackets/rx_for_User3.mat differ diff --git a/TXPackets/transmit_data.mat b/TXPackets/transmit_data.mat new file mode 100644 index 0000000..6b908d2 Binary files /dev/null and b/TXPackets/transmit_data.mat differ diff --git a/TXPackets/txPSDUByteUser.mat b/TXPackets/txPSDUByteUser.mat new file mode 100644 index 0000000..984f6bb Binary files /dev/null and b/TXPackets/txPSDUByteUser.mat differ diff --git a/TX_SU.m b/TX_SU.m new file mode 100644 index 0000000..006e958 --- /dev/null +++ b/TX_SU.m @@ -0,0 +1,121 @@ +function [txPad,cfgHE]=TX_SU(cfgHE,txPSDU) + +if isfield(cfgHE,'InterleavLen') + tx = SU_Special(cfgHE,txPSDU); + % Add trailing zeros to allow for channel delay + txPad = [tx; zeros(30,1)]; +else + +% psduLength = getPSDULength(cfgHE); % PSDU length in bytes +% txPSDUcrc = TX_CRC32(txPSDU(1:(psduLength*8 - 32))); +txPSDUcrc = txPSDU; +tx = wlanWaveformGenerator(txPSDUcrc,cfgHE); + +% Add trailing zeros to allow for channel delay +txPad = [tx; zeros(30,cfgHE.NumTransmitAntennas)]; + +end +end + +function crcData = TX_CRC32(dataPacketBits) + poly = [32,26,23,22,16,12,11,10,8,7,5,4,2,1,0]; + crcGen32 = comm.CRCGenerator(... + 'Polynomial', poly, ... + 'InitialConditions', 1, ... % Initial states of the internal shift register 绉讳綅瀵勫瓨鍣 + 'DirectMethod', true, ... + 'FinalXOR', 1); % 鎵撳紑 XOR鎿嶄綔 + crcData = crcGen32(dataPacketBits); +end +%% +function tx = SU_Special(WaveformPara,txPSDU) + +% tx_STF = STS_modulation(NumerologyNo); % STF + +scrData = TX_CRC32_Scramble(txPSDU); +trellis = poly2trellis(7,[171 133]); +tx_encoded_bit = convenc(scrData,trellis,WaveformPara.puncpat); +tx_padded_bit = [tx_encoded_bit;zeros(WaveformPara.DataPadding,1)]; +%--------------------------------------------- +dataMod = qammod(tx_padded_bit,WaveformPara.Mod,'InputType','bit','UnitAveragePower',true); +WaveformPara.Power_sym = mean(abs(dataMod).^2); +dataMod = reshape(dataMod,WaveformPara.UsedSubcarrier,[]); + +% 鑾峰彇淇¢亾鍝嶅簲 %鍋囪宸茬煡鍓嶄竴鏃跺埢鐨勪俊鍙 + % 鐢熸垚淇¢亾鍝嶅簲 +% N_carriers = WaveformPara.N_FFT; +% rng(2); +% channel_response=sqrt(1/2)*(randn(1,N_carriers)+1i*randn(1,N_carriers)); +% % 绐楃殑鏁扮洰 +% N_windows = 8; +% % 瀹氫箟閲忓寲姘村钩鎬绘暟 +% N_level = 10; +% % 鏈灏廋P鍊硷紝澶栭儴鏉′欢锛屽彇鍐充簬鏈澶у欢杩熸嫇灞曪紝鏈夋渶灏廋P鍊>鏈澶у欢杩熸嫇灞 +% min_len_CP = 30; +% % 娴嬭瘯鍑芥暟 +% CPset = Generate_CP(channel_response,N_windows,N_level,min_len_CP); + +% 鐗╃悊灞傚姞瀵 +% [serialOFDMsym,WaveformPara.pilot,myFrameLen] = tx_FFTSymbolGen_PilotBlockPN(dataMod,WaveformPara,CPset); % 鍔燙P鐨勫湴鏂 +[serialOFDMsym,WaveformPara.pilot] = tx_FFTSymbolGen_PilotBlockPN(dataMod,WaveformPara); +WaveformPara.Power_sig = mean(abs(serialOFDMsym).^2); +txdata = serialOFDMsym/sqrt(WaveformPara.Power_sig); + +cfg = wlanNonHTConfig('SignalChannelBandwidth',true, ... + 'BandwidthOperation','Static'); +cfg.MCS = WaveformPara.MCS; +cfg.PSDULength = WaveformPara.PacketSize; %0-4095 +% tx_STF = STS_modulation(NumerologyNo); % STF +FrameNo = WaveformPara.NumerologyNo; +tx_STF = wlanLSTF(cfg); % STF +tx_LTF = wlanLLTF(cfg); % LTF +[tx_SIG, tx_sigdata] = newLSIG(cfg,FrameNo);% SIG + +tx = [tx_STF;tx_LTF;tx_SIG;serialOFDMsym;zeros(30,1)]; + + +end + + +function [serialOFDMsym,pilot] = tx_FFTSymbolGen_PilotBlockPN(dataModInMatrix,WaveformPara,SysParameters) + +numOfdmSymbol = WaveformPara.FrameSymNum; +N_used = WaveformPara.UsedSubcarrier; +CP_Len = WaveformPara.CP_Len; + +pnSequence = comm.PNSequence('Polynomial',[8 6 5 4 0], ... + 'SamplesPerFrame',N_used,'InitialConditions',[0 0 0 0 0 0 0 1]); + +% pilotPower = WaveformPara.Power_sym; +% pilot = generatePilot(pnSequence,pilotPower); +pilot = generatePilot(pnSequence,1); + +m = WaveformPara.PilotNum-1; +dataModAddPilot = zeros(N_used,numOfdmSymbol); +for i = 1:m + dataModAddPilot(:,(i-1)*(WaveformPara.PilotInterval+1)+1:i*(WaveformPara.PilotInterval+1)) = ... + [pilot,dataModInMatrix(:,(i-1)*WaveformPara.PilotInterval+1:i*WaveformPara.PilotInterval)]; +end +dataModAddPilot(:,m*(WaveformPara.PilotInterval+1)+1:end) = ... + [pilot,dataModInMatrix(:,m*WaveformPara.PilotInterval+1:end)]; + +dataModAddPilot_shift = [zeros(1,WaveformPara.FrameSymNum);... + dataModAddPilot(1:(N_used-1)/2,:);... + zeros(WaveformPara.NullSubcarrier,WaveformPara.FrameSymNum);... + dataModAddPilot(1+(N_used-1)/2:end,:)]; + + +OFDMsym = ifft(dataModAddPilot_shift,[],1)*sqrt(WaveformPara.N_FFT)/sqrt(N_used)*sqrt(WaveformPara.N_FFT); + +OFDMsymAddCP = [OFDMsym(WaveformPara.N_FFT-CP_Len+1:end,:);OFDMsym]; + +serialOFDMsym = reshape(OFDMsymAddCP,[],1); + +end + +function pilot = generatePilot(pnSequence,Power) + + pilot = pnSequence(); + + bpskModulator = comm.BPSKModulator; + pilot = bpskModulator(pilot)*sqrt(Power); +end diff --git a/TX_transmit.m b/TX_transmit.m new file mode 100644 index 0000000..b70389f --- /dev/null +++ b/TX_transmit.m @@ -0,0 +1,124 @@ +%*********************************lql03.21淇敼********************************* +function [paraCal,tx_data,cfgUI,errorMessage] = TX_transmit(numPkt,SEED,cfgUI,feedback_data,txPSDUfile) +% try +if strcmp(cfgUI.DataType,'photo') + pktLength = cfgUI.user1.APEPlength; + % 鍖呴暱搴︿笌鍖呬釜鏁拌嚜閫傚簲鍒ゆ柇 zjd3.26 + if numPkt*pktLength >= length(txPSDUfile(:,1)) % 濡傛灉褰撳墠鍖呴暱搴﹀湪5涓寘鍐呰兘浼犺緭褰撳墠鏁版嵁閲 + for pktNum = 1:numPkt % 鍒ゆ柇褰撳墠鍖呴暱搴︿笅鏈灏戠敤鍑犱釜鍖呬紶鏁版嵁 + if pktNum*pktLength >= length(txPSDUfile(:,1)) + numPkt = pktNum; + break + end + end + else % 褰撳墠鍖呴暱搴︿笉鑳藉湪5涓寘鍐呰兘浼犺緭褰撳墠鏁版嵁閲忥紝鑷姩璋冩暣 + if numPkt*7007 >= length(txPSDUfile(:,1)) + for pktNum = 1:numPkt % 鍒ゆ柇褰撳墠鍖呴暱搴︿笅鏈灏戠敤鍑犱釜鍖呬紶鏁版嵁 + if pktNum*7007 >= length(txPSDUfile(:,1)) + numPkt = pktNum; + pktLength = 7007; + break + end + end + else + for pktNum = 1:numPkt % 鍒ゆ柇褰撳墠鍖呴暱搴︿笅鏈灏戠敤鍑犱釜鍖呬紶鏁版嵁 + if pktNum*12012 >= length(txPSDUfile(:,1)) + numPkt = pktNum; + pktLength = 12012; + break + end + end + end + end + % 閰嶇疆璁$畻鍚庣殑鍙傛暟 + cfgUI.user1.APEPlength = pktLength; + cfgUI.user2.APEPlength = pktLength; + cfgUI.user1.t = ones(1,numPkt); + cfgUI.user2.t = ones(1,numPkt); +end +if strcmp(cfgUI.FeedbackMode,'feedback') + [cfgHE,RU_index,cfgUI] = parameter_fromAlgorithm(cfgUI,feedback_data);% 鏍规嵁鍙嶉鏁版嵁閰嶇疆RU +else + [cfgHE,RU_index,cfgUI] = parameter_fromUI(cfgUI); % 鍙傛暟涓嶳U閰嶇疆 +end +%*********************************lql03.21淇敼********************************* +showResourceMatrix(cfgHE,cfgUI,RU_index); % 灞曠ず璧勬簮鍒嗛厤鎯呭喌 +txPSDU = DataGenerate(cfgUI,numPkt,SEED,cfgHE,txPSDUfile); +% for i = 1:length(txPSDU) +% txPSDU{i} = HESU_gen(txPSDU,cfgUI.ChannelBandwidth,cfgHE.User{1}.MCS); +% end + + +tx_data = []; +% HE-SU +if strcmp(cfgUI.UserMode,'SU') + txPSDU = txPSDU{1}; +% --------------------------------zjd0323淇敼----------------------- + for pnum = 1 : numPkt + [txPad,cfgHE]=TX_SU(cfgHE,txPSDU(:,pnum)); + tx_awgn = wgn(600,cfgHE.NumTransmitAntennas,-20,'complex'); + tx_data = [tx_data;txPad;tx_awgn]; % 甯ч棿琛ラ浂 + end + tx_data = [tx_awgn(1:50,:);tx_data]; % 甯уご琛ラ浂 + +elseif strcmp(cfgUI.UserMode,'MU') +% HE-MU + if strcmp(cfgUI.FeedbackMode,'feedback') + +%*********************************lql03.21淇敼********************************* + % 琛ュ厖铏氭嫙鐢ㄦ埛鐨刦eedback_data + for nuser = 1 : length(cfgHE.User) + STAID = cfgHE.User{nuser}.STAID; + if STAID == 0 + eval(['Feedback_data.user' num2str(nuser) '= feedback_data.user2']); + else + eval(['Feedback_data.user' num2str(nuser) '= feedback_data.user' num2str(STAID)]); + end + end + staFeedback = cell(1,length(cfgHE.User)); + for userIdx = 1:length(cfgHE.User) + eval(['staFeedback{userIdx} = Feedback_data.user' num2str(userIdx) '.staFeedback;']); + end +%*********************************lql03.21淇敼********************************* + cfgNDP = wlanHESUConfig('APEPLength',0,'GuardInterval',0.8); % No data in an NDP + cfgNDP.NumTransmitAntennas = cfgHE.NumTransmitAntennas;%?? + cfgNDP.NumSpaceTimeStreams = 1;%?? + % For each RU calculate the steering matrix to apply + for ruIdx = 1:numel(cfgHE.RU) + % Calculate the steering matrix to apply to the RU given the feedback + steeringMatrix = heMUCalculateSteeringMatrix(staFeedback,cfgHE,cfgNDP,ruIdx); + % Apply the steering matrix to each RU + cfgHE.RU{ruIdx}.SpatialMapping = 'Custom'; + cfgHE.RU{ruIdx}.SpatialMappingMatrix = steeringMatrix; + end + elseif strcmp(cfgUI.FeedbackMode,'openloop') + for ruIdx = 1:numel(cfgHE.RU) + % Apply the steering matrix to each RU +% cfgHE.RU{ruIdx}.SpatialMapping = cfgUI.user1.SpatialMapping;%% --??? + cfgHE.RU{ruIdx}.SpatialMapping = 'Hadamard'; + end + end + % Generate waveform with idle period + for pnum = 1 : numPkt + userString = '{'; + for nuser = 1 : length(cfgHE.User) + userString = [userString 'txPSDU{' num2str(nuser) '}(:,pnum),']; + end + txPSDUtmp = eval([userString(1:(end-1)) '}']); %%------------闇娣诲姞CRC + tx = wlanWaveformGenerator(txPSDUtmp,cfgHE); + tx_awgn = wgn(600,cfgHE.NumTransmitAntennas,-20,'complex'); +% awgn(zeros(600,cfgHE.NumTransmitAntennas),20,'linear',1,'all'); + tx_data = [tx_data;tx;tx_awgn]; % 甯ч棿琛ラ浂 + end + tx_data = [tx_awgn(1:50,:);tx_data]; % 甯уご琛ラ浂 +% tx_data = awgn(tx_data,30,mean(abs(tx_data(1:1000)).^2)); % 娣诲姞鍣0 +end +paraCal = calculate_PAPR(tx_data(1500:2500,:)); +errorMessage = ''; +% catch ErrorInfo +% paraCal = 0; +% tx_data = []; +% cfgUI = struct(); +% errorMessage = ErrorInfo.message; +% end +end \ No newline at end of file diff --git a/WaveAdp/ChanStat.m b/WaveAdp/ChanStat.m new file mode 100644 index 0000000..4c6f648 --- /dev/null +++ b/WaveAdp/ChanStat.m @@ -0,0 +1,28 @@ +function ChanPara = ChanStat(SNR, tau_d, velocity, Ts, FreqCarrier, TimeStamp) + A_dB = -15;norm_flag = 1;c = 3e8; + ChanPara.PDP = exp_PDP(tau_d,Ts,A_dB,norm_flag); + ChanPara.delay = ones(1,length(ChanPara.PDP))*Ts; + ChanPara.rmsdelay = tau_d; + ChanPara.doppler = 'Jakes'; + ChanPara.Velocity = velocity; + ChanPara.epsilon = 0; + ChanPara.SNR = SNR; + ChanPara.FreqCarrier = FreqCarrier; + ChanPara.MaximumDopplerShift = FreqCarrier/c * velocity/3600 *1000; + ChanPara.SampleFs = 1/Ts; +end + + +function PDP = exp_PDP(tau_d,Ts,A_dB,norm_flag) +if nargin<4, norm_flag=1; end % normalizes +if nargin<3, A_dB=-20; end % 20dB below +sigma_tau = tau_d; A = 10^(A_dB/10); +lmax=ceil(-tau_d*log(A)/Ts); % get max. path index (2.8)/Ts +% Computes normalization factor for power normalization +if norm_flag + p0=(1-exp(-Ts/sigma_tau))/(1-exp(-(lmax+1)*Ts/sigma_tau)); % (2.10) + else p0=1/sigma_tau; +end +% Exponential PDP +l=0:lmax; PDP = p0*exp(-l*Ts/sigma_tau); % (2.11) +end diff --git a/WaveAdp/ExecutedMAIN0701.m b/WaveAdp/ExecutedMAIN0701.m new file mode 100644 index 0000000..e0adb96 --- /dev/null +++ b/WaveAdp/ExecutedMAIN0701.m @@ -0,0 +1,61 @@ +%% +function [BER,PER,SyncErr] = ExecutedMAIN0701(PacketSize, NumerologySel, MCS_no, ChanPara, FreqCarrier, FrameNo, seed_set) + [SysParameters,WaveformPara] = WaveformNumerologyCal(PacketSize,NumerologySel,MCS_no); + %% TX + rng(seed_set(1),'twister'); + dataPacketBits = randi([0 1],WaveformPara.Payload,1); + scrData = TX_CRC32_Scramble(dataPacketBits); + trellis = poly2trellis(7,[171 133]); + tx_encoded_bit = convenc(scrData,trellis,WaveformPara.puncpat); + tx_padded_bit = [tx_encoded_bit;zeros(WaveformPara.DataPadding,1)]; + %--------------------------------------------- + dataMod = qammod(tx_padded_bit,WaveformPara.Mod,'InputType','bit'); + WaveformPara.Power_sym = mean(abs(dataMod).^2); + dataMod = reshape(dataMod,WaveformPara.UsedSubcarrier,[]); + %% 鑾峰彇淇¢亾鍝嶅簲 %鍋囪宸茬煡鍓嶄竴鏃跺埢鐨勪俊鍙 + % 鐢熸垚淇¢亾鍝嶅簲 + N_carriers = WaveformPara.N_FFT; + channel_response=sqrt(1/2)*(randn(1,N_carriers)+1i*randn(1,N_carriers)); + % 绐楃殑鏁扮洰 + N_windows = 8; + % 瀹氫箟閲忓寲姘村钩鎬绘暟 + N_level = 10; + % 鏈灏廋P鍊硷紝澶栭儴鏉′欢锛屽彇鍐充簬鏈澶у欢杩熸嫇灞曪紝鏈夋渶灏廋P鍊>鏈澶у欢杩熸嫇灞 + min_len_CP = 30; + % 娴嬭瘯鍑芥暟 + CPset = Generate_CP(channel_response,N_windows,N_level,min_len_CP); + + % 鐗╃悊灞傚姞瀵 + [serialOFDMsym,WaveformPara.pilot,myFrameLen] = tx_FFTSymbolGen_PilotBlockPN(dataMod,WaveformPara,CPset); % 鍔燙P鐨勫湴鏂 + WaveformPara.Power_sig = mean(abs(serialOFDMsym).^2); + [WaveformPara.preamble,WaveformPara.c1,WaveformPara.c2] = ... + generatePreamble(WaveformPara,WaveformPara.Power_sig); % hb:杩欓噷鍓嶅鐮佺殑鐩殑鏄粈涔堬紵 + txSignal = [WaveformPara.preamble;serialOFDMsym]; +% RFSignal = RFimpairment(txSignal, FreqCarrier); + +% 鍒嗕袱鍧楋紝鍙戦 鎺ユ敹 + %% Channel + [rxSignal,ChanPara] = PassChan(txSignal,WaveformPara,SysParameters,ChanPara,seed_set(2)); + +% SIR = InterfCal(rxSignal,ChanPara); + + + %% RX + [rxSignal_sync,SyncErr] = rxSyncSC(rxSignal,WaveformPara,myFrameLen); + [rxDataModLs] = rx_PilotBlockChanEst(rxSignal_sync,ChanPara,WaveformPara,CPset,SysParameters); % 鍘籆P鐨勫湴鏂 + dataDemod = qamdemod(rxDataModLs,WaveformPara.Mod,'OutputType','approxllr', ... + 'UnitAveragePower',false,'NoiseVariance',ChanPara.N0); + dataDePadded = dataDemod(1:end - WaveformPara.DataPadding); + tb = 8; + decoded = vitdec(dataDePadded,trellis,tb,'trunc','unquant',WaveformPara.puncpat); + + [rx,PER] = RX_CRC32_deScramble(decoded); + [numErrors,BER] = biterr(dataPacketBits,rx); +end + +%% + + + + + diff --git a/WaveAdp/Generate_CP.m b/WaveAdp/Generate_CP.m new file mode 100644 index 0000000..69d8bdd --- /dev/null +++ b/WaveAdp/Generate_CP.m @@ -0,0 +1,73 @@ +function [len_CP] = Generate_CP(channel_response,N_windows,N_level,min_len_CP) +% 浜х敓CP闀垮害鐨勫嚱鏁 +% 鍦ㄨ鏂规硶涓紝鍋囪灏嗚鍙戦佺殑OFDM绗﹀彿鐨勬暟閲忕瓑浜嶩_bs涓獥鐨勬暟鐩 +% 杈撳叆channel_response 淇¢亾鍝嶅簲锛汵_windows 闇瑕佸垝绐楃殑涓暟锛汵_level 閲忓寲绛夌骇锛 +% min_len_CP锛屾渶灏廋P鍙栧喅浜庢渶澶у欢杩熸嫇灞曪紝鏈夋渶灏廋P鍊>鏈澶у欢杩熸嫇灞 +% 杈撳嚭CP鐨勯暱搴﹀悜閲忥紝鍏堕暱搴︾瓑浜庣獥鐨勪釜鏁 + +% 瀛愯浇娉釜鏁 +N_carriers = length(channel_response); +% 绐楃殑闀垮害 +Len_windows = N_carriers / N_windows; + +% 瀵逛俊閬撳鐩婅繘琛岄檷搴忔帓搴忓苟璁板綍绱㈠紩 +[channel_gain, sorted_indices] = sort(abs(channel_response), 'descend');% +G_max = max(channel_gain); +G_min = min(channel_gain); + +% 璺濈姘村钩 +D_L = (G_max-G_min)/N_level; + +% 璁$畻姣忎釜瀛愯浇娉㈠搴旂殑绛夌骇 +level_k = zeros(1,N_carriers); +for i = 1:N_carriers + % 杈圭晫鍊煎鐞 + if channel_gain(i) <= G_min + level_k(i) = 1; + elseif channel_gain(i) >= G_max + level_k(i) = N_level; + % 鏍规嵁寮(3)鐨勫彉褰紝鍙互寰楀埌鏉冮噸绛夌骇 + else + level_k(i) = ceil((channel_gain(i) - G_min)/D_L); + end +end + +% 鏍规嵁寮(2)璁$畻瀛愯浇娉㈡潈鍊 +weight_k = 2*level_k-1; + +% 瀵规瘡涓獥杩涜璁$畻锛岃幏寰楄绐椾笅瀵瑰簲鐨凜P闀垮害 +weight_matrix = zeros(N_windows,Len_windows); +choose_matrix = zeros(N_windows,Len_windows); +len_CP = zeros(1,N_windows); +for i = 1:N_windows + + weight_matrix(i,:) = weight_k((i-1)*Len_windows+1:i*Len_windows);% 灏嗘潈閲嶅悜閲忚浆鍙樹负鏉冮噸鐭╅樀锛屽叾涓瘡涓琛屼负姣忎竴涓獥鐨勬潈閲 + current_CP = weight_matrix(i,1);% 涓存椂鍙橀噺锛岀敤浜庢潈閲嶇浉鍔 + choose_matrix(i,1) = current_CP;% 閫夋嫨鐭╅樀锛岃褰曠浉鍔犳潈閲嶇殑鍊 + for j = 2:Len_windows + % 绱姞鏉冮噸锛岃繘琛屽垽鏂 + if current_CP <= min_len_CP + current_CP = current_CP + weight_matrix(i,j); + choose_matrix(i,j) = weight_matrix(i,j); + % 杈圭晫鏉′欢澶勭悊 + if j == Len_windows + if current_CP <= min_len_CP % 浠嶆湭婊¤冻鏉′欢 + len_CP(i) = min_len_CP; + break; + else + len_CP(i) = current_CP;% 婊¤冻鏉′欢 + break; + end + end + + % current_CP > min_len_CP锛岀洿鎺ヨ祴鍊 + else + len_CP(i) = current_CP; + break; + end + end + +end + + +end \ No newline at end of file diff --git a/WaveAdp/NumerologySet.mat b/WaveAdp/NumerologySet.mat new file mode 100644 index 0000000..25aeb00 Binary files /dev/null and b/WaveAdp/NumerologySet.mat differ diff --git a/WaveAdp/PassChan.m b/WaveAdp/PassChan.m new file mode 100644 index 0000000..6251448 --- /dev/null +++ b/WaveAdp/PassChan.m @@ -0,0 +1,18 @@ +function [chanOut,ChanPara] = PassChan(dataIn,WaveformPara,SysParameters,ChanPara,seed) + +Chan = comm.RayleighChannel(... + 'SampleRate',ChanPara.SampleFs, ... + 'PathDelays', ChanPara.delay, ... + 'AveragePathGains', ChanPara.PDP, ... + 'NormalizePathGains',true, ... + 'MaximumDopplerShift',floor(ChanPara.MaximumDopplerShift), ... + 'DopplerSpectrum',doppler(ChanPara.doppler), ... + 'PathGainsOutputPort',SysParameters.ShowPathGain); + [rAddMultipath,~] = Chan(dataIn); +% rAddCFO = rAddMultipath.*exp(sqrt(-1)*2*pi*(1:length(rAddMultipath))'*ChanPara.epsilon/N_FFT); + SNR = ChanPara.SNR; + ChanPara.N0 = WaveformPara.Power_sig/10^(SNR/10); + rAddAWGN = awgn(rAddMultipath,SNR,'measured'); + chanOut = [sqrt(ChanPara.N0)*(randn(400,1)+1i*randn(400,1));rAddAWGN]; + +end \ No newline at end of file diff --git a/WaveAdp/RX.m b/WaveAdp/RX.m new file mode 100644 index 0000000..6471c15 --- /dev/null +++ b/WaveAdp/RX.m @@ -0,0 +1,115 @@ +%% RX for labview +clc; +close all; +clear ; +%% 鎺ユ敹绔緭鍏ユ祴璇曟暟鎹 +receiveAntennasNum = 1; +receiveBandwidth = 20e6; +syncAlgo = 'first'; +chanEstiAlgo = 'first'; +equalizeAlgo = 'first'; +demodMode = 'first'; +interpCodeAlgo = 'first'; + +%% 鎺ユ敹绔緭鍏ョ鎺ュ彛 +rxInput.receiveAntennasNum = receiveAntennasNum; % 1 or 2 +rxInput.receiveBandwidth = receiveBandwidth;% 10~20M +rxInput.syncAlgo = syncAlgo;% 鎺ュ彛锛岃緭鍏ョ被鍨嬩负string +rxInput.chanEstiAlgo = chanEstiAlgo;% 鎺ュ彛锛岃緭鍏ョ被鍨嬩负string +rxInput.equalizeAlgo = equalizeAlgo;% 鎺ュ彛锛岃緭鍏ョ被鍨嬩负string +rxInput.demodMode = demodMode;% 鎺ュ彛锛岃緭鍏ョ被鍨嬩负string +rxInput.interpCodeAlgo = interpCodeAlgo;% 鎺ュ彛锛岃緭鍏ョ被鍨嬩负string + + +%% 娴嬭瘯鏁版嵁锛屽彂閫佺娉㈠舰鍙傛暟 +userNum = 1; +fileType = 'text'; +encryptMode = 'encryptMode'; +antennasNum = 1; +centerFreq = 2450e6; +sendBandwidth = 5e6; +fftNum = 128; +CPratio = 0.25; +pilotInterval = 3; +% MCS_set=[2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6];% 鍔犲瘑绠楁硶闇瑕佸仛涓涓傞厤锛孋HB锛3.19 +MCS = [16,4]; +subcarriersNum = 128; % 寰呴傞厤 + +%% 鎺ユ敹绔 璁$畻鍙戦佺鐨勪竴鍒囩姸鎬 +txInput.userNum = userNum; % 1 or 2 +txInput.fileType = fileType; % 'text' or 'picture' + +txInput.encryptMode = encryptMode; % 'encrypt' or 'not encrypt' +txInput.antennasNum = antennasNum; % 1 or 2 +txInput.centerFreq = centerFreq; % 500MHz~2.4G +txInput.sendBandwidth = sendBandwidth; % 20~40MHz +txInput.fftNum = fftNum;% 128 or 256 or 512 +txInput.CPratio = CPratio; % 0.25 or 0.125 or 0.0625 +txInput.pilotInterval = pilotInterval; % 3 5 7 +txInput.MCS = MCS; % a set [2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6] +txInput.subcarriersNum = subcarriersNum;% 52 or 106 or 242 + +PacketSize = 200; +NumerologySel = [txInput.fftNum,txInput.CPratio,txInput.pilotInterval]; +MCS_no = txInput.MCS; + +[SysParameters,WaveformPara] = WaveformNumerologyCal(PacketSize,NumerologySel,MCS_no); + +seed_set = [1 2 3]; +rng(seed_set(1),'twister'); +dataPacketBits = randi([0 1],WaveformPara.Payload,1); +scrData = TX_CRC32_Scramble(dataPacketBits); +trellis = poly2trellis(7,[171 133]); +tx_encoded_bit = convenc(scrData,trellis,WaveformPara.puncpat); +tx_padded_bit = [tx_encoded_bit;zeros(WaveformPara.DataPadding,1)]; +%--------------------------------------------- +dataMod = qammod(tx_padded_bit,WaveformPara.Mod,'InputType','bit'); +WaveformPara.Power_sym = mean(abs(dataMod).^2); +dataMod = reshape(dataMod,WaveformPara.UsedSubcarrier,[]); + +%% 鑾峰彇淇¢亾鍝嶅簲 %鍋囪宸茬煡鍓嶄竴鏃跺埢鐨勪俊鍙 + % 鐢熸垚淇¢亾鍝嶅簲 +N_carriers = WaveformPara.N_FFT; +channel_response=sqrt(1/2)*(randn(1,N_carriers)+1i*randn(1,N_carriers)); +N_windows = 8; +N_level = 10; +min_len_CP = 30; +CPset = Generate_CP(channel_response,N_windows,N_level,min_len_CP); +[serialOFDMsym,WaveformPara.pilot,myFrameLen] = tx_FFTSymbolGen_PilotBlockPN(dataMod,WaveformPara,CPset); +WaveformPara.Power_sig = mean(abs(serialOFDMsym).^2); +[WaveformPara.preamble,WaveformPara.c1,WaveformPara.c2] = ... + generatePreamble(WaveformPara,WaveformPara.Power_sig); +%% 杈撳叆鏁版嵁锛屾帴鏀剁 + +load('rxdata5normalized.mat') + +% rxSignal = rxSignal(5382:8680)'; +% rxSignal = downsample(rxSignal, 2)';%data1 part + +% rxSignal = rxSignal(5128:7889)';%data2 part + +rxSignal = rxSignal(8500:10500)'; + +[rxSignal_sync,SyncErr] = rxSyncSC(rxSignal,WaveformPara,myFrameLen); +[rxDataModLs] = rx_PilotBlockChanEstForLabview(rxSignal_sync,WaveformPara,CPset); % 鍘籆P鐨勫湴鏂 +dataDemod = qamdemod(rxDataModLs,WaveformPara.Mod,'OutputType','approxllr', ... + 'UnitAveragePower',false,'NoiseVariance',0.0243);%ChanPara.N0 +dataDePadded = dataDemod(1:end - WaveformPara.DataPadding); +tb = 8; +decoded = vitdec(dataDePadded,trellis,tb,'trunc','unquant',WaveformPara.puncpat); + +[rxBits,PER] = RX_CRC32_deScramble(decoded);% 涓㈠寘鐜 +[numErrors,BER] = biterr(dataPacketBits,rxBits); + +%% 鎺ユ敹绔緭鍑烘帴鍙 +rxOutput.delaySpread = 0; % 鎺ュ彛锛岃緭鍑虹被鍨嬩负double +rxOutput.SNR = 0;% 鎺ュ彛锛岃緭鍑虹被鍨嬩负double +rxOutput.RSSI = 0;% 鎺ュ彛锛岃緭鍑虹被鍨嬩负double +rxOutput.BER = BER;% 杈撳嚭绫诲瀷涓篸ouble +rxOutput.PER = PER;% 杈撳嚭绫诲瀷涓篸ouble +rxOutput.EVM = 0;% 鎺ュ彛锛岃緭鍑虹被鍨嬩负double +rxOutput.phaseNoise = 0;% 鎺ュ彛锛岃緭鍑虹被鍨嬩负double + +BER +PER +SyncErr \ No newline at end of file diff --git a/WaveAdp/RX_CRC32_deScramble.m b/WaveAdp/RX_CRC32_deScramble.m new file mode 100644 index 0000000..296b577 --- /dev/null +++ b/WaveAdp/RX_CRC32_deScramble.m @@ -0,0 +1,15 @@ +function [rx,frmError] = RX_CRC32_deScramble(dataPacketBits) + +descrambler = comm.Descrambler(2,[1 0 0 0 0 0 1 0 1], ... + zeros(8,1)); +deScrData = descrambler(dataPacketBits); + +poly = [32,26,23,22,16,12,11,10,8,7,5,4,2,1,0]; +crcdetector = comm.CRCDetector(... + 'Polynomial', poly, ... + 'InitialConditions', 1, ... + 'DirectMethod', true, ... + 'FinalXOR', 1); +[rx,frmError] = crcdetector(deScrData); + +end diff --git a/WaveAdp/RX_forPreamble.m b/WaveAdp/RX_forPreamble.m new file mode 100644 index 0000000..235c244 --- /dev/null +++ b/WaveAdp/RX_forPreamble.m @@ -0,0 +1,124 @@ +%% RX for labview +clc; +close all; +clear ; +%% 鎺ユ敹绔緭鍏ユ祴璇曟暟鎹 +receiveAntennasNum = 1; +receiveBandwidth = 20e6; +syncAlgo = 'first'; +chanEstiAlgo = 'first'; +equalizeAlgo = 'first'; +demodMode = 'first'; +interpCodeAlgo = 'first'; + +%% 鎺ユ敹绔緭鍏ョ鎺ュ彛 +rxInput.receiveAntennasNum = receiveAntennasNum; % 1 or 2 +rxInput.receiveBandwidth = receiveBandwidth;% 10~20M +rxInput.syncAlgo = syncAlgo;% 鎺ュ彛锛岃緭鍏ョ被鍨嬩负string +rxInput.chanEstiAlgo = chanEstiAlgo;% 鎺ュ彛锛岃緭鍏ョ被鍨嬩负string +rxInput.equalizeAlgo = equalizeAlgo;% 鎺ュ彛锛岃緭鍏ョ被鍨嬩负string +rxInput.demodMode = demodMode;% 鎺ュ彛锛岃緭鍏ョ被鍨嬩负string +rxInput.interpCodeAlgo = interpCodeAlgo;% 鎺ュ彛锛岃緭鍏ョ被鍨嬩负string + + +%% 娴嬭瘯鏁版嵁锛屽彂閫佺娉㈠舰鍙傛暟 +userNum = 1; +fileType = 'text'; +encryptMode = 'encryptMode'; +antennasNum = 1; +centerFreq = 2450e6; +sendBandwidth = 5e6; +fftNum = 128; +CPratio = 0.25; +pilotInterval = 3; +% MCS_set=[2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6];% 鍔犲瘑绠楁硶闇瑕佸仛涓涓傞厤锛孋HB锛3.19 +MCS = [16,4]; +subcarriersNum = 128; % 寰呴傞厤 + +%% 鎺ユ敹绔 璁$畻鍙戦佺鐨勪竴鍒囩姸鎬 +txInput.userNum = userNum; % 1 or 2 +txInput.fileType = fileType; % 'text' or 'picture' + +txInput.encryptMode = encryptMode; % 'encrypt' or 'not encrypt' +txInput.antennasNum = antennasNum; % 1 or 2 +txInput.centerFreq = centerFreq; % 500MHz~2.4G +txInput.sendBandwidth = sendBandwidth; % 20~40MHz +txInput.fftNum = fftNum;% 128 or 256 or 512 +txInput.CPratio = CPratio; % 0.25 or 0.125 or 0.0625 +txInput.pilotInterval = pilotInterval; % 3 5 7 +txInput.MCS = MCS; % a set [2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6] +txInput.subcarriersNum = subcarriersNum;% 52 or 106 or 242 + +PacketSize = 200; +NumerologySel = [txInput.fftNum,txInput.CPratio,txInput.pilotInterval]; +MCS_no = txInput.MCS; + +[SysParameters,WaveformPara] = WaveformNumerologyCal(PacketSize,NumerologySel,MCS_no); + +seed_set = [1 2 3]; +rng(seed_set(1),'twister'); +dataPacketBits = randi([0 1],WaveformPara.Payload,1); +scrData = TX_CRC32_Scramble(dataPacketBits); +trellis = poly2trellis(7,[171 133]); +tx_encoded_bit = convenc(scrData,trellis,WaveformPara.puncpat); +tx_padded_bit = [tx_encoded_bit;zeros(WaveformPara.DataPadding,1)]; +%--------------------------------------------- +dataMod = qammod(tx_padded_bit,WaveformPara.Mod,'InputType','bit'); +WaveformPara.Power_sym = mean(abs(dataMod).^2); +dataMod = reshape(dataMod,WaveformPara.UsedSubcarrier,[]); + +%% 鑾峰彇淇¢亾鍝嶅簲 %鍋囪宸茬煡鍓嶄竴鏃跺埢鐨勪俊鍙 + % 鐢熸垚淇¢亾鍝嶅簲 +N_carriers = WaveformPara.N_FFT; +channel_response=sqrt(1/2)*(randn(1,N_carriers)+1i*randn(1,N_carriers)); +N_windows = 8; +N_level = 10; +min_len_CP = 30; +CPset = Generate_CP(channel_response,N_windows,N_level,min_len_CP); +[serialOFDMsym,WaveformPara.pilot,serialFrameLen] = tx_FFTSymbolGen_PilotBlockPN(dataMod,WaveformPara,CPset); +WaveformPara.Power_sig = mean(abs(serialOFDMsym).^2); +[WaveformPara.preamble,WaveformPara.c1,WaveformPara.c2] = ... + generatePreamble(WaveformPara,WaveformPara.Power_sig); +%% 杈撳叆鏁版嵁锛屾帴鏀剁 +% load('preambleData.mat') +load('rxdata6normalizedzero.mat') + +% A = 20*log10(abs(fft(rxSignal))); +% plot(A); +% rxSignal = rxSignal(5382:8680)'; +% rxSignal = downsample(rxSignal, 2)';%data1 part + +% rxSignal = rxSignal(5128:7889)';%data2 part + +% rxSignal = rxSignal(4500+370:7000)';%data4 part + +rxSignal = rxSignal(14000:17000)'/sqrt(mean(abs(rxSignal(14000:17000).^2))); +rxSignal=awgn(rxSignal,20,mean(abs(rxSignal.^2))); +% rxSignal = txSignalNormalized; +% [rxSignal_sync,SyncErr] = rxSyncSC(rxSignal,WaveformPara,myFrameLen); + +[rxSignal_sync,MCS,PSDULength,failCheck,rec_frameNo,estNumerlogy,numeroError] = rx_preambleprocess(rxSignal); +rxSignal_sync = rxSignal_sync(1:serialFrameLen); + +[rxDataModLs] = rx_PilotBlockChanEstForLabview(rxSignal_sync,WaveformPara,CPset); % 鍘籆P鐨勫湴鏂 +dataDemod = qamdemod(rxDataModLs,WaveformPara.Mod,'OutputType','approxllr', ... + 'UnitAveragePower',false,'NoiseVariance',0.0243);%ChanPara.N0 +dataDePadded = dataDemod(1:end - WaveformPara.DataPadding); +tb = 8; +decoded = vitdec(dataDePadded,trellis,tb,'trunc','unquant',WaveformPara.puncpat); + +[rxBits,PER] = RX_CRC32_deScramble(decoded);% 涓㈠寘鐜 +[numErrors,BER] = biterr(dataPacketBits,rxBits); + +%% 鎺ユ敹绔緭鍑烘帴鍙 +rxOutput.delaySpread = 0; % 鎺ュ彛锛岃緭鍑虹被鍨嬩负double +rxOutput.SNR = 0;% 鎺ュ彛锛岃緭鍑虹被鍨嬩负double +rxOutput.RSSI = 0;% 鎺ュ彛锛岃緭鍑虹被鍨嬩负double +rxOutput.BER = BER;% 杈撳嚭绫诲瀷涓篸ouble +rxOutput.PER = PER;% 杈撳嚭绫诲瀷涓篸ouble +rxOutput.EVM = 0;% 鎺ュ彛锛岃緭鍑虹被鍨嬩负double +rxOutput.phaseNoise = 0;% 鎺ュ彛锛岃緭鍑虹被鍨嬩负double + +BER +PER +% SyncErr \ No newline at end of file diff --git a/WaveAdp/STS_M32Q16.mat b/WaveAdp/STS_M32Q16.mat new file mode 100644 index 0000000..a32de85 Binary files /dev/null and b/WaveAdp/STS_M32Q16.mat differ diff --git a/WaveAdp/STS_demod.m b/WaveAdp/STS_demod.m new file mode 100644 index 0000000..29e300c --- /dev/null +++ b/WaveAdp/STS_demod.m @@ -0,0 +1,68 @@ +function [estNumerlogy,frmError] = STS_demod(rx_STF) +M=32;Q=16;OVR=1; +load STS_M32Q16.mat theta_mod v_mod STS_freq + +STS_NUM = 9; +RX_re = reshape(rx_STF(17:end),[],STS_NUM); +STS_f = fftshift(fft(RX_re,16,1)); +STS_freq_sk = STS_f([3:8,10:15],:); +theta_est = mod(angle(STS_freq_sk(2:end,:)./STS_freq_sk(1:end-1,:)),2*pi); + +mmse_est = zeros(Q,STS_NUM); +for iii = 1:STS_NUM + for ii = 1:Q + mmse_tmp = abs(theta_est(:,iii)-theta_mod(ii,:).'); + mmse_est(ii,iii) = norm(min([mmse_tmp,2*pi - mmse_tmp],[],2))^2; + end +end +mmse_est2 = sum(mmse_est,2); +v_est_no = find(mmse_est2==min(mmse_est2)); +v_est = exp(j*v_mod(v_est_no(1))); +%%------demodulate phi +STS_freq_sk = STS_freq_sk./abs(STS_freq_sk); +% [~,STS_ref] = preamble_modu1(theta_mod(v_est_no(1),:), 0, OVR); +STS_ref = STS_freq(:,v_est_no(1),1); +STS_ref = STS_ref./abs(STS_ref); +phi_est_angle = mod(angle(sum(sum((STS_freq_sk(:,1:STS_NUM)./STS_ref)))),2*pi); +phi_est = exp(j*phi_est_angle); + +% demap v and phi to Bits using pskmod +v_data_est = pskdemod(v_est,Q,0,'gray'); +dePreambleQ = de2bi(v_data_est,log2(Q)); + +phi_data_est = pskdemod(phi_est,M,0,'gray'); +dePreambleM = de2bi(phi_data_est,log2(M)); + +% CRC +poly = 'z4+z3+z2+z+1'; +crcdetector = comm.CRCDetector(poly); +codeword = [dePreambleM,dePreambleQ]; +[msg,frmError] = crcdetector(codeword.'); +estNumerlogy = bi2de(msg.')+1; + +end + +%% +function [STS_time,STS_freq] = preamble_modu1(theta, phi, OVR) +% N_FFT =64; +s_k(1) = 1*exp(j*phi); +s_k(1) = sqrt(2)/abs(s_k(1))*s_k(1); +for i = 1:11 + s_k(i+1) = s_k(i)*exp(j*theta(i)); +end +STS_LOC = [9,13,17,21,25,29,37,41,45,49,53,57]; +Short_preamble_slot_Frequency = zeros(1,64); % [1x64] +Short_preamble_slot_Frequency(STS_LOC) = s_k; +STS_freq = ifftshift(Short_preamble_slot_Frequency); +STS = ifft([STS_freq(1:32),zeros(1,64*(OVR-1)),STS_freq(33:64)])*sqrt(64); +STS = sqrt(1/mean(abs(STS).^2))*STS; + +STS_f = fft(STS); +STS_f = [STS_f(1:32),STS_f((end-31:end))]; +STS_freq = fftshift(STS_f); + +STS_time = STS(1:16*OVR); +STS_freq = STS_freq(STS_LOC); +% STS_dpsk = mod(angle(STS_freq((2:end))./STS_freq((1:end-1))),2*pi); +% STS_dpsk(theta==0) = min([STS_dpsk(theta==0);2*pi - STS_dpsk(theta==0)],[],1); +end \ No newline at end of file diff --git a/WaveAdp/STS_modulation.m b/WaveAdp/STS_modulation.m new file mode 100644 index 0000000..7cf06e6 --- /dev/null +++ b/WaveAdp/STS_modulation.m @@ -0,0 +1,28 @@ +function tx_STF = STS_modulation(Numerology) +% total 9bits 5 for Numerology 4 for crc +% input is the No. of Numerology 0~26 + +preamblebit = de2bi(Numerology-1,5); + +% CRC +poly = 'z4+z3+z2+z+1'; +crcgenerator = comm.CRCGenerator(poly); +codeword = crcgenerator(preamblebit.'); +crccode = codeword(6:end).'; +% preamble mod +M=32;Q=16; +theta0 = [0 0 0 pi 0 pi pi 0 pi pi pi]; +phi_mod = [0:1/M*2*pi:2*pi-1/M*2*pi]; +v_mod = [0:1/Q*2*pi:2*pi-1/Q*2*pi]; + +load STS_M32Q16.mat STS_mod + +phi_data = pskmod(bi2de(preamblebit),M,0,'gray');% for M +v_data = pskmod(bi2de(crccode),Q,0,'gray');% for Q + +[~,phi_no] = min(abs(phi_mod-mod(angle(phi_data),2*pi))); +[~,v_no] = min(abs(v_mod-mod(angle(v_data),2*pi))); +STF = STS_mod(:,v_no,phi_no); +tx_STF = repmat(STF,10,1); + +end diff --git a/WaveAdp/TX.m b/WaveAdp/TX.m new file mode 100644 index 0000000..39eb125 --- /dev/null +++ b/WaveAdp/TX.m @@ -0,0 +1,110 @@ +%% TX for labview +clc; +close all; +clear ; + +%% 娴嬭瘯鏁版嵁锛岃緭鍏 +userNum = 1; +fileType = 'text'; +encryptMode = 'encryptMode'; +antennasNum = 1; +centerFreq = 2450e6; +sendBandwidth = 5e6; +fftNum = 128; +CPratio = 0.25; +pilotInterval = 3; +% MCS_set=[2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6];% 鍔犲瘑绠楁硶闇瑕佸仛涓涓傞厤锛孋HB锛3.19 +MCS = [16,4]; +subcarriersNum = 128; % 寰呴傞厤 + + +%% 鎺ュ彛 +txInput.userNum = userNum; % 1 or 2 +txInput.fileType = fileType; % 'text' or 'picture' + +txInput.encryptMode = encryptMode; % 'encrypt' or 'not encrypt' +txInput.antennasNum = antennasNum; % 1 or 2 +txInput.centerFreq = centerFreq; % 500MHz~2.4G +txInput.sendBandwidth = sendBandwidth; % 20~40MHz +txInput.fftNum = fftNum;% 128 or 256 or 512 +txInput.CPratio = CPratio; % 0.25 or 0.125 or 0.0625 +txInput.pilotInterval = pilotInterval; % 3 5 7 +txInput.MCS = MCS; % a set [2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6] +txInput.subcarriersNum = subcarriersNum;% 52 or 106 or 242 + +%% 浜х敓鍙戦佹暟鎹 + +TimeStamp = []; +FreqCarrier = txInput.centerFreq;% 涓績棰戠偣鍙皟 +Bandwidth = txInput.sendBandwidth;% 甯﹀鍙皟 20~40e6 +Ts = 1/Bandwidth; +% 浠跨湡鍙傛暟 +Delay_set = [1:20]*Ts; +Velocity_set = [5:250]; +SNR_set = [0:5:35]; + +FrameNum = 1; +PacketSize = 200; % + +% load NumerologySet.mat NumerologySet +% NumerologySel = NumerologySet(1,:); % 128 0.25 3 % N_FFT; CPratio; PilotInterval +NumerologySel = [txInput.fftNum,txInput.CPratio,txInput.pilotInterval]; +MCS_no = txInput.MCS; + + + +SNR = 25; +tau_d = Delay_set(randi([1 20])); +velocity = Velocity_set(randi([1 length(Velocity_set)])); +ChanPara = ChanStat(SNR, tau_d, velocity, Ts, FreqCarrier, TimeStamp); + +% seed_set = randi([0 5000],FrameNum,3); +seed_set = [1 2 3]; + +[SysParameters,WaveformPara] = WaveformNumerologyCal(PacketSize,NumerologySel,MCS_no); +%% TX +rng(seed_set(1),'twister'); +dataPacketBits = randi([0 1],WaveformPara.Payload,1); +scrData = TX_CRC32_Scramble(dataPacketBits); +trellis = poly2trellis(7,[171 133]); +tx_encoded_bit = convenc(scrData,trellis,WaveformPara.puncpat); +tx_padded_bit = [tx_encoded_bit;zeros(WaveformPara.DataPadding,1)]; +%--------------------------------------------- +dataMod = qammod(tx_padded_bit,WaveformPara.Mod,'InputType','bit'); +WaveformPara.Power_sym = mean(abs(dataMod).^2); +dataMod = reshape(dataMod,WaveformPara.UsedSubcarrier,[]); +%% 鑾峰彇淇¢亾鍝嶅簲 %鍋囪宸茬煡鍓嶄竴鏃跺埢鐨勪俊鍙 + % 鐢熸垚淇¢亾鍝嶅簲 +N_carriers = WaveformPara.N_FFT; +channel_response=sqrt(1/2)*(randn(1,N_carriers)+1i*randn(1,N_carriers)); +% 绐楃殑鏁扮洰 +N_windows = 8; +% 瀹氫箟閲忓寲姘村钩鎬绘暟 +N_level = 10; +% 鏈灏廋P鍊硷紝澶栭儴鏉′欢锛屽彇鍐充簬鏈澶у欢杩熸嫇灞曪紝鏈夋渶灏廋P鍊>鏈澶у欢杩熸嫇灞 +min_len_CP = 30; +% 娴嬭瘯鍑芥暟 +CPset = Generate_CP(channel_response,N_windows,N_level,min_len_CP); + +% 鐗╃悊灞傚姞瀵 +[serialOFDMsym,WaveformPara.pilot,myFrameLen] = tx_FFTSymbolGen_PilotBlockPN(dataMod,WaveformPara,CPset); % 鍔燙P鐨勫湴鏂 +WaveformPara.Power_sig = mean(abs(serialOFDMsym).^2); +[WaveformPara.preamble,WaveformPara.c1,WaveformPara.c2] = ... + generatePreamble(WaveformPara,WaveformPara.Power_sig); % hb:杩欓噷鍓嶅鐮佺殑鐩殑鏄粈涔堬紵 +txSignal = [WaveformPara.preamble;serialOFDMsym]; +txSignalNormalized = txSignal./max(abs(txSignal)); + +txOutput.sendSignal = txSignalNormalized; + +% 寰呰绠 +txOutput.PAPR = 0; + + %% Channel +% [rxSignal,ChanPara] = PassChan(txSignal,WaveformPara,SysParameters,ChanPara,seed_set(2)); +% save('testInputDataForLabview.mat','rxSignal'); + +% SIR = InterfCal(rxSignal,ChanPara); + +% A = 20*log10(abs(fft(txSignal))); +% plot(A); + diff --git a/WaveAdp/TX_CRC32_Scramble.m b/WaveAdp/TX_CRC32_Scramble.m new file mode 100644 index 0000000..6b4a38e --- /dev/null +++ b/WaveAdp/TX_CRC32_Scramble.m @@ -0,0 +1,13 @@ +function scrData = TX_CRC32_Scramble(dataPacketBits) +poly = [32,26,23,22,16,12,11,10,8,7,5,4,2,1,0]; +crcGen32 = comm.CRCGenerator(... + 'Polynomial', poly, ... + 'InitialConditions', 1, ... + 'DirectMethod', true, ... + 'FinalXOR', 1); +dataPacket = crcGen32(dataPacketBits); + +scrambler = comm.Scrambler(2,[1 0 0 0 0 0 1 0 1], ... + zeros(8,1)); +scrData = scrambler(dataPacket); +end \ No newline at end of file diff --git a/WaveAdp/TX_forPreamble.m b/WaveAdp/TX_forPreamble.m new file mode 100644 index 0000000..35d550f --- /dev/null +++ b/WaveAdp/TX_forPreamble.m @@ -0,0 +1,130 @@ +%% TX for labview +clc; +close all; +clear ; + +%% 娴嬭瘯鏁版嵁锛岃緭鍏 +userNum = 1; +fileType = 'text'; +encryptMode = 'encryptMode'; +antennasNum = 1; +centerFreq = 2450e6; +sendBandwidth = 5e6; +fftNum = 128; +CPratio = 0.25; +pilotInterval = 3; +% MCS_set=[2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6];% 鍔犲瘑绠楁硶闇瑕佸仛涓涓傞厤锛孋HB锛3.19 +MCS = [16,4]; +subcarriersNum = 128; % 寰呴傞厤 + + +%% 鎺ュ彛 +txInput.userNum = userNum; % 1 or 2 +txInput.fileType = fileType; % 'text' or 'picture' + +txInput.encryptMode = encryptMode; % 'encrypt' or 'not encrypt' +txInput.antennasNum = antennasNum; % 1 or 2 +txInput.centerFreq = centerFreq; % 500MHz~2.4G +txInput.sendBandwidth = sendBandwidth; % 20~40MHz +txInput.fftNum = fftNum;% 128 or 256 or 512 +txInput.CPratio = CPratio; % 0.25 or 0.125 or 0.0625 +txInput.pilotInterval = pilotInterval; % 3 5 7 +txInput.MCS = MCS; % a set [2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6] +txInput.subcarriersNum = subcarriersNum;% 52 or 106 or 242 + +%% 浜х敓鍙戦佹暟鎹 + +TimeStamp = []; +FreqCarrier = txInput.centerFreq;% 涓績棰戠偣鍙皟 +Bandwidth = txInput.sendBandwidth;% 甯﹀鍙皟 20~40e6 +Ts = 1/Bandwidth; +% 浠跨湡鍙傛暟 +Delay_set = [1:20]*Ts; +Velocity_set = [5:250]; +SNR_set = [0:5:35]; + +FrameNum = 1; +PacketSize = 200; % + +% load NumerologySet.mat NumerologySet +% NumerologySel = NumerologySet(1,:); % 128 0.25 3 % N_FFT; CPratio; PilotInterval +NumerologySel = [txInput.fftNum,txInput.CPratio,txInput.pilotInterval]; +MCS_no = txInput.MCS; + + + +SNR = 25; +tau_d = Delay_set(randi([1 20])); +velocity = Velocity_set(randi([1 length(Velocity_set)])); +ChanPara = ChanStat(SNR, tau_d, velocity, Ts, FreqCarrier, TimeStamp); + +% seed_set = randi([0 5000],FrameNum,3); +seed_set = [1 2 3]; + +[SysParameters,WaveformPara] = WaveformNumerologyCal(PacketSize,NumerologySel,MCS_no); +%% TX +rng(seed_set(1),'twister'); +dataPacketBits = randi([0 1],WaveformPara.Payload,1); +scrData = TX_CRC32_Scramble(dataPacketBits); +trellis = poly2trellis(7,[171 133]); +tx_encoded_bit = convenc(scrData,trellis,WaveformPara.puncpat); +tx_padded_bit = [tx_encoded_bit;zeros(WaveformPara.DataPadding,1)]; +%--------------------------------------------- +dataMod = qammod(tx_padded_bit,WaveformPara.Mod,'InputType','bit'); +WaveformPara.Power_sym = mean(abs(dataMod).^2); +dataMod = reshape(dataMod,WaveformPara.UsedSubcarrier,[]); +%% 鑾峰彇淇¢亾鍝嶅簲 %鍋囪宸茬煡鍓嶄竴鏃跺埢鐨勪俊鍙 + % 鐢熸垚淇¢亾鍝嶅簲 +N_carriers = WaveformPara.N_FFT; +channel_response=sqrt(1/2)*(randn(1,N_carriers)+1i*randn(1,N_carriers)); +% 绐楃殑鏁扮洰 +N_windows = 8; +% 瀹氫箟閲忓寲姘村钩鎬绘暟 +N_level = 10; +% 鏈灏廋P鍊硷紝澶栭儴鏉′欢锛屽彇鍐充簬鏈澶у欢杩熸嫇灞曪紝鏈夋渶灏廋P鍊>鏈澶у欢杩熸嫇灞 +min_len_CP = 30; +% 娴嬭瘯鍑芥暟 +CPset = Generate_CP(channel_response,N_windows,N_level,min_len_CP); + +% 鐗╃悊灞傚姞瀵 +[serialOFDMsym,WaveformPara.pilot,myFrameLen] = tx_FFTSymbolGen_PilotBlockPN(dataMod,WaveformPara,CPset); % 鍔燙P鐨勫湴鏂 +WaveformPara.Power_sig = mean(abs(serialOFDMsym).^2); + + +% [WaveformPara.preamble,WaveformPara.c1,WaveformPara.c2] = ... +% generatePreamble(WaveformPara,WaveformPara.Power_sig); % hb:杩欓噷鍓嶅鐮佺殑鐩殑鏄粈涔堬紵 +% txSignal = [WaveformPara.preamble;serialOFDMsym]; + +%% Parameter +NumerologyNo = 1; % 1~27 +FrameNo = 7; +% MCS_No = 3; + +cfg = wlanNonHTConfig('SignalChannelBandwidth',true, ... + 'BandwidthOperation','Static'); +% cfg.MCS = MCS_No; +PacketSize = 200; +cfg.PSDULength = PacketSize; %0-4095 +%% preamble +tx_STF = STS_modulation(NumerologyNo); % STF +tx_LTF = wlanLLTF(cfg); % LTF +[tx_SIG, tx_sigdata] = newLSIG(cfg,FrameNo);% SIG +% load('cfg.mat') + +txSignal = [zeros(500,1);tx_STF;tx_LTF;tx_SIG;serialOFDMsym]; +txSignalNormalized = txSignal./max(abs(txSignal)); +txOutput.sendSignal = txSignalNormalized; +% 寰呰绠 +txOutput.PAPR = 0; + %% Channel + [rxSignal,ChanPara] = PassChan(txSignal,WaveformPara,SysParameters,ChanPara,seed_set(2)); +save('testInputDataForLabview.mat','rxSignal'); + +% SIR = InterfCal(rxSignal,ChanPara); + + + + +% A = 20*log10(abs(fft(txSignal))); +% plot(A); + diff --git a/WaveAdp/TestMAIN2022.m b/WaveAdp/TestMAIN2022.m new file mode 100644 index 0000000..b0879cc --- /dev/null +++ b/WaveAdp/TestMAIN2022.m @@ -0,0 +1,68 @@ +%% 閫傚彉妗嗘灦-0318 +% 2022.03.18 +clear +%% Environment Scope +TimeStamp = []; +FreqCarrier = 2450e6;% 涓績棰戠偣鍙皟 +Bandwidth = 5e6;% 甯﹀鍙皟 20~40e6 +Ts = 1/Bandwidth; + +% 浠跨湡鍙傛暟 +Delay_set = [1:20]*Ts; +Velocity_set = [5:250]; +SNR_set = [0:5:35]; + +NodePair_num = 10; + +load NumerologySet.mat NumerologySet +MCS_set=[2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6]; + +% isEncrypt = 1; % 涓1鏃堕噰鐢ㄥ彉CP鍔犲瘑浼犺緭锛屽叾浠栦笉鍔犲瘑 +% 瀵嗛挜瀹夊叏绠楁硶锛孋P闅忔満鍖栫畻娉 +% 涓婂眰鏄剧ず閲囩敤鐨勬尝褰㈠弬鏁帮紙FFT锛孋P锛屽棰戯級浠ュ強瀵嗛挜绠楁硶绛 + + +%% Action +FrameNum = 1; +PacketSize = 200; % +NumerologySel = NumerologySet(1,:); % 128 0.25 3 % N_FFT; CPratio; PilotInterval +MCS_no = MCS_set(7,:); + +%% +seed_set = randi([0 5000],FrameNum,3); +SyncER_statis = zeros(NodePair_num,FrameNum); +BER_statis = zeros(NodePair_num,FrameNum); +PER_statis = zeros(NodePair_num,FrameNum); +SIR = zeros(NodePair_num,FrameNum); +for vv = 1:NodePair_num + %% Environment Parameterant + SNR = 25; + tau_d = Delay_set(randi([1 20])); + velocity = Velocity_set(randi([1 length(Velocity_set)])); + ChanPara = ChanStat(SNR, tau_d, velocity, Ts, FreqCarrier, TimeStamp); + + %% Reward + for mont = 1:FrameNum + [BER_statis(vv,mont),PER_statis(vv,mont),SyncER_statis(vv,mont)] = ... + ExecutedMAIN0701(PacketSize, NumerologySel, MCS_no, ChanPara, FreqCarrier, mont, seed_set(mont,:)); + end +end + + + + + + + + + + + + + + + + + +%% TMP + diff --git a/WaveAdp/WaveformNumerologyCal.m b/WaveAdp/WaveformNumerologyCal.m new file mode 100644 index 0000000..deb337c --- /dev/null +++ b/WaveAdp/WaveformNumerologyCal.m @@ -0,0 +1,50 @@ + +function [SysParameters,WaveformPara] = WaveformNumerologyCal(PacketSize,NumerologySel,MCS_no) +%% 鍑芥暟璇存槑锛氳绠楃郴缁熼摼璺殑鍙傛暟 +N_FFT = NumerologySel(1); CPratio = NumerologySel(2); PilotInterval = NumerologySel(3); +%% 绯荤粺鍙傛暟 +SysParameters.ShowTiming =true; +SysParameters.ShowPAPR =false; +SysParameters.ShowFigure =true; +SysParameters.ShowPathGain =true; +SysParameters.SaveSimData =false; + +WaveformPara.FrameType ='Data'; % Control涓烘帶鍒剁煭甯э紝Data涓烘暟鎹抚锛孉udio涓鸿闊冲抚 + +% 娉㈠舰鍙傛暟 +WaveformPara.PacketSize = PacketSize; +WaveformPara.Payload = WaveformPara.PacketSize*8; +WaveformPara.PayloadCRC = 32; +%-- +WaveformPara.NullRatio = 1/4; +WaveformPara.N_FFT = N_FFT; +WaveformPara.CPratio = CPratio; +WaveformPara.PilotPattern = 'block'; +WaveformPara.PilotInterval = PilotInterval; + +WaveformPara.CP_Len = WaveformPara.N_FFT*WaveformPara.CPratio; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +WaveformPara.SymbolLen = WaveformPara.N_FFT*(WaveformPara.CPratio+1); +WaveformPara.UsedSubcarrier = (1-WaveformPara.NullRatio)*WaveformPara.N_FFT-1; +WaveformPara.NullSubcarrier = WaveformPara.NullRatio*WaveformPara.N_FFT; +%-- + +WaveformPara.Mod = MCS_no(1); +WaveformPara.Modulation = log2(WaveformPara.Mod); +WaveformPara.CodeRate = (MCS_no(2)-1)/MCS_no(2); +puncpat=[1,1,0,1,1,0,0,1,1,0]; +WaveformPara.puncpat = puncpat(1:2*(MCS_no(2)-1)); +%--------------------------------------------- + +WaveformPara.BitsPerSig = WaveformPara.UsedSubcarrier*WaveformPara.Modulation; +WaveformPara.DataFrame = ceil((WaveformPara.Payload+WaveformPara.PayloadCRC)/WaveformPara.CodeRate/WaveformPara.BitsPerSig); +WaveformPara.DataPadding = WaveformPara.DataFrame*WaveformPara.BitsPerSig - floor((WaveformPara.Payload+WaveformPara.PayloadCRC)/WaveformPara.CodeRate); + +WaveformPara.PilotNum = ceil(WaveformPara.DataFrame/WaveformPara.PilotInterval); +WaveformPara.FrameSymNum = WaveformPara.DataFrame+WaveformPara.PilotNum; + +WaveformPara.FrameConstelNum = WaveformPara.UsedSubcarrier * WaveformPara.DataFrame; +WaveformPara.FrameLen = WaveformPara.SymbolLen * WaveformPara.FrameSymNum; + +WaveformPara.InterleavLen = 4080; +end + diff --git a/WaveAdp/cfg.mat b/WaveAdp/cfg.mat new file mode 100644 index 0000000..13f9378 Binary files /dev/null and b/WaveAdp/cfg.mat differ diff --git a/WaveAdp/generatePreamble.m b/WaveAdp/generatePreamble.m new file mode 100644 index 0000000..e8626fd --- /dev/null +++ b/WaveAdp/generatePreamble.m @@ -0,0 +1,47 @@ +function [preamble,c1,c2] = generatePreamble(WaveformPara,Power) + + N = WaveformPara.N_FFT; + L = WaveformPara.CP_Len; + pnSequence = comm.PNSequence('Polynomial',[8 6 5 4 0], ... + 'SamplesPerFrame',N,'InitialConditions',[0 0 0 0 0 0 0 1]); + pnSeq1 = pnSequence(); + + Modulator = comm.QPSKModulator; + + dataInMatrix1 = reshape(pnSeq1,N/2,2); + dataSymbolsIn1 = bi2de(dataInMatrix1); + + pnSeq1 = Modulator(dataSymbolsIn1); + + c1 = sqrt(2)*kron(pnSeq1,[1,0]'); + + pnSequence = comm.PNSequence('Polynomial',[8 6 5 4 0], ... + 'SamplesPerFrame',N,'InitialConditions',[0 1 0 0 0 0 0 1]); + pnSeq2 = pnSequence(); + dataInMatrix2 = reshape(pnSeq2,N/2,2); + dataSymbolsIn2 = bi2de(dataInMatrix2); + + pnSeq2 = Modulator(dataSymbolsIn2); + + pnSequence = comm.PNSequence('Polynomial',[8 6 5 4 0], ... + 'SamplesPerFrame',N,'InitialConditions',[1 1 0 0 1 0 0 1]); + pnSeq3 = pnSequence(); + dataInMatrix3 = reshape(pnSeq3,N/2,2); + dataSymbolsIn3 = bi2de(dataInMatrix3); + + pnSeq3 = Modulator(dataSymbolsIn3); + + c2 = zeros(N,1); + c2(1:2:N-1) = pnSeq2; + c2(2:2:N) = pnSeq3; + + ofdmSymbol = ifft(c1,N)*sqrt(N); + cp = ofdmSymbol(length(ofdmSymbol)-L+1:end); + trainingSym1 = [cp;ofdmSymbol]; + + ofdmSymbol = ifft(c2,N)*sqrt(N); + cp = ofdmSymbol(length(ofdmSymbol)-L+1:end); + trainingSym2 = [cp;ofdmSymbol]; + + preamble = sqrt(Power)/sqrt(2)*([trainingSym1;trainingSym2]); +end \ No newline at end of file diff --git a/WaveAdp/helperFrequencyOffset.m b/WaveAdp/helperFrequencyOffset.m new file mode 100644 index 0000000..cd1db86 --- /dev/null +++ b/WaveAdp/helperFrequencyOffset.m @@ -0,0 +1,27 @@ +function out = helperFrequencyOffset(in, fs, foffset) +%helperFrequencyOffset Apply a frequency offset to the input signal +% +% OUT = helperFrequencyOffset(IN, FS, FOFFSET) applies the specified +% frequency offset to the input signal. +% +% OUT is the frequency-offset output of the same size as IN. +% IN is the complex 2D array input. +% FS is the sampling rate in Hz (e.g. 80e6). +% FOFFSET is the frequency offset to apply to the input in Hz. +% +% See also comm.PhaseFrequencyOffset. + +% Copyright 2015-2019 The MathWorks, Inc. + +%#codegen + +% Initialize output +out = complex(zeros(size(in))); + +% Create vector of time samples +t = ((0:size(in,1)-1)/fs).'; + +% For each antenna, apply the frequency offset +for i = 1:size(in,2) + out(:,i) = in(:,i).*exp(1i*2*pi*foffset*t); +end \ No newline at end of file diff --git a/WaveAdp/mainForLabview.m b/WaveAdp/mainForLabview.m new file mode 100644 index 0000000..2158429 --- /dev/null +++ b/WaveAdp/mainForLabview.m @@ -0,0 +1,127 @@ +%% simulation for labview + +clear; +clc; + +TimeStamp = []; +FreqCarrier = 2450e6;% 涓績棰戠偣鍙皟 +Bandwidth = 5e6;% 甯﹀鍙皟 20~40e6 +Ts = 1/Bandwidth; + +FrameNum = 1; +PacketSize = 200; % +load NumerologySet.mat NumerologySet +NumerologySel = NumerologySet(1,:); % 128 0.25 3 % N_FFT; CPratio; PilotInterval + +MCS_set=[2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6];% 鍔犲瘑绠楁硶闇瑕佸仛涓涓傞厤锛孋HB锛3.19;娉ㄩ噴锛氱8涓柟妗堟湁闂 +MCS_no = MCS_set(5,:); + +% 浠跨湡鍙傛暟 +Delay_set = [1:20]*Ts; +Velocity_set = [5:250]; +SNR_set = [0:5:35]; + +SNR = 25; +tau_d = Delay_set(randi([1 20])); +velocity = Velocity_set(randi([1 length(Velocity_set)])); + +ChanPara = ChanStat(SNR, tau_d, velocity, Ts, FreqCarrier, TimeStamp); + +seed_set = [1 2 3]; + +[SysParameters,WaveformPara] = WaveformNumerologyCal(PacketSize,NumerologySel,MCS_no); +%% TX +rng(seed_set(1),'twister'); +dataPacketBits = randi([0 1],WaveformPara.Payload,1); +scrData = TX_CRC32_Scramble(dataPacketBits); +trellis = poly2trellis(7,[171 133]); +tx_encoded_bit = convenc(scrData,trellis,WaveformPara.puncpat); +tx_padded_bit = [tx_encoded_bit;zeros(WaveformPara.DataPadding,1)]; +%--------------------------------------------- +dataMod = qammod(tx_padded_bit,WaveformPara.Mod,'InputType','bit'); +WaveformPara.Power_sym = mean(abs(dataMod).^2); +dataMod = reshape(dataMod,WaveformPara.UsedSubcarrier,[]); +%% 鑾峰彇淇¢亾鍝嶅簲 %鍋囪宸茬煡鍓嶄竴鏃跺埢鐨勪俊鍙 + % 鐢熸垚淇¢亾鍝嶅簲 +N_carriers = WaveformPara.N_FFT; +channel_response=sqrt(1/2)*(randn(1,N_carriers)+1i*randn(1,N_carriers)); +N_windows = 8; +N_level = 10; +min_len_CP = 30; +% 娴嬭瘯鍑芥暟 +CPset = Generate_CP(channel_response,N_windows,N_level,min_len_CP); + +% 鐗╃悊灞傚姞瀵 +[serialOFDMsym,WaveformPara.pilot,serialFrameLen,symbolNum] = tx_FFTSymbolGen_PilotBlockPN(dataMod,WaveformPara,CPset); % 鍔燙P鐨勫湴鏂 +WaveformPara.Power_sig = mean(abs(serialOFDMsym).^2); +serialOFDMsym = serialOFDMsym./max(abs(serialOFDMsym)); +% [WaveformPara.preamble,WaveformPara.c1,WaveformPara.c2] = ... +% generatePreamble(WaveformPara,WaveformPara.Power_sig); % hb:杩欓噷鍓嶅鐮佺殑鐩殑鏄粈涔堬紵 +% txSignal = [WaveformPara.preamble;serialOFDMsym]; +% RFSignal = RFimpairment(txSignal, FreqCarrier); + +%% Parameter +NumerologyNo = 1; % 1~27 +FrameNo = 7; +% MCS_No = 3; +% PacketSize = 200; + +cfg = wlanNonHTConfig('SignalChannelBandwidth',true, ... + 'BandwidthOperation','Static'); + +% cfg.MCS = MCS_No; +cfg.PSDULength = PacketSize; %0-4095 +%% preamble +tx_STF = STS_modulation(NumerologyNo); % STF +tx_LTF = wlanLLTF(cfg); % LTF +[tx_SIG, tx_sigdata] = newLSIG(cfg,FrameNo);% SIG +% load('preambleData.mat') + +txSignal = [tx_STF;tx_LTF;tx_SIG;serialOFDMsym]; +txSignalNormalized = txSignal; + + + + +% A = 20*log10(abs(fft(txSignal))); +% plot(A); +% 鍒嗕袱鍧楋紝鍙戦 鎺ユ敹 + %% Channel +% [rxSignal,ChanPara] = PassChan(txSignal,WaveformPara,SysParameters,ChanPara,seed_set(2)); +len = 20000; +n = rand(len,1)+rand(len,1)*1i; + rxSignal = [n*0.1;txSignalNormalized]; + +% SIR = InterfCal(rxSignal,ChanPara); + +%% RX +% [rxSignal_sync,SyncErr] = rxSyncSC(rxSignal,WaveformPara,serialFrameLen); +% process preamble +[rxSignal_sync,MCS,PSDULength,failCheck,rec_frameNo,estNumerlogy,numeroError] = rx_preambleprocess(rxSignal); +rxSignal_sync = rxSignal_sync(1:serialFrameLen); + +[rxDataModLs] = rx_PilotBlockChanEst(rxSignal_sync,ChanPara,WaveformPara,CPset,symbolNum,SysParameters); % 鍘籆P鐨勫湴鏂 +dataDemod = qamdemod(rxDataModLs,WaveformPara.Mod,'OutputType','approxllr', ... + 'UnitAveragePower',false,'NoiseVariance',0.0243);%ChanPara.N0 +dataDePadded = dataDemod(1:end - WaveformPara.DataPadding); +tb = 8; +decoded = vitdec(dataDePadded,trellis,tb,'trunc','unquant',WaveformPara.puncpat); + +[rx,PER] = RX_CRC32_deScramble(decoded); % 涓㈠寘鐜 +[numErrors,BER] = biterr(dataPacketBits,rx); +BER +PER +% SyncErr + +%% + + + + + + + + + + + diff --git a/WaveAdp/newLSIG.m b/WaveAdp/newLSIG.m new file mode 100644 index 0000000..420890f --- /dev/null +++ b/WaveAdp/newLSIG.m @@ -0,0 +1,42 @@ +function [y, bits] = newLSIG(cfgFormat,frameNo) + +R = wlan.internal.nonHTRateSignalBits(cfgFormat.MCS); +length = cfgFormat.PSDULength; + + +% Construct the SIGNAL field. Length parameter with LSB first, which is 12 bits +lengthBits = de2bi(length,12,'right-msb').'; + +% Even parity bit +parityBit = mod(sum([R;lengthBits],1),2); + +% 鑷缓:鎻掑叆甯у彿 +frameNoBits = de2bi(frameNo,6,'right-msb').'; + +% The SIGNAL field (IEEE Std 802.11-2016, Section 17.3.4.2) +% bits = [R; 0; lengthBits; parityBit; zeros(6,1,'int8')]; +bits = [R; 0; lengthBits; parityBit; frameNoBits]; + +% Process L-SIG bits +encodedBits = wlanBCCEncode(bits,'1/2'); +interleavedBits = wlanBCCInterleave(encodedBits,'Non-HT',48); +modData = wlanConstellationMap(interleavedBits,1); + +% Add pilot symbols, from IEEE Std 802.11-2016, Equation 19-14 +Nsym = 1; % One symbol +z = 0; % No offset as first symbol is with pilots +modPilots = wlan.internal.nonHTPilots(Nsym,z); + +% Map subcarriers and replicate over bandwidth +cfgOFDM = wlan.internal.wlanGetOFDMConfig(cfgFormat.ChannelBandwidth,'Long','Legacy'); +sym = complex(zeros(cfgOFDM.FFTLength,1)); +sym(cfgOFDM.DataIndices,1) = repmat(modData,cfgOFDM.NumSubchannels,1); +sym(cfgOFDM.PilotIndices,1) = repmat(modPilots,cfgOFDM.NumSubchannels,1); + +% Apply gamma rotation, replicate over antennas and apply cyclic shifts +[lsig,scalingFactor] = wlan.internal.legacyFieldMap(sym,cfgOFDM.NumTones,cfgFormat); + +% OFDM modulate +y = wlan.internal.wlanOFDMModulate(lsig,cfgOFDM.CyclicPrefixLength)*scalingFactor; + +end \ No newline at end of file diff --git a/WaveAdp/preambleData.mat b/WaveAdp/preambleData.mat new file mode 100644 index 0000000..1056a65 Binary files /dev/null and b/WaveAdp/preambleData.mat differ diff --git a/WaveAdp/rRemoveIFO.m b/WaveAdp/rRemoveIFO.m new file mode 100644 index 0000000..e69de29 diff --git a/WaveAdp/rxSyncSC.m b/WaveAdp/rxSyncSC.m new file mode 100644 index 0000000..733ae85 --- /dev/null +++ b/WaveAdp/rxSyncSC.m @@ -0,0 +1,97 @@ +function [rxSignal,SyncErr] = rxSyncSC(rxSignal,WaveformPara,myFrameLen) +SyncErr = 0; +N = WaveformPara.N_FFT; +L = WaveformPara.CP_Len; +delay = 6000; +c1 = WaveformPara.c1; +c2 = WaveformPara.c2; +preamble = WaveformPara.preamble; + +timing = estSymTiming(rxSignal,N,L);%%%%%%%%% +tshift = delay+L+1-timing; +if timing > WaveformPara.CP_Len+delay + SyncErr=1; + timing = delay+1; +end + +rRemoveSTO = rxSignal(timing:end);%%%%%%%%%%%%% + +phi = estFFO(rRemoveSTO,N,L);%%%%%%%%%%%% +rRemoveFFO = rRemoveSTO.*exp(-sqrt(-1)*2*pi*(1:length(rRemoveSTO))'*phi/N); + +gEst = estIFO(rRemoveFFO,N,L,c1,c2);%%%%%%%%%%% + +rRemoveIFO = rRemoveFFO.*exp(-sqrt(-1)*2*pi*(1:length(rRemoveFFO))'*(2*gEst)/N); + +fshift = phi+2*gEst; + +startWindow = length(preamble)-L+1;%%%%%%%%%%%% +% endWindow = startWindow+WaveformPara.FrameLen-1;% +endWindow = startWindow+myFrameLen-1;% +rxSignal = rRemoveIFO(startWindow:endWindow); + +end + +%% APPENDIX +function [timing,Mavg] = estSymTiming(r,N,L) + P = zeros(length(r)-2*N/2+1,1); + R = zeros(length(r)-2*N/2+1,1); + M = zeros(length(P),1); + + for d = 1:length(M) + P(d) = (r(d:d+N/2-1))'*r(d+N/2:d+N-1); + + R(d) = (r(d+N/2:d+N-1))'*r(d+N/2:d+N-1); + + M(d) = (abs(P(d)))^2/(R(d))^2; + + end + + movavgWindow = dsp.MovingAverage(L-1); + Mavg = movavgWindow(M); + Mavg = Mavg(L/2:end); + [Mmax,timing] = max(Mavg); + timing=183; +figure +plot(M) +hold on; +plot(Mavg) +end + +function gEst = estIFO(rRemoveFFO,N,L,c1,c2) + X = 1:2:N-1; + + v = sqrt(2)*c2(X)./c1(X); + x1 = fft(rRemoveFFO(1:N))/sqrt(N); + x2 = fft(rRemoveFFO(1+N+L:2*N+L))/sqrt(N); + + D = 2*(sum((abs(x2(X))).^2))^2; + + gNum = N/2; + X1 = zeros(N,gNum); + X2 = zeros(N,gNum); + X1(:,1) = x1; + X2(:,1) = x2; + + for i = 2:gNum + X1(:,i) = [X1(3:end,i-1);X1(1:2,i-1)]; + X2(:,i) = [X2(3:end,i-1);X2(1:2,i-1)]; + end + + B = zeros(gNum,1); + + for i = 1:gNum + B(i) = (abs(sum(... + conj(X1(X,i)).*conj(v).*X2(X,i)... + )))^2/D; + end + + [Bmax,g] = max(B); + G = [0:N/4-1,-N/4:-1]; + gEst = G(g); +end + +function phi = estFFO(r,N,L) + P = sum(conj(r(1:N/2)).*r(N/2+1:2*N/2)); + phi = angle(P)/pi; +end \ No newline at end of file diff --git a/WaveAdp/rx_PilotBlockChanEstForLabview.m b/WaveAdp/rx_PilotBlockChanEstForLabview.m new file mode 100644 index 0000000..3842508 --- /dev/null +++ b/WaveAdp/rx_PilotBlockChanEstForLabview.m @@ -0,0 +1,71 @@ + +function [rxDataModLs] = rx_PilotBlockChanEst(rAddAWGN,WaveformPara,CPset,SysParameters) +N = WaveformPara.N_FFT; +N_Used = WaveformPara.UsedSubcarrier; +L = WaveformPara.CP_Len; +m = WaveformPara.PilotNum; +pnSequence = comm.PNSequence('Polynomial',[8 6 5 4 0], ... + 'SamplesPerFrame',WaveformPara.UsedSubcarrier,'InitialConditions',[0 0 0 0 0 0 0 1]); + WaveformPara.pilot = generatePilot(pnSequence,1); +pilot = WaveformPara.pilot; + +% rxOFDMsym = reshape(rAddAWGN,WaveformPara.SymbolLen,WaveformPara.FrameSymNum); +% rxOFDMcore = rxOFDMsym(L+1:L+N,:); % 鍘籆P鐨勫湴鏂 + +rxOFDMcore = []; + % 灏嗙煩闃垫搷浣滆浆鎹㈡垚瀵瑰垪鎿嶄綔 +kk1=1; +for i = 1:length(CPset) + get_idx = kk1:(kk1 + N + CPset(i)-1); + currentSym = rAddAWGN(get_idx); + rxOFDMcore = [rxOFDMcore,currentSym(CPset(i)+1:end)]; + kk1=kk1+(N+CPset(i)); +end + + +rxDataModInMatrix = fft(rxOFDMcore,[],1)/sqrt(N); + +rxDataModInMatrix_shift = [rxDataModInMatrix(2:2+(N_Used-1)/2-1,:);rxDataModInMatrix(end - (N_Used-1)/2:end,:)]; + +rxPilot = rxDataModInMatrix_shift(:,1:WaveformPara.PilotInterval+1:WaveformPara.FrameSymNum); + +hLs = zeros(N_Used,m); + +rxDataRemovPilot = zeros(N_Used,WaveformPara.DataFrame); + + +for i = 1:m-1 + rxDataRemovPilot(:,(i-1)*WaveformPara.PilotInterval+1:i*(WaveformPara.PilotInterval)) = ... + rxDataModInMatrix_shift(:,(i-1)*(WaveformPara.PilotInterval+1)+2:i*(WaveformPara.PilotInterval+1)); + hLs(:,i) = rxPilot(:,i)./pilot; +end + +rxDataRemovPilot(:,(m-1)*WaveformPara.PilotInterval+1:end) = ... + rxDataModInMatrix_shift(:,(m-1)*(WaveformPara.PilotInterval+1)+2:end); +hLs(:,m) = rxPilot(:,m)./pilot; + + + +if mod(WaveformPara.DataFrame,WaveformPara.PilotInterval)>0 + hLs = [kron(hLs(:,1:m-1),ones(1,WaveformPara.PilotInterval)),kron(hLs(:,m),ones(1,mod(WaveformPara.DataFrame,WaveformPara.PilotInterval)))]; +else + hLs = kron(hLs(:,1:m),ones(1,WaveformPara.PilotInterval)); +end + +eqDataModLs = zeros(N_Used,WaveformPara.DataFrame); + +for i = 1:WaveformPara.DataFrame + eqDataModLs(:,i) = rxDataRemovPilot(:,i)./hLs(:,i); +end + +rxDataModLs = reshape(eqDataModLs,[],1); + +end +%% +function pilot = generatePilot(pnSequence,Power) + + pilot = pnSequence(); + + bpskModulator = comm.BPSKModulator; + pilot = bpskModulator(pilot)*sqrt(Power); +end \ No newline at end of file diff --git a/WaveAdp/rx_PilotBlockChanEst_CPrand.m b/WaveAdp/rx_PilotBlockChanEst_CPrand.m new file mode 100644 index 0000000..11c254b --- /dev/null +++ b/WaveAdp/rx_PilotBlockChanEst_CPrand.m @@ -0,0 +1,80 @@ + +function [rxDataModLs] = rx_PilotBlockChanEst_CPrand(rAddAWGN,ChanPara,WaveformPara,CPset,symbolNum,SysParameters) +N = WaveformPara.N_FFT; +N_Used = WaveformPara.UsedSubcarrier; +L = WaveformPara.CP_Len; +m = WaveformPara.PilotNum; +pilot = WaveformPara.pilot; + +% rxOFDMsym = reshape(rAddAWGN,WaveformPara.SymbolLen,WaveformPara.FrameSymNum); +% rxOFDMcore = rxOFDMsym(L+1:L+N,:); % 鍘籆P鐨勫湴鏂 + +rxOFDMcore = []; + % 灏嗙煩闃垫搷浣滆浆鎹㈡垚瀵瑰垪鎿嶄綔 +kk1=1; +lenCPset = length(CPset); +if lenCPset0 + hLs = [kron(hLs(:,1:m-1),ones(1,WaveformPara.PilotInterval)),kron(hLs(:,m),ones(1,mod(WaveformPara.DataFrame,WaveformPara.PilotInterval)))]; +else + hLs = kron(hLs(:,1:m),ones(1,WaveformPara.PilotInterval)); +end + +eqDataModLs = zeros(N_Used,WaveformPara.DataFrame); + +for i = 1:WaveformPara.DataFrame + eqDataModLs(:,i) = rxDataRemovPilot(:,i)./hLs(:,i); +end + +rxDataModLs = reshape(eqDataModLs,[],1); + +end \ No newline at end of file diff --git a/WaveAdp/rx_preambleprocess.m b/WaveAdp/rx_preambleprocess.m new file mode 100644 index 0000000..c53d46f --- /dev/null +++ b/WaveAdp/rx_preambleprocess.m @@ -0,0 +1,90 @@ +function [rx_datafield,MCS,PSDULength,failCheck,rec_frameNo,estNumerlogy,numeroError] = rx_preambleprocess(rx) + +searchOffset = 0; % Offset from start of waveform in samples +rxWaveLen = 30000; +cfg = wlanNonHTConfig('SignalChannelBandwidth',true, ... + 'BandwidthOperation','Static'); + +% Generate field indices +ind = wlanFieldIndices(cfg); +chanBW = cfg.ChannelBandwidth; +sr = wlanSampleRate(cfg); % Sample rate + +% while (searchOffset + minPktLen) <= rxWaveLen + +% Packet detection +threshold = 0.8; + +[pktOffset,M] = wlanPacketDetect(rx,chanBW,searchOffset,threshold); +plot(M) +% Adjust packet offset +pktOffset = searchOffset + pktOffset; +if isempty(pktOffset) || (pktOffset + ind.LSIG(2) > rxWaveLen) + error('** No packet detected **'); +end +% Coarse frequency offset estimation and correction using L-STF +rxLSTF = rx(pktOffset+(ind.LSTF(1):ind.LSTF(2)), :); +coarseFreqOffset = wlanCoarseCFOEstimate(rxLSTF,chanBW); +rx = helperFrequencyOffset(rx,sr,-coarseFreqOffset); +% Symbol timing synchronization +searchBufferLLTF = rx(pktOffset+(ind.LSTF(1):ind.LSIG(2)),:); +pktOffset = pktOffset+wlanSymbolTimingEstimate(searchBufferLLTF,chanBW); +% Fine frequency offset estimation and correction using L-STF +rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:); + +fineFreqOffset = wlanFineCFOEstimate(rxLLTF,chanBW); +rx = helperFrequencyOffset(rx,sr,-fineFreqOffset); + +% Scale the waveform based on L-STF power (AGC) +gain = 1./(sqrt(mean(rxLSTF.*conj(rxLSTF)))); +rx = rx.*gain; + +% Packet Format Detection +rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:); +lltfDemod = wlanLLTFDemodulate(rxLLTF,chanBW); +lltfChanEst = wlanLLTFChannelEstimate(lltfDemod,chanBW); +noiseVar = helperNoiseEstimate(lltfDemod); + +% Recover L-SIG field bits +[rxLSIGBits, failCheck, ~] = wlanLSIGRecover(rx(pktOffset + (ind.LSIG(1):ind.LSIG(2)), :), lltfChanEst, noiseVar, chanBW); +[MCS,PSDULength] = interpretLSIG(rxLSIGBits); +rec_frameNo = bi2de(rxLSIGBits(19:24).','right-msb'); + +if failCheck % Skip L-STF length of samples and continue searching + disp('** L-SIG check fail **'); + else + disp('L-SIG check pass'); +end + +% Recover Numerology field bits +rxLSTF = rx(pktOffset+(ind.LSTF(1):ind.LSTF(2)), :); +[estNumerlogy,numeroError] = STS_demod(rxLSTF); +if numeroError + disp('** Numerology check fail **'); + else + disp('Numerology check pass'); +end + +% output data field +% rx_datafield = rx(pktOffset+(ind.NonHTData(1):ind.NonHTData(2)), :); +rx_datafield = rx(pktOffset+ind.NonHTData(1):end, :); +end +%% +function [MCS,PSDULength] = interpretLSIG(recLSIGBits) +% InterpretLSIG Interprets recovered L-SIG bits +% +% [MCS,PSDULENGTH] = interpretLSIG(RECLSIGBITS) returns the +% modulation and coding scheme and PSDU length given the recovered L-SIG +% bits + +% Rate and length are determined from bits +rate = double(recLSIGBits(1:3)); +length = double(recLSIGBits(5+(1:12))); + +% MCS rate table, IEEE Std 802.11-2016, Table 17-6. +R = wlan.internal.nonHTRateSignalBits(); +mcstmp = find(all(bsxfun(@eq,R(1:3,:),rate)))-1; +MCS = mcstmp(1); % For codegen +PSDULength = bi2de(length.'); + +end \ No newline at end of file diff --git a/WaveAdp/rxdata1.mat b/WaveAdp/rxdata1.mat new file mode 100644 index 0000000..84424b8 Binary files /dev/null and b/WaveAdp/rxdata1.mat differ diff --git a/WaveAdp/rxdata2.mat b/WaveAdp/rxdata2.mat new file mode 100644 index 0000000..1940ff9 Binary files /dev/null and b/WaveAdp/rxdata2.mat differ diff --git a/WaveAdp/rxdata3preamble.mat b/WaveAdp/rxdata3preamble.mat new file mode 100644 index 0000000..1e213f7 Binary files /dev/null and b/WaveAdp/rxdata3preamble.mat differ diff --git a/WaveAdp/rxdata4preamble.mat b/WaveAdp/rxdata4preamble.mat new file mode 100644 index 0000000..411251c Binary files /dev/null and b/WaveAdp/rxdata4preamble.mat differ diff --git a/WaveAdp/rxdata5normalized.mat b/WaveAdp/rxdata5normalized.mat new file mode 100644 index 0000000..f07559b Binary files /dev/null and b/WaveAdp/rxdata5normalized.mat differ diff --git a/WaveAdp/rxdata6normalizedzero.mat b/WaveAdp/rxdata6normalizedzero.mat new file mode 100644 index 0000000..40d792e Binary files /dev/null and b/WaveAdp/rxdata6normalizedzero.mat differ diff --git a/WaveAdp/temp.m b/WaveAdp/temp.m new file mode 100644 index 0000000..d53bd83 --- /dev/null +++ b/WaveAdp/temp.m @@ -0,0 +1,14 @@ +cfgNonHT = wlanNonHTConfig; + +txWaveform = wlanWaveformGenerator([1;0;0;1],cfgNonHT,... + 'WindowTransitionTime',0); +len = 30000; +n = rand(len,1)+rand(len,1)*1i; +rxWaveform = [n;txWaveform]; +% 0.001*rand(100,1) + +offset = 5; +threshold = 1-10*eps; +startOffset = wlanPacketDetect(rxWaveform,... + cfgNonHT.ChannelBandwidth,offset,threshold) ; +totalOffset = offset + startOffset \ No newline at end of file diff --git a/WaveAdp/testInputDataForLabview.mat b/WaveAdp/testInputDataForLabview.mat new file mode 100644 index 0000000..41b6c7f Binary files /dev/null and b/WaveAdp/testInputDataForLabview.mat differ diff --git a/WaveAdp/txSignalForNormalized.mat b/WaveAdp/txSignalForNormalized.mat new file mode 100644 index 0000000..e71c89a Binary files /dev/null and b/WaveAdp/txSignalForNormalized.mat differ diff --git a/WaveAdp/txSignalForNormalizedZero.mat b/WaveAdp/txSignalForNormalizedZero.mat new file mode 100644 index 0000000..77afe75 Binary files /dev/null and b/WaveAdp/txSignalForNormalizedZero.mat differ diff --git a/WaveAdp/txSignalForPreamble.mat b/WaveAdp/txSignalForPreamble.mat new file mode 100644 index 0000000..a24d469 Binary files /dev/null and b/WaveAdp/txSignalForPreamble.mat differ diff --git a/WaveAdp/txSignalForPreamble1.mat b/WaveAdp/txSignalForPreamble1.mat new file mode 100644 index 0000000..359689c Binary files /dev/null and b/WaveAdp/txSignalForPreamble1.mat differ diff --git a/WaveAdp/tx_FFTSymbolGen_PilotBlockPN_CPrand.m b/WaveAdp/tx_FFTSymbolGen_PilotBlockPN_CPrand.m new file mode 100644 index 0000000..60bad02 --- /dev/null +++ b/WaveAdp/tx_FFTSymbolGen_PilotBlockPN_CPrand.m @@ -0,0 +1,69 @@ +function [serialOFDMsym,pilot,serialFrameLen,symbolNum] = tx_FFTSymbolGen_PilotBlockPN_CPrand(dataModInMatrix,WaveformPara,CPset,SysParameters) + +numOfdmSymbol = WaveformPara.FrameSymNum; +N_used = WaveformPara.UsedSubcarrier; +CP_Len = WaveformPara.CP_Len; + +pnSequence = comm.PNSequence('Polynomial',[8 6 5 4 0], ... + 'SamplesPerFrame',N_used,'InitialConditions',[0 0 0 0 0 0 0 1]); + +pilotPower = WaveformPara.Power_sym; +pilot = generatePilot(pnSequence,pilotPower); + +m = WaveformPara.PilotNum-1; +dataModAddPilot = zeros(N_used,numOfdmSymbol); +for i = 1:m + dataModAddPilot(:,(i-1)*(WaveformPara.PilotInterval+1)+1:i*(WaveformPara.PilotInterval+1)) = ... + [pilot,dataModInMatrix(:,(i-1)*WaveformPara.PilotInterval+1:i*WaveformPara.PilotInterval)]; +end +dataModAddPilot(:,m*(WaveformPara.PilotInterval+1)+1:end) = ... + [pilot,dataModInMatrix(:,m*WaveformPara.PilotInterval+1:end)]; + +dataModAddPilot_shift = [zeros(1,WaveformPara.FrameSymNum);... + dataModAddPilot(1:(N_used-1)/2,:);... + zeros(WaveformPara.NullSubcarrier,WaveformPara.FrameSymNum);... + dataModAddPilot(1+(N_used-1)/2:end,:)]; + + +OFDMsym = ifft(dataModAddPilot_shift,[],1)*sqrt(WaveformPara.N_FFT); + +[~,symNum]=size(OFDMsym); +serialOFDMsym = []; +lenCPset = length(CPset); +% 瀵瑰垪鎿嶄綔 +if lenCPset typeMax + prototype = bitClassMax; + subone = false; + else + prototype = typeMax; + end + xNew = x; + isInputFloat = false; + end + useN = cast(N,'like',prototype); + + if isInputFloat || (isSim && isInputLogical) + + inSize = size(xNew); + nIntsPerCol = inSize(1)/double(N); + coder.internal.errorIf(nIntsPerCol ~= floor(nIntsPerCol), 'comm:bit2int:InvalidInputDims'); + outSize = inSize; + outSize(1) = nIntsPerCol; + xMat = reshape(xNew, useN, []); + if MSBFirst + powOf2 = pow2(useN-1:-1:0); + powOf2(1) = (1-2*IsSigned)*powOf2(1); + else + powOf2 = pow2(0:useN-1); + powOf2(end) = (1-2*IsSigned)*powOf2(end); + end + + y = reshape(powOf2 * xMat, outSize); + + else + + Nidx = coder.internal.indexInt(useN); + [nRows,nCols,nPages] = size(xNew); + nIntsPerCol = idivide(nRows, Nidx); + coder.internal.errorIf(Nidx*nIntsPerCol ~= nRows, ... + 'comm:bit2int:InvalidInputDims'); + + y = coder.nullcopy(zeros(nIntsPerCol, nCols, nPages, 'like', prototype)); + + powOf2 = cast(2, 'like', y) .^ (useN-1:-1:0)'; + powOf2(1) = (1-2*IsSigned)*powOf2(1)-1*subone*IsSigned; + xTmp = cast(xNew, 'like', y); + nInts = nIntsPerCol * coder.internal.indexInt(nCols) * coder.internal.indexInt(nPages); + if MSBFirst + idx = 1:Nidx; + else + idx = Nidx:-1:1; + end + for k=1:nInts + bits = xTmp(Nidx*(k-1) + idx); + y(k) = sum(bits(:) .* powOf2, 'native'); + end + end + +end diff --git a/calculate_CDF.m b/calculate_CDF.m new file mode 100644 index 0000000..617086f --- /dev/null +++ b/calculate_CDF.m @@ -0,0 +1,4 @@ +function [CDF] = calculate_CDF(data,PAPR_bin) +count = hist(data,PAPR_bin); +CDF = cumsum(count)/sum(count); +end \ No newline at end of file diff --git a/calculate_PAPR.m b/calculate_PAPR.m new file mode 100644 index 0000000..2bf7c68 --- /dev/null +++ b/calculate_PAPR.m @@ -0,0 +1,8 @@ +function [PAPR_dex,Mean_Power] = calculate_PAPR(x) +% PAPR_dex 璁$畻寰楀埌鎵鏈夌鍙峰悇鑷殑PAPR +% Mean_Power 绗﹀彿鐨勫钩鍧囧姛鐜 + Signal_Power = abs(x).^2; + Peak_Power = max(Signal_Power,[],1); % norm(x,Inf)^2 + Mean_Power = mean(Signal_Power,1); % norm(x,2)^2/(K*IF) + PAPR_dex = Peak_Power./Mean_Power; +end \ No newline at end of file diff --git a/channelSounding.m b/channelSounding.m new file mode 100644 index 0000000..9a432cc --- /dev/null +++ b/channelSounding.m @@ -0,0 +1,28 @@ +function txNDP = channelSounding(cfg) +% 浜х敓NDP +guardInterval = 0.8;% Guard interval in Microseconds +cfgNDP = wlanHESUConfig('APEPLength',0,'GuardInterval',guardInterval); % No data in an NDP +cfgNDP.ChannelBandwidth = cfg.ChannelBandwidth; +cfgNDP.NumTransmitAntennas = cfg.numTx; +cfgNDP.NumSpaceTimeStreams = cfg.numTx; +txNDP = wlanWaveformGenerator([],cfgNDP); + +% 鍔犲叆PN搴忓垪锛屾浜ゅ簭鍒楋紝閲嶅涓夋宸﹀彸 +soundingSeqMod_1 = zadoffChuSeq(29,255); +soundingSeqMod_2 = zadoffChuSeq(47,255); + +% Concatenate multiple sequences to sound the channel periodically. +numSeq = 20; % Number of transmitted sequences in a WSS period +if cfg.numTx == 1 +soundingSeqMod = [soundingSeqMod_1]; +txSignal = repmat(soundingSeqMod,numSeq,1); +else +soundingSeqMod = [soundingSeqMod_1, soundingSeqMod_2]; +txSignal = repmat(soundingSeqMod,numSeq,1); +end + +% with 50 sample padding. +% txNDP = [txNDP; zeros(50,size(txNDP,2))]; +txNDP = [txNDP; txSignal; zeros(50,size(txNDP,2))]; + +end diff --git a/createMUMIMO.m b/createMUMIMO.m new file mode 100644 index 0000000..06cc594 --- /dev/null +++ b/createMUMIMO.m @@ -0,0 +1,18 @@ +function cfgMUMIMO = createMUMIMO(Tx, CP, NumSpaceTimeStreams, MCS, APEPLength, numUsers) +numTx = Tx; % Number of transmit antennas +% MU-MIMO configuration - 4 users on one 242-tone RU +cfgMUMIMO = wlanHEMUConfig(191+numUsers); + +guardInterval = CP; % Guard interval in Microseconds + +% Configure common parameters for all users +cfgMUMIMO.NumTransmitAntennas = numTx; +cfgMUMIMO.GuardInterval = guardInterval; + +% Configure per user parameters +for idx = 1:numUsers + cfgMUMIMO.User{idx}.NumSpaceTimeStreams = NumSpaceTimeStreams(idx); + cfgMUMIMO.User{idx}.MCS = MCS(idx); + cfgMUMIMO.User{idx}.APEPLength = APEPLength(idx); +end +end diff --git a/demodSIG.m b/demodSIG.m new file mode 100644 index 0000000..8b56148 --- /dev/null +++ b/demodSIG.m @@ -0,0 +1,219 @@ +%% 瑙e寘澶 +function [cfgRx,cfgUsers,rmsEVM,failCRC] = demodSIG(rx,rx_format,cfgUI)%% Packet Format Detection +%% L-LTF Channel Estimate +% Demodulate the L-LTF and perform channel estimation. The demodulated +% L-LTF symbols include tone rotation for each 20 MHz segment as described +% in [ <#16 2> ], section 21.3.7.5. The L-LTF channel estimates (with tone +% rotation) are used to equalize and decode the pre-HE-LTF fields. + +lltfDemod = wlanHEDemodulate(rxLLTF,'L-LTF',chanBW); +lltfChanEst = wlanLLTFChannelEstimate(lltfDemod,chanBW); + +%% L-SIG and RL-SIG Decoding +% The L-SIG field is used to determine the receive time, or RXTIME, of the +% packet. The RXTIME is calculated using the length bits of the L-SIG +% payload. The L-SIG and RL-SIG fields are recovered to perform the channel +% estimate on the extra subcarriers in the L-SIG and RL-SIG fields. The +% |lltfChanEst| channel estimates are updated to include the channel +% estimates on extra subcarriers in the L-SIG and RL-SIG fields. The L-SIG +% payload is decoded using an estimate of the channel and noise power +% obtained from the L-LTF field. The L-SIG length property in +% is updated after L-SIG decoding. + +disp('Decoding L-SIG... '); +% Extract L-SIG and RL-SIG fields +rxLSIG = rx(pktOffset+(ind.LSIG(1):ind.RLSIG(2)),:); + +% OFDM demodulate +helsigDemod = wlanHEDemodulate(rxLSIG,'L-SIG',chanBW); + +% Estimate CPE and phase correct symbols +helsigDemod = preHECommonPhaseErrorTracking(helsigDemod,lltfChanEst,'L-SIG',chanBW); + +% Estimate channel on extra 4 subcarriers per subchannel and create full +% channel estimate +preheInfo = wlanHEOFDMInfo('L-SIG',chanBW); +preHEChanEst = preHEChannelEstimate(helsigDemod,lltfChanEst,preheInfo.NumSubchannels); + +% Average L-SIG and RL-SIG before equalization +helsigDemod = mean(helsigDemod,2); + +% Equalize data carrying subcarriers, merging 20 MHz subchannels +[eqLSIGSym,csi] = preHESymbolEqualize(helsigDemod(preheInfo.DataIndices,:,:), ... + preHEChanEst(preheInfo.DataIndices,:,:),noiseVar,preheInfo.NumSubchannels); + +% Decode L-SIG field +[~,failCheck,lsigInfo] = wlanLSIGBitRecover(eqLSIGSym,noiseVar,csi); + +if failCheck + disp(' ** L-SIG check fail **'); +else + disp(' L-SIG check pass'); +end +% Get the length information from the recovered L-SIG bits and update the +% L-SIG length property of the recovery configuration object +lsigLength = lsigInfo.Length; +cfgRx.LSIGLength = lsigLength; + +% Measure EVM of L-SIG symbols +EVM = comm.EVM; +EVM.ReferenceSignalSource = 'Estimated from reference constellation'; +EVM.Normalization = 'Average constellation power'; +EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK'); +rmsEVM = EVM(eqLSIGSym); +fprintf(' L-SIG EVM: %2.2fdB\n\n',20*log10(rmsEVM/100)); + +% Calculate the receive time and corresponding number of samples in the +% packet +RXTime = ceil((lsigLength + 3)/3) * 4 + 20; % In microseconds +numRxSamples = round(RXTime * 1e-6 * sr); % Number of samples in time + +fprintf(' RXTIME: %dus\n',RXTime); +fprintf(' Number of samples in the packet: %d\n\n',numRxSamples); + +%% +% The waveform and spectrum of the detected packet within |rx| is +% displayed given the calculated RXTIME and corresponding number of +% samples. + +sampleOffset = max((-lstfLength + pktOffset),1); % First index to plot +sampleSpan = numRxSamples + 2*lstfLength; % Number samples to plot +% Plot as much of the packet (and extra samples) as we can +plotIdx = sampleOffset:min(sampleOffset + sampleSpan,rxWaveLen); + +% Configure timeScope to display the packet 灞曠ず +timeScope.TimeSpan = sampleSpan/sr; +timeScope.TimeDisplayOffset = sampleOffset/sr; +timeScope.YLimits = [0 max(abs(rx(:)))]; +timeScope(abs(rx(plotIdx,:))); +release(timeScope); + +% Display the spectrum of the detected packet 灞曠ず +spectrumAnalyzer(rx(pktOffset + (1:numRxSamples),:)); +release(spectrumAnalyzer); + +%% HE-SIG-A Decoding +% The HE-SIG-A field contains the transmission configuration of an HE +% packet. An estimate of the channel and noise power obtained from the +% L-LTF is required to decode the HE-SIG-A field. + +disp('Decoding HE-SIG-A...') +rxSIGA = rx(pktOffset+(ind.HESIGA(1):ind.HESIGA(2)),:); +sigaDemod = wlanHEDemodulate(rxSIGA,'HE-SIG-A',chanBW); +hesigaDemod = preHECommonPhaseErrorTracking(sigaDemod,preHEChanEst,'HE-SIG-A',chanBW); + +% Equalize data carrying subcarriers, merging 20 MHz subchannels +preheInfo = wlanHEOFDMInfo('HE-SIG-A',chanBW); +[eqSIGASym,csi] = preHESymbolEqualize(hesigaDemod(preheInfo.DataIndices,:,:), ... + preHEChanEst(preheInfo.DataIndices,:,:), ... + noiseVar,preheInfo.NumSubchannels); +% Recover HE-SIG-A bits +[sigaBits,failCRC] = wlanHESIGABitRecover(eqSIGASym,noiseVar,csi); + +% Perform the CRC on HE-SIG-A bits +if failCRC + disp(' ** HE-SIG-A CRC fail **'); +else + disp(' HE-SIG-A CRC pass'); +end + +% Measure EVM of HE-SIG-A symbols +release(EVM); +if strcmp(pktFormat,'HE-EXT-SU') + % The second symbol of an HE-SIG-A field for an HE-EXT-SU packet is + % QBPSK. + EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK',[0 pi/2 0 0]); + % Account for scaling of L-LTF for an HE-EXT-SU packet + rmsEVM = EVM(eqSIGASym*sqrt(2)); +else + EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK'); + rmsEVM = EVM(eqSIGASym); +end +fprintf(' HE-SIG-A EVM: %2.2fdB\n\n',20*log10(mean(rmsEVM)/100)); + +%% Interpret Recovered HE-SIG-A bits +% The object +% is updated after interpreting the recovered HE-SIG-A bits. + +cfgRx = interpretHESIGABits(cfgRx,sigaBits); +ind = wlanFieldIndices(cfgRx); % Update field indices +%% +% Display the common transmission configuration obtained from HE-SIG-A +% field for an HE-MU packet. The properties indicated by -1 are unknown or +% undefined. The unknown user-related properties are updated after +% successful decoding of the HE-SIG-B field. +disp(cfgRx) + +%% HE-SIG-B Decoding +% For an HE-MU packet the HE-SIG-B field contains: +% +% * The RU allocation information for a non-compressed SIGB waveform is +% inferred from HE-SIG-B Common field [ <#16 1> Table. 27-23]. For a +% compressed SIGB waveform the RU allocation information is inferred from +% the recovered HE-SIG-A bits. +% * For a non-compressed SIGB waveform the number of HE-SIG-B symbols are +% updated in the object. +% The symbols are only updated if the number of HE-SIG-B symbols indicated +% in the HE-SIG-A field is set to 15 and all content channels pass the CRC. +% The number of HE-SIG-B symbols indicated in the HE-SIG-A field are not +% updated if any HE-SIG-B content channel fails the CRC. +% * The user transmission parameters for both SIGB compressed and +% non-compressed waveforms are inferred from the HE-SIG-B user field [ <#16 1> +% Table. 27-25, 27-26]. +% +% An estimate of the channel and noise power obtained from the L-LTF is +% required to decode the HE-SIG-B field. + +if strcmp(pktFormat,'HE-MU') + fprintf('Decoding HE-SIG-B...\n'); + if ~cfgRx.SIGBCompression + fprintf(' Decoding HE-SIG-B common field...\n'); + s = getSIGBLength(cfgRx); + % Get common field symbols. The start of HE-SIG-B field is known + rxSym = rx(pktOffset+(ind.HESIGA(2)+(1:s.NumSIGBCommonFieldSamples)),:); + % Decode HE-SIG-B common field + [status,cfgRx] = heSIGBCommonFieldDecode(rxSym,preHEChanEst,noiseVar,cfgRx); + + % CRC on HE-SIG-B content channels + if strcmp(status,'Success') + fprintf(' HE-SIG-B (common field) CRC pass\n'); + elseif strcmp(status,'ContentChannel1CRCFail') + fprintf(' ** HE-SIG-B CRC fail for content channel-1\n **'); + elseif strcmp(status,'ContentChannel2CRCFail') + fprintf(' ** HE-SIG-B CRC fail for content channel-2\n **'); + elseif any(strcmp(status,{'UnknownNumUsersContentChannel1','UnknownNumUsersContentChannel2'})) + error(' ** Unknown packet length, discard packet\n **'); + else + % Discard the packet if all HE-SIG-B content channels fail + error(' ** HE-SIG-B CRC fail **'); + end + % Update field indices as the number of HE-SIG-B symbols are + % updated + ind = wlanFieldIndices(cfgRx); + end + + % Get complete HE-SIG-B field samples + rxSIGB = rx(pktOffset+(ind.HESIGB(1):ind.HESIGB(2)),:); + fprintf(' Decoding HE-SIG-B user field... \n'); + % Decode HE-SIG-B user field + [failCRC,cfgUsers] = heSIGBUserFieldDecode(rxSIGB,preHEChanEst,noiseVar,cfgRx); + + % CRC on HE-SIG-B users + if ~all(failCRC) + fprintf(' HE-SIG-B (user field) CRC pass\n\n'); + numUsers = numel(cfgUsers); + elseif all(failCRC) + % Discard the packet if all users fail the CRC + error(' ** HE-SIG-B CRC fail for all users **'); + else + fprintf(' ** HE-SIG-B CRC fail for at least one user\n **'); + % Only process users with valid CRC + numUsers = numel(cfgUsers); + end + +else % HE-SU, HE-EXT-SU + cfgUsers = {cfgRx}; + numUsers = 1; +end +end + diff --git a/findPDF.m b/findPDF.m new file mode 100644 index 0000000..aeba020 --- /dev/null +++ b/findPDF.m @@ -0,0 +1,6 @@ +function pdf = findPDF(range,data) + pdf = zeros(length(range)-1,1); + for i = 1:length(range)-1 + pdf(i) = length(find(range(i) < data & data <= range(i+1)))/length(data); + end +end diff --git a/findTemporalParameters.m b/findTemporalParameters.m new file mode 100644 index 0000000..fb730ed --- /dev/null +++ b/findTemporalParameters.m @@ -0,0 +1,4 @@ +function [delay_average,delay_spread]=findTemporalParameters(PDP, T_sample, K) + delay_average = sum(PDP.*(0:K-1).'*T_sample)/sum(PDP); + delay_spread = sqrt(sum(PDP.*((0:K-1).'*T_sample-delay_average).^2)/sum(PDP)); +end \ No newline at end of file diff --git a/heCPECorrection.m b/heCPECorrection.m new file mode 100644 index 0000000..44090e5 --- /dev/null +++ b/heCPECorrection.m @@ -0,0 +1,32 @@ +function rx = heCPECorrection(rx,sigbPilots,chanEst,chanBW) +%heCPECorrection Estimate and correct common phase error +% +% RX = heCPECorrection(RX,SIGBPILOTS,CHANEST,CHANBW) returns the phase +% corrected OFDM symbols of HE-SIG-B field using the input, RX, HE-SIG-B +% pilot symbols, SIGBPILOTS, channel estimates, CHANEST, and channel +% bandwidth CHANBW. +% +% RX is a complex Nst-by-Nsym-by-Nr array containing the received OFDM +% symbols. Nst is the number of subcarriers and Nr is the number of +% receive antennas. +% +% SIGBPILOTS are the pilots symbols in the demodulated HE-SIG-B field. +% +% CHANEST is a complex Nst-by-1-by-Nr array containing the estimated +% channel at data and pilot subcarriers, where Nst is the number of +% occupied subcarriers and Nr is the number of receive antennas. +% +% CHANBW is a character vector or string. The allowed channel bandwidth +% are 'CBW20', 'CBW40', 'CBW80' and 'CBW160'. + +% Copyright 2018 The MathWorks, Inc. + +numSubchannels = wlan.internal.cbwStr2Num(chanBW)/20; + +numBits = size(sigbPilots,2); +z = 4; +refPilots = repmat(wlan.internal.nonHTPilots(numBits, z),numSubchannels,1,1); +cpe = wlan.internal.commonPhaseErrorEstimate(sigbPilots,chanEst,refPilots); +rx = wlan.internal.commonPhaseErrorCorrect(rx,cpe); + +end \ No newline at end of file diff --git a/heCommonPhaseErrorTracking.m b/heCommonPhaseErrorTracking.m new file mode 100644 index 0000000..0e4100c --- /dev/null +++ b/heCommonPhaseErrorTracking.m @@ -0,0 +1,92 @@ +function [y,cpe] = heCommonPhaseErrorTracking(x,chanEst,cfg,varargin) +%heCommonPhaseErrorTracking HE common pilot phase tracking +% +% [Y,CPE] = heCommonPhaseErrorTracking(X,CHANEST,CFGHE) performs common +% pilot error phase tracking of the single user HE format input X. +% +% Y is a complex Nst-by-Nsym-by-Nr array containing the common phase +% corrected OFDM symbols. Nst is the number of occupied subcarriers, Nsym +% is the number of symbols, and Nr is the number of receive antennas. Y +% is the common phase error corrected symbols. +% +% X is a complex Nst-by-Nsym-by-Nr array containing the received OFDM +% symbols. +% +% CHANEST is a complex Nst-by-Nsts-by-Nr array containing the channel +% gains for all active subcarriers, or a Nsp-by-Nsts-by-Nr array +% containing the channel gains for only pilot subcarriers. Nsts is the +% number of space-time streams. +% +% CFGHE is the format configuration object of type wlanHESUConfig, +% wlanHETBConfig or wlanHERecoveryConfig. +% +% [Y,CPE] = heCommonPhaseErrorTracking(X,CHANEST,CFGMU,RUNUMBER) performs +% common pilot error phase tracking of the multi-user HE format input X. +% +% CFGMU is the format configuration object of type wlanHEMUConfig. +% +% RUNUMBER is the RU (resource unit) number. + +% Copyright 2017-2019 The MathWorks, Inc. + +%#codegen + +validateattributes(cfg,{'wlanHESUConfig','wlanHEMUConfig','wlanHETBConfig','wlanHERecoveryConfig'},{'scalar'},mfilename,'format configuration object'); + +if isa(cfg,'wlanHERecoveryConfig') + pktFormat = cfg.PacketFormat; + if strcmp(pktFormat,'HE-MU') + numSpaceTimeStreamsPerRU = cfg.RUTotalSpaceTimeStreams; + s = getSIGBLength(cfg); + numHESIGB = s.NumSIGBSymbols; + else % SU or EXT_SU + numSpaceTimeStreamsPerRU = cfg.NumSpaceTimeStreams; + numHESIGB = 0; + end + ruIdx = cfg.RUIndex; + ruSize = cfg.RUSize; +else % wlanHESUConfig,wlanHEMUConfig,wlanHETBConfig + pktFormat = packetFormat(cfg); + allocInfo = ruInfo(cfg); + if strcmp(pktFormat,'HE-MU') + narginchk(4,4) + ruNumber = varargin{1}; + sigbInfo = wlan.internal.heSIGBCodingInfo(cfg); + numHESIGB = sigbInfo.NumSymbols; + numSpaceTimeStreamsPerRU = allocInfo.NumSpaceTimeStreamsPerRU(ruNumber); + ruSize = allocInfo.RUSizes(ruNumber); + ruIdx = allocInfo.RUIndices(ruNumber); + else + % SU, EXT SU, TB + numHESIGB = 0; + ruIdx = allocInfo.RUIndices; + numSpaceTimeStreamsPerRU = allocInfo.NumSpaceTimeStreamsPerRU; + ruSize = allocInfo.RUSizes; + end +end + +numOFDMSym = size(x,2); +n = (0:numOFDMSym-1); +if strcmp(pktFormat,'HE-EXT-SU') + numHESIGA = 4; +else % SU or MU + numHESIGA = 2; +end + +z = 2+numHESIGA+numHESIGB; % Pilot symbol offset +refPilots = wlan.internal.hePilots(ruSize,numSpaceTimeStreamsPerRU,n,z); + +% Estimate CPE and phase correct symbols +info = wlanHEOFDMInfo('HE-Data',cfg.ChannelBandwidth,cfg.GuardInterval,[ruSize ruIdx]); + +if numel(info.PilotIndices)==size(chanEst,1) + % Assume channel estimate is only for pilots + chanEstPilots = chanEst; +else + % Otherwise extract pilots from channel estimate + chanEstPilots = chanEst(info.PilotIndices,:,:); +end +cpe = wlan.internal.commonPhaseErrorEstimate(x(info.PilotIndices,:,:),chanEstPilots,refPilots); +y = wlan.internal.commonPhaseErrorCorrect(x,cpe); + +end \ No newline at end of file diff --git a/heEqualizeCombine.m b/heEqualizeCombine.m new file mode 100644 index 0000000..dfe6a4a --- /dev/null +++ b/heEqualizeCombine.m @@ -0,0 +1,78 @@ +function [y,csi] = heEqualizeCombine(x,chanEst,nVar,cfg,varargin) +%heEqualizeCombine HE MIMO channel equalization and STBC combining +% +% [Y,CSI] = heEqualizeCombine(X,CHANEST,NOISEVAR,CFGHE) performs +% minimum-mean-square-error (MMSE) frequency domain equalization and +% optionally STBC combining using the signal input X, the channel +% estimate, CHANEST, and noise variance, NVAR. +% +% Y is an estimate of the transmitted frequency domain signal and is of +% size Nsd-by-Nsym-by-Nsts, where Nsd represents the number of carriers +% (frequency domain), Nsym represents the number of symbols (time +% domain), and Nsts represents the number of space-time streams (spatial +% domain). It is complex when either X or CHANEST is complex, or is real +% otherwise. +% +% CSI is a real matrix of size Nsd-by-Nsts containing the soft channel +% state information. +% +% X is a real or complex array containing the frequency domain signal to +% equalize. It is of size Nsd-by-Nsym-by-Nr, where Nr represents the +% number of receive antennas. +% +% CHANEST is a real or complex array containing the channel estimates for +% each carrier and symbol. It is of size Nsd-by-Nsts-by-Nr. +% +% NVAR is a nonnegative scalar representing the noise variance. +% +% CFGHE is the format configuration object of type wlanHESUConfig, +% wlanHETBConfig or wlanHERecoveryConfig. +% +% [Y,CSI] = heEqualizeCombine(X,CHANEST,NVAR,CFGMU,USERIDX) performs +% equalization of a HE multi user transmission. The user index is +% required to extract the space-time streams of interest for the user. +% +% CFGMU is the format configuration object of type wlanHEMUConfig or, +% heTBSystemConfig, which specifies the parameters for multi user +% HE format and trigger-based (HT TB) system format configuration object +% respectively. +% +% USERIDX is the index of the user to decode within the RU. + +% Copyright 2017-2019 The MathWorks, Inc. + +%#codegen + +validateattributes(cfg,{'wlanHESUConfig','wlanHEMUConfig','wlanHETBConfig','heTBSystemConfig','wlanHERecoveryConfig'},{'scalar'},mfilename,'format configuration object'); + +% Defaults +userIdx = 1; +if isa(cfg,'wlanHEMUConfig') + narginchk(5,5) % Require user index for multi-user reception + if nargin>4 + userIdx = varargin{1}; + end + % Get the indices of the space-time streams for this user + allSTSIdx = wlan.internal.heSpaceTimeStreamIndices(cfg); + stsIdx = allSTSIdx(1,userIdx):allSTSIdx(2,userIdx); +elseif isa(cfg,'wlanHERecoveryConfig') && strcmp(cfg.PacketFormat,'HE-MU') + % Get the indices of the space-time streams for this user + stsIdx = cfg.SpaceTimeStreamStartingIndex:(cfg.SpaceTimeStreamStartingIndex+cfg.NumSpaceTimeStreams-1); +else + stsIdx = 1; % For codegen +end + +if cfg.STBC + % Only SU, get num of SS from size of channel estimate + nss = size(chanEst,2)/2; + [y,csi] = wlan.internal.wlanSTBCCombine(x,chanEst,nss,'MMSE',nVar); +else + % Equalize + [y,csi] = helperSymbolEqualize(x,chanEst,nVar); + if isa(cfg,'wlanHEMUConfig') || (isa(cfg,'wlanHERecoveryConfig') && strcmp(cfg.PacketFormat,'HE-MU')) + % Extract used STS + y = y(:,:,stsIdx); + csi = csi(:,stsIdx); + end +end +end \ No newline at end of file diff --git a/heLTFChannelEstimate.m b/heLTFChannelEstimate.m new file mode 100644 index 0000000..ae20216 --- /dev/null +++ b/heLTFChannelEstimate.m @@ -0,0 +1,281 @@ +function [chanEstRU,varargout] = heLTFChannelEstimate(demodHELTFRU,cfg,varargin) +%heLTFChannelEstimate Channel estimation using HE-LTF +% CHANESTRU = heLTFChannelEstimate(RXSYM,CFGHE) returns the estimated +% channel between all space-time streams and receive antennas using +% HE-LTF of an HE single user, extended range single user, multi-user or +% trigger-based (HE TB) packet. The channel estimate includes the +% effect of the applied spatial mapping matrix and cyclic shifts at the +% transmitter. If HE-LTF compression is used, linear interpolation is +% performed to create a channel estimate for all subcarriers. +% +% CHANESTRU is an array characterizing the estimated channel for the data +% and pilot subcarriers. EST is a complex Nst-by-Nsts-by-Nr array +% characterizing the estimated channel for the data and pilot +% subcarriers, where Nst is the number of occupied subcarriers, Nsts is +% the total number of space-time streams, and Nr is the number of receive +% antennas. If CFGHE is a MU configuration, then the channel estimate for +% all RUs is returned. +% +% RXSYM is a complex Nst-by-Nsym-by-Nr array containing demodulated +% concatenated HE-LTF. Nsym is the number of demodulated HE-LTF symbols. +% +% CFGHE is a format configuration object of type wlanHESUConfig +% wlanHEMUConfig, wlanHETBConfig, heTBSystemConfig or wlanHERecoveryConfig. +% +% CHANESTRU = heLTFChannelEstimate(RXSYM,CFGMU,RUOFINTEREST) returns the +% channel estimate for the RU of interest index RUOFINTEREST for a +% multi-user configuration. CFGMU is of type wlanHEMUConfig or +% heTBSystemConfig. If not provided the default is 1. +% +% [...,CHANESTSSPILOTS] = heLTFChannelEstimate(...) additionally returns +% a Nsp-by-Nsym-by-Nr array characterizing the estimated channel for +% pilot subcarrier locations for each symbol, assuming one space-time +% stream at the transmitter. +% +% Examples: +% % Decode the HE data field for each user in and OFDMA and MU-MIMO +% % transmission with a fading channel model. Estimate the channel for +% % each user. +% +% % Create packet configuration +% allocationIndex = [192 193]; % Two 242 RUs, the second with 2 users +% cfg = wlanHEMUConfig(allocationIndex); +% cfg.NumTransmitAntennas = 2; +% cfg.User{1}.NumSpaceTimeStreams = 2; +% +% % Generate MU waveform +% txWaveform = wlanWaveformGenerator([1;0;0;1],cfg); +% +% % Channel and receiver per user +% ind = wlanFieldIndices(cfg); +% allocationInfo = ruInfo(cfg); +% for ruIdx = 1:allocationInfo.NumRUs +% for userIdx = 1:allocationInfo.NumUsersPerRU(ruIdx) +% % Add channel and noise +% snr = 20; +% channel = wlanTGaxChannel; +% channel.NumTransmitAntennas = 2; +% channel.NumReceiveAntennas = 2; +% channel.SampleRate = wlanSampleRate(cfg); +% rxWaveform = awgn(channel([txWaveform; zeros(10,2)]),snr); +% +% % Synchronize +% rxWaveform = rxWaveform(1+4:end,:); +% +% % Extract and OFDM demodulate the HE-LTF for the RU of +% % interest +% rxHETLF = rxWaveform(ind.HELTF(1): ind.HELTF(2),:); +% demodHELTF = wlanHEDemodulate(rxHETLF,'HE-LTF',cfg,ruIdx); +% +% % Channel estimate for RU of interest +% chanEst = heLTFChannelEstimate(demodHELTF,cfg,ruIdx); +% +% % Extract and OFDM demodulate the data field for the RU of +% % interest +% rxData = rxWaveform(ind.HEData(1):ind.HEData(2),:); +% demodData = wlanHEDemodulate(rxData,'HE-Data',cfg,ruIdx); +% +% % Equalize data symbols - extract the space-time streams for +% % the user of interest after equalization +% nVar = 10^-(snr/10); +% [eqSym,csi] = heEqualizeCombine(demodData,chanEst,nVar, ... +% cfg,userIdx); +% +% % Discard pilot carriers and decode +% info = wlanHEOFDMInfo('HE-Data',cfg,ruIdx); +% rxBits = wlanHEDataBitRecover(eqSym(info.DataIndices,:,:), ... +% nVar,csi(info.DataIndices,:),cfg,userIdx); +% end +% end + +% Copyright 2017-2019 The MathWorks, Inc. + +%#codegen + +if nargin>2 + ruOfInterest = varargin{1}; +else + ruOfInterest = 1; +end + +% Validate the format configuration object is a valid type +validateattributes(cfg,{'wlanHESUConfig','wlanHEMUConfig','wlanHETBConfig','heTBSystemConfig','wlanHERecoveryConfig'},{'scalar'},mfilename,'format configuration object'); + +% Get allocation information +if isa(cfg,'wlanHERecoveryConfig') + ruSizeRU = cfg.RUSize; + ruIndexRU = cfg.RUIndex; + pktFormat = cfg.PacketFormat; + if strcmp(pktFormat,'HE-MU') + numSTSRU = cfg.RUTotalSpaceTimeStreams; + else % SU, EXT SU + numSTSRU = cfg.NumSpaceTimeStreams; + end +else + allocInfo = ruInfo(cfg); + coder.internal.errorIf(ruOfInterest>allocInfo.NumRUs,'wlan:he:InvalidRUOfInterest',ruOfInterest,allocInfo.NumRUs); + ruSizeRU = allocInfo.RUSizes(ruOfInterest); + ruIndexRU = allocInfo.RUIndices(ruOfInterest); + numSTSRU = allocInfo.NumSpaceTimeStreamsPerRU(ruOfInterest); + pktFormat = packetFormat(cfg); +end + +% Validate symbol type +validateattributes(demodHELTFRU,{'single','double'},{'3d'},mfilename,'HE-LTF OFDM symbol(s)'); +[numST,numLTF,numRx] = size(demodHELTFRU); +tac = wlan.internal.heRUToneAllocationConstants(ruSizeRU); +coder.internal.errorIf(numST~=tac.NST,'wlan:wlanChannelEstimate:IncorrectNumSC',tac.NST,numST); +ofdmInfo = wlanHEOFDMInfo('HE-LTF',cfg.ChannelBandwidth,cfg.GuardInterval,[ruSizeRU ruIndexRU]); +if numLTF==0 + chanEstRU = zeros(numST,numSTSRU,numRx); + varargout{1} = zeros(numel(ofdmInfo.PilotIndices),numLTF,numRx); % For codegen + return; +end +minNumLTF = wlan.internal.numVHTLTFSymbols(numSTSRU); +coder.internal.errorIf(numLTF1 + chanEstRU = chanEstInterp(chanEstRU,cbw,N_HE_LTF_Mode,ruSizeRU,ruIndexRU); + end +else + % MIMO channel estimation as per Perahia, Eldad, and Robert Stacey. + % Next Generation Wireless LANs: 802.11 n and 802.11 ac. Cambridge + % University Press, 2013, page 100, Equation 4.39. + % Remove orthogonal sequence across subcarriers (if used) + if isaTBConfig && cfg.SingleStreamPilots==false + % Only perform channel estimate for non-pilot subcarriers as pilots + % are single stream + kMIMO = kRU; % All subcarriers MIMO estimates + mimoInd = 1:numST; + chanEstRUMIMO = wlan.internal.mimoChannelEstimate(demodHELTFRU,HELTFRU,numSTSRU); + chanEstRUMIMO = removeOrthogonalSequence(chanEstRUMIMO,numSTSRU,kRU,N_HE_LTF_Mode); + else + % Only perform channel estimate for non-pilot subcarriers as pilots + % are single stream + mimoInd = ofdmInfo.DataIndices; + kMIMO = kRU(mimoInd); % Only data subcarriers MIMO estimates + chanEstRUMIMO = wlan.internal.mimoChannelEstimate(demodHELTFRU(ofdmInfo.DataIndices,:,:),HELTFRU(mimoInd),numSTSRU); + end + + % Undo cyclic shift for each STS before averaging and interpolation + nfft = (cbw/20)*256; + numSTSTotal = size(chanEstRUMIMO,2); + csh = wlan.internal.getCyclicShiftVal('VHT',numSTSTotal,cbw); + chanEstRUMIMO = wlan.internal.cyclicShiftChannelEstimate(chanEstRUMIMO,-csh,nfft,kMIMO); + + % Interpolate over pilot locations, and any compressed subcarriers + chanEstRU = chanEstInterp(chanEstRUMIMO,cbw,N_HE_LTF_Mode,ruSizeRU,ruIndexRU,mimoInd); + + % Re-apply cyclic shift after interpolation + chanEstRU = wlan.internal.cyclicShiftChannelEstimate(chanEstRU,csh,nfft,kRU); +end + +% If extended range SU, then the HE-LTF are boosted by sqrt(2). If we +% don't remove this at demodulation then we must de-scale the channel +% estimate as the data field is not scaled. +if strcmp(pktFormat,'HE-EXT-SU') + eta = 1/sqrt(2); +else + eta = 1; +end +chanEstRU = chanEstRU*eta; % Scale for HE-EXT-SU + +% Channel estimate for pilots +if nargout>1 + if isaTBConfig && cfg.SingleStreamPilots==false + % Create single stream from MIMO pilot estimates by summing across + % space-time streams (2nd dimension) + varargout{1} = sum(chanEstRU(ofdmInfo.PilotIndices,:,:),2); + else + % Channel estimate for single-stream pilots + Pheltf = wlan.internal.mappingMatrix(numLTF); + R = Pheltf(1,1:numLTF); % R matrix changes pilot polarity per symbol + % Estimate the channel at pilot subcarriers accounting for polarity + chanEstSSPilots = bsxfun(@rdivide,demodHELTFRU(ofdmInfo.PilotIndices,:,:),bsxfun(@times,HELTFRU(ofdmInfo.PilotIndices),R)); + varargout{1} = chanEstSSPilots*eta; % Scale for HE_EXT_SU + end +end + +end + +function chanEstRUInterp = chanEstInterp(chanEstRU,cbw,N_HE_LTF_Mode,ruSize,ruIndex,varargin) + % Interpolate over pilot locations and compressed subcarriers + + Nfft = 256*cbw/20; + + % Get the subcarrier indices within the FFT for the channel estimate + % input + kAct = wlan.internal.heRUSubcarrierIndices(cbw,ruSize,ruIndex)+Nfft/2+1; + % If the channelEstRU is not the entire RU, then we need to make sure + % we know the subcarrier indices, so use the ruInd input. For example + % this allows us to pass in only the data subcarriers. + if nargin>5 + ruInd = varargin{1}; + kChanEstInputs = kAct(ruInd); + else + % Assume chanEstRU is the whole RU + kChanEstInputs = kAct; + end + + % Get the indices within the FFT which contain actual estimates + % (excluding the guard bands). This is how the pattern is structured + kAll = 1:N_HE_LTF_Mode:Nfft; + + % Find the subcarrier indices within the FFT which contain actual data + % within the channel estimate input (kToInterp) and the indices of + % these within the chanEstDataRU input array (toInterpInd) + [kToInterp,toInterpInd] = intersect(kChanEstInputs,kAll); + + % Interpolate and extrapolate over all RU subcarrier indices to + % interpolate over compressed region and pilots + magPart = interp1(kToInterp.',abs(chanEstRU(toInterpInd,:,:)),kAct,'linear','extrap'); + phasePart = interp1(kToInterp.',unwrap(angle(chanEstRU(toInterpInd,:,:))),kAct,'linear','extrap'); + [realPart,imagPart] = pol2cart(phasePart,magPart); + chanEstRUInterp = complex(realPart,imagPart); + +end + +function chanEstRUData = removeOrthogonalSequence(chanEstRUData,numSTSRU,k,N_HE_LTF_Mode) + % Remove the orthogonal sequence across subcarriers + M = 0; % Assume space-time streams of all users in estimate + m = 1:numSTSRU; + Pheltf = wlan.internal.mappingMatrix(8); + seq = Pheltf(M+m,mod(ceil(k/N_HE_LTF_Mode)-1,8)+1).'; % Nsts-by-Nst + chanEstRUData = chanEstRUData./seq; +end diff --git a/heMUCalculateSteeringMatrix.m b/heMUCalculateSteeringMatrix.m new file mode 100644 index 0000000..c4f2046 --- /dev/null +++ b/heMUCalculateSteeringMatrix.m @@ -0,0 +1,115 @@ +function steeringMatBF = heMUCalculateSteeringMatrix(steeringMatFB,cfg,cfgNDP,ruIdx) +%heMUCalculateSteeringMatrix Calculate beamforming steering matrix +% +% Note: This is an internal undocumented function and its API and/or +% functionality may change in subsequent releases. +% +% STEERINGMATBF = heMUCalculateSteeringMatrix(STEERINGMATFB,CFG,CFGNDP,RUIDX) +% returns the steering matrix recommended to beamform an RU in a transmit +% beamforming, or MU-MIMO configuration. ZF precoding is used. +% +% STEERINGMATFB is a cell array containing the steering matrices fed-back +% by each user in the RU to beamform. +% +% CFG is the configuration of the HE-MU transmission and is a format +% configuration object of type +% wlanHEMUConfig. +% +% CFGNDP is the configuration of the HE-NDP used to gather feedback and +% is a format configuration object of type +% wlanHESUConfig. +% +% RUIDX is the RU index. + +% Copyright 2018 The MathWorks, Inc. + +allocInfo = ruInfo(cfg); +% Indices of active subcarriers within the RU +ruOFDMInfo = wlanHEOFDMInfo('HE-Data',cfg,ruIdx); +ruInd = ruOFDMInfo.ActiveFrequencyIndices; + +% Indices of active subcarriers in the NDP +ndpOFDMInfo = wlanHEOFDMInfo('HE-Data',cfgNDP); +trainingInd = ndpOFDMInfo.ActiveFrequencyIndices; + +% Get the indices which overlap - use to extract from NDP +[~,scUseInd] = intersect(trainingInd,ruInd); + +% Extract the RU of interest from the full bandwidth grid +numUsers = allocInfo.NumUsersPerRU(ruIdx); +steeringMatUse = cell(numUsers,1); + +for i = 1:numUsers + % Only take the RU subcarriers and space-time streams of + % interest for the current RU and user + userIdx = cfg.RU{ruIdx}.UserNumbers(i); + numSTS = cfg.User{userIdx}.NumSpaceTimeStreams; + numRx = size(steeringMatFB{userIdx},2); + if numSTS>numRx + error('The number of space-time streams (%d) exceeds the number of receive antennas (%d) for user %d',numSTS,numRx,userIdx); + end + steeringMatUse{i} = steeringMatFB{userIdx}(scUseInd,1:numSTS,:); +end + +% Extract steering matrix for each RU +if numUsers>1 + steeringMatBF = muSteeringMatrixFromFeedback(steeringMatUse); +else + steeringMatBF = steeringMatUse{1}; +end + +end + +function steeringMatrix = muSteeringMatrixFromFeedback(mappingMatrix,varargin) +% Q = muSteeringMatrixFromFeedback(QU) calculates the spatial mapping +% matrix for a MU transmission using the ZF algorithm. +% +% Q is an Nst-by-Nsts-by-Nt mapping matrix. Nst is the number of +% subcarriers, Nsts is the total number of space-time streams, and Nt is +% the number of transmit antennas. +% +% QU is a cell array containing the individual mapping matrix for each +% user. Each element of QU is sized Nst-by-Nstsu-by-Nt, where Nstsu is +% the number of space-time streams for the individual user. +% +% Q = muSteeringMatrixFromFeedback(QU,SNR) calculates the spatial mapping +% matrix for a MU transmission using the MMSE algorithm given the SNR. + if nargin>1 + precodingType = 'MMSE'; + snr = varargin{1}; + else + precodingType = 'ZF'; + end + + numUsers = numel(mappingMatrix); + + % Get the number of STS per user + numSTS = zeros(numUsers,1); + for uIdx = 1:numUsers + numSTS(uIdx) = size(mappingMatrix{uIdx},2); + end + numSTSTotal = sum(numSTS); + + % Pack the per user CSI into a matrix + [numST,~,numTx] = size(mappingMatrix{1}); % Number of subcarriers + steeringMatrix = zeros(numST,numTx,numSTSTotal); % Nst-by-Nt-by-Nsts + + for uIdx = 1:numUsers + stsIdx = sum(numSTS(1:uIdx-1))+(1:numSTS(uIdx)); + steeringMatrix(:,:,stsIdx) = permute(mappingMatrix{uIdx},[1 3 2]); % Nst-by-Nt-by-Nsts + end + + % Zero-forcing or MMSE precoding solution + if strcmp(precodingType, 'ZF') + delta = 0; % Zero-forcing + else + delta = (numTx/(10^(snr/10))) * eye(numTx); % MMSE + end + for i = 1:numST + % Channel inversion precoding + h = squeeze(steeringMatrix(i,:,:)); + steeringMatrix(i,:,:) = h/(h'*h + delta); + end + + steeringMatrix = permute(steeringMatrix,[1 3 2]); +end \ No newline at end of file diff --git a/heMURx.m b/heMURx.m new file mode 100644 index 0000000..ae75881 --- /dev/null +++ b/heMURx.m @@ -0,0 +1,373 @@ +function [rxPSDU,bitErrorRate,eqSymPlot,evm] = heMURx(rx,cfg,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot) +% [rxPSDU,eqSymUser,csiData/csi,pktFormat,evm,cfoCorrection]=heMURx(rx,cfgMUMIMO,userIdx,cfgPara); +% numBitError = 0; +% wlan ug +%% Setup Waveform Recovery Parameters +% Perform synchronization with 11ac components +chanBW = cfg.ChannelBandwidth; +fs = wlanSampleRate(cfg); +% Specify pilot tracking method for recovering the data field. This can be: +% 'Joint' - use joint common phase error and sample rate offset tracking +% 'CPE' - use only common phase error tracking +% When recovering 26-tone RUs only CPE tracking is used as the joint +% tracking algorithm is susceptible to noise. +pilotTracking = 'Joint'; +% Create an HE recovery configuration object and set the channel bandwidth +cfgRx = wlanHERecoveryConfig; +cfgRx.ChannelBandwidth = chanBW; + +% Get the field indices for extract fields from the PPDU +% ind = wlanFieldIndices(cfg); +ind = wlanFieldIndices(cfgRx); + +% % Setup plots for the example +% [spectrumAnalyzer,timeScope,ConstellationDiagram,EVMPerSubcarrier,EVMPerSymbol] = heSigRecSetupPlots(fs); + +% Minimum packet length is 10 OFDM symbols +% lstfLength = double(ind.LSTF(2)); +% minPktLen = lstfLength*5; % Number of samples in L-STF + +rxWaveLen = size(rx,1); + +%% Front-End Processing +searchOffset = 0; % Offset from start of waveform in samples +% Packet detection +coarsePktOffset = wlanPacketDetect(rx,chanBW,searchOffset,0.8); +LSTF = wlanLSTF(wlanNonHTConfig); +% if isempty(coarsePktOffset)||(coarsePktOffset + ind.LSIG(2) > rxWaveLen) % If empty no L-STF detected; packet error +% numBitError = numBitError+numBits;%%??????????????? +% userIdx = userIdx+1; +% end +% Coarse frequency offset estimation and correction using L-STF +if isempty(coarsePktOffset) + coarsePktOffset = 0; +end +rxLSTF = rx(coarsePktOffset+(ind.LSTF(1):ind.LSTF(2)), :); +tmp_xcorr = abs(xcorr(LSTF,rxLSTF)); +coarseFreqOffset = wlanCoarseCFOEstimate(rxLSTF,chanBW); +rx = helperFrequencyOffset(rx,fs,-coarseFreqOffset); +% Extract the non-HT fields and determine fine packet offset +nonhtfields = rx(coarsePktOffset+(ind.LSTF(1):ind.LSIG(2)),:); +finePktOffset = wlanSymbolTimingEstimate(nonhtfields,chanBW); + +% Determine final packet offset +pktOffset = coarsePktOffset+finePktOffset; + +% if pktOffset>50%%????????????????????????????/// +% % numPacketErrors(userIdx) = numPacketErrors(userIdx)+1; +% numBitError = numBitError+numBits; +% userIdx = userIdx+1; +% % continue; % Go to next loop iteration +% end + +% Extract L-LTF and perform fine frequency offset correction +rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:); +fineFreqOff = wlanFineCFOEstimate(rxLLTF,chanBW); +rx = helperFrequencyOffset(rx,fs,-fineFreqOff); + +% Timing synchronization complete: packet detected +fprintf('Packet detected at index %d\n',pktOffset + 1); + +% Display estimated carrier frequency offset +cfoCorrection = coarseFreqOffset + fineFreqOff; % Total CFO +fprintf('Estimated CFO: %5.1f Hz\n\n',cfoCorrection); + + +if max(tmp_xcorr)>50 + +% Scale the waveform based on L-STF power (AGC) +gain = 1./(sqrt(mean(rxLSTF.*conj(rxLSTF)))); +rx = rx.*gain; + +%% Packet Format Detection + +rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:); +lltfDemod = wlanLLTFDemodulate(rxLLTF,chanBW); +lltfChanEst = wlanLLTFChannelEstimate(lltfDemod,chanBW); +noiseVar = helperNoiseEstimate(lltfDemod); + +rxSIGA = rx(pktOffset+(ind.LSIG(1):ind.HESIGA(2)),:); +pktFormat = wlanFormatDetect(rxSIGA,lltfChanEst,noiseVar,chanBW); +fprintf(' %s packet detected\n\n',pktFormat); + +% Set the packet format in the recovery object and update the field indices +cfgRx.PacketFormat = pktFormat; +ind = wlanFieldIndices(cfgRx); + +%% L-LTF Channel Estimate +lltfDemod = wlanHEDemodulate(rxLLTF,'L-LTF',chanBW); +lltfChanEst = wlanLLTFChannelEstimate(lltfDemod,chanBW); + +%% L-SIG and RL-SIG Decoding +% Extract L-SIG and RL-SIG fields +rxLSIG = rx(pktOffset+(ind.LSIG(1):ind.RLSIG(2)),:); + +% OFDM demodulate +helsigDemod = wlanHEDemodulate(rxLSIG,'L-SIG',chanBW); + +% Estimate CPE and phase correct symbols +helsigDemod = preHECommonPhaseErrorTracking(helsigDemod,lltfChanEst,'L-SIG',chanBW); + +% Estimate channel on extra 4 subcarriers per subchannel and create full +% channel estimate +preheInfo = wlanHEOFDMInfo('L-SIG',chanBW); +preHEChanEst = preHEChannelEstimate(helsigDemod,lltfChanEst,preheInfo.NumSubchannels); + +% Average L-SIG and RL-SIG before equalization +helsigDemod = mean(helsigDemod,2); + +% Equalize data carrying subcarriers, merging 20 MHz subchannels +[eqLSIGSym,csi] = preHESymbolEqualize(helsigDemod(preheInfo.DataIndices,:,:), ... + preHEChanEst(preheInfo.DataIndices,:,:),noiseVar,preheInfo.NumSubchannels); + +% Decode L-SIG field +[~,failCheck,lsigInfo] = wlanLSIGBitRecover(eqLSIGSym,noiseVar,csi); + +if failCheck + disp(' ** L-SIG check fail **'); +else + disp(' L-SIG check pass'); +end +% Get the length information from the recovered L-SIG bits and update the +% L-SIG length property of the recovery configuration object +lsigLength = lsigInfo.Length; +cfgRx.LSIGLength = lsigLength; + +% Measure EVM of L-SIG symbols +EVM = comm.EVM; +EVM.ReferenceSignalSource = 'Estimated from reference constellation'; +EVM.Normalization = 'Average constellation power'; +EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK'); +rmsEVM = EVM(eqLSIGSym); +evm = 20*log10(rmsEVM/100); +fprintf(' L-SIG EVM: %2.2fdB\n\n',20*log10(rmsEVM/100)); + +% Calculate the receive time and corresponding number of samples in the +% packet +RXTime = ceil((lsigLength + 3)/3) * 4 + 20; % In microseconds +numRxSamples = round(RXTime * 1e-6 * fs); % Number of samples in time + +%% HE-SIG-A Decoding +rxSIGA = rx(pktOffset+(ind.HESIGA(1):ind.HESIGA(2)),:); +sigaDemod = wlanHEDemodulate(rxSIGA,'HE-SIG-A',chanBW); +hesigaDemod = preHECommonPhaseErrorTracking(sigaDemod,preHEChanEst,'HE-SIG-A',chanBW); + +% Equalize data carrying subcarriers, merging 20 MHz subchannels +preheInfo = wlanHEOFDMInfo('HE-SIG-A',chanBW); +[eqSIGASym,csi] = preHESymbolEqualize(hesigaDemod(preheInfo.DataIndices,:,:), ... + preHEChanEst(preheInfo.DataIndices,:,:), ... + noiseVar,preheInfo.NumSubchannels); +% Recover HE-SIG-A bits +[sigaBits,failCRC] = wlanHESIGABitRecover(eqSIGASym,noiseVar,csi); + +% Perform the CRC on HE-SIG-A bits +if failCRC + disp(' ** HE-SIG-A CRC fail **'); +else + disp(' HE-SIG-A CRC pass'); +end + +% Measure EVM of HE-SIG-A symbols +% release(EVM); +% if strcmp(pktFormat,'HE-EXT-SU') +% % The second symbol of an HE-SIG-A field for an HE-EXT-SU packet is +% % QBPSK. +% EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK',[0 pi/2 0 0]); +% % Account for scaling of L-LTF for an HE-EXT-SU packet +% rmsEVM = EVM(eqSIGASym*sqrt(2)); +% else +% EVM.ReferenceConstellation = wlanReferenceSymbols('BPSK'); +% rmsEVM = EVM(eqSIGASym); +% end +% fprintf(' HE-SIG-A EVM: %2.2fdB\n\n',20*log10(mean(rmsEVM)/100)); + +%% Interpret Recovered HE-SIG-A bits +cfgRx = interpretHESIGABits(cfgRx,sigaBits); +ind = wlanFieldIndices(cfgRx); % Update field indices +% disp(cfgRx) + + +%% HE-SIG-B Decoding +if strcmp(pktFormat,'HE-MU') + if ~cfgRx.SIGBCompression + s = getSIGBLength(cfgRx); + % Get common field symbols. The start of HE-SIG-B field is known + rxSym = rx(pktOffset+(ind.HESIGA(2)+(1:s.NumSIGBCommonFieldSamples)),:); + % Decode HE-SIG-B common field + [status,cfgRx] = heSIGBCommonFieldDecode(rxSym,preHEChanEst,noiseVar,cfgRx); + + % CRC on HE-SIG-B content channels + if strcmp(status,'Success') + fprintf(' HE-SIG-B (common field) CRC pass\n'); + elseif strcmp(status,'ContentChannel1CRCFail') + fprintf(' ** HE-SIG-B CRC fail for content channel-1\n **'); + elseif strcmp(status,'ContentChannel2CRCFail') + fprintf(' ** HE-SIG-B CRC fail for content channel-2\n **'); + elseif any(strcmp(status,{'UnknownNumUsersContentChannel1','UnknownNumUsersContentChannel2'})) + error(' ** Unknown packet length, discard packet\n **'); + else + % Discard the packet if all HE-SIG-B content channels fail + error(' ** HE-SIG-B CRC fail **'); + end + % Update field indices as the number of HE-SIG-B symbols are + % updated + ind = wlanFieldIndices(cfgRx); + end + + % Get complete HE-SIG-B field samples + rxSIGB = rx(pktOffset+(ind.HESIGB(1):ind.HESIGB(2)),:); + fprintf(' Decoding HE-SIG-B user field... \n'); + % Decode HE-SIG-B user field + [failCRC,cfgUsers] = heSIGBUserFieldDecode(rxSIGB,preHEChanEst,noiseVar,cfgRx); + %*********************************03.22淇敼********************************* + for ii = 1:numel(cfgUsers) + if cfgUsers{ii}.STAID == userIdx + user = cfgUsers{ii}; + break; + end + end +% user = cfgUsers{1}; + %*********************************03.22淇敼********************************* + % CRC on HE-SIG-B users + if ~all(failCRC) + fprintf(' HE-SIG-B (user field) CRC pass\n\n'); + numUsers = numel(cfgUsers); + elseif all(failCRC) + % Discard the packet if all users fail the CRC + error(' ** HE-SIG-B CRC fail for all users **'); + else + fprintf(' ** HE-SIG-B CRC fail for at least one user\n **'); + % Only process users with valid CRC + numUsers = numel(cfgUsers); + end + +else % HE-SU, HE-EXT-SU + cfgUsers = {cfgRx}; + numUsers = 1; +end + +%% HE-Data Decoding +cfgDataRec = trackingRecoveryConfig; +cfgDataRec.PilotTracking = pilotTracking; +% Get recovery configuration object for each user +user = cfgUsers{userIdx}; +if strcmp(pktFormat,'HE-MU') + fprintf(' Decoding User:%d, STAID:%d, RUSize:%d\n',userIdx,user.STAID,user.RUSize); +else + fprintf(' Decoding RUSize:%d\n',user.RUSize); +end + +heInfo = wlanHEOFDMInfo('HE-Data',chanBW,user.GuardInterval,[user.RUSize user.RUIndex]); + +% HE-LTF demodulation and channel estimation +rxHELTF = rx(pktOffset+(ind.HELTF(1):ind.HELTF(2)),:); +heltfDemod = wlanHEDemodulate(rxHELTF,'HE-LTF',chanBW,user.GuardInterval, ... + user.HELTFType,[user.RUSize user.RUIndex]); +[chanEst,pilotEst] = heLTFChannelEstimate(heltfDemod,user); + +% Number of expected data OFDM symbols +symLen = heInfo.FFTLength+heInfo.CPLength; +numOFDMSym = (ind.HEData(2)-ind.HEData(1)+1)/symLen; +numOFDMSym = double(numOFDMSym); + +% HE-Data demodulation with pilot phase and timing tracking +% Account for extra samples when extracting data field from the packet +% for sample rate offset tracking. Extra samples may be required if the +% receiver clock is significantly faster than the transmitter. +maxSRO = 120; % Parts per million +Ne = ceil(numRxSamples*maxSRO*1e-6); % Number of extra samples +Ne = min(Ne,rxWaveLen-numRxSamples); % Limited to length of waveform +numRxSamplesProcess = numRxSamples+Ne; +rxData = rx(pktOffset+(ind.HEData(1):numRxSamplesProcess),:); +if user.RUSize==26 + % Force CPE only tracking for 26-tone RU as algorithm susceptible + % to noise + cfgDataRec.PilotTracking = 'CPE'; +else + cfgDataRec.PilotTracking = pilotTracking; +end + [demodSym,cpe,peg] = heTrackingOFDMDemodulate(rxData,chanEst,numOFDMSym,user,cfgDataRec); + +% Estimate noise power in HE fields +demodPilotSym = demodSym(heInfo.PilotIndices,:,:); +nVarEst = heNoiseEstimate(demodPilotSym,pilotEst,user); + +% Equalize +[eqSym,csi] = heEqualizeCombine(demodSym,chanEst,nVarEst,user); + +% Discard pilot subcarriers +eqSymUser = eqSym(heInfo.DataIndices,:,:); +csiData = csi(heInfo.DataIndices,:); +eqSymPlot = eqSymUser(1:end); + +% scatterplot +% eqSymplot = eqSymUser(1:end); +% scatterplot(eqSymplot); + +% Demap and decode bits +rxPSDUbits = wlanHEDataBitRecover(eqSymUser,nVarEst,csiData,user,'LDPCDecodingMethod','layered-bp'); +% rxPSDU(:,frameIdx) = RX_CRC32(double(rxPSDUbits)); +rxPSDU(:,frameIdx) = rxPSDUbits; +% Compare bit error +% bitError = sum(txPSDU{userIdx}~=rxPSDU); +% numBitError = numBitError+bitError; + +% Measure EVM of HE-Data symbols +release(EVM); +EVM.ReferenceConstellation = wlanReferenceSymbols(user); +rmsEVM = EVM(eqSymUser(:)); +fprintf(' HE-Data EVM:%2.2fdB\n\n',20*log10(rmsEVM/100)); +rx = rx(pktOffset+ind.HEData(2)+100:end,:); +% && mean(abs(rx)) > mean_power/2 +% else + +% if length(rx) > ind.HEData(2) +frameIdx = frameIdx+1; +[rxPSDU,bitErrorRate,eqSymPlot] = heMURx(rx,cfg,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot); +else + [~,numPkt] = size(rxPSDU); + psduLength = length(rxPSDU(:,1))/8; + + switch cfgUI.DataType + case 'test' + txPSDU = DataGenerate(cfgUI,numPkt,SEED,psduLength,0); + txPSDUuser = txPSDU{userIdx};% BER璁$畻鏂瑰紡 鐢ㄦ敹鍙戠害瀹氱殑绉嶅瓙鏁颁骇鐢燂紝瑙5涓寘纭畾ACK涓暟 + bitError = sum(txPSDUuser(1:end,:)~=rxPSDU(1:length(txPSDUuser(1:end,:)),:)); + bitErrorRate = bitError/length(rxPSDU); + ACK = (bitErrorRate == rxPSDU(end)); + case 'text' + case 'photo' + [~,numPkt] = size(rxPSDU); + bitstream = reshape(rxPSDU,1,[]); % 鎭㈠姣旂壒鍙樻瘮鐗规祦 + imgLen = bin2dec(num2str(bitstream(1:16))); +% bitstream = bitstream(1:len*width*8); % 鍙栧浘鍍忓儚绱犲ぇ灏 + bit_array = reshape(bitstream,8,[])'; + byte_array = num2str(bit_array); + img = bin2dec(byte_array); % 浜岃繘鍒惰浆鍗佽繘鍒 + if imgLen+3 > length(img) + imgLen = length(img)-3; + end + rxPSDU = int16(img(1:imgLen+3)); % double杞琲nt16 + rxCRC = 0; + for i = 1:imgLen+2 + rxCRC = mod(rxCRC+rxPSDU(i),256); + end + if rxCRC == rxPSDU(end) + disp('Pass RX Sum Check!'); + bitErrorRate = zeros(1,numPkt); + else + disp('Fail RX Sum Check!'); + bitErrorRate = ones(1,numPkt); + end +% img = mat2gray(rxPSDU); % 鎭㈠鍑虹殑鏁版嵁鎸夊浘鍍忓ぇ灏忔帓鍒 +% imshow(img); % 灞曠ず鍥惧儚 +% pause(1); +% close; + ACK = (rxCRC == rxPSDU(end)); + end + save(['TXPackets\ACKfeedback_for_User' num2str(userIdx) '.mat'],'ACK'); +% scatterplot(eqSymPlot(1:end)); +% pause(1); +% close; +end +end diff --git a/heNoiseEstimate.m b/heNoiseEstimate.m new file mode 100644 index 0000000..4bf8ce5 --- /dev/null +++ b/heNoiseEstimate.m @@ -0,0 +1,106 @@ +function [nest,sigest] = heNoiseEstimate(x,chanEstSSPilots,cfg,varargin) +%heNoiseEstimate Estimate noise power using HE data field pilots +% +% NEST = heNoiseEstimate(x,CHANESTSSPILOTS,CFGHE) estimates the mean +% noise power in watts using the demodulated pilot symbols in the HE data +% field and single-stream channel estimates at pilot subcarriers. The +% noise estimate is averaged over the number of symbols and receive +% antennas. +% +% X is a complex Nsp-by-Nsym-by-Nr array containing demodulated pilot +% subcarrier in HE data field. Nsym is the number of demodulated HE-Data +% symbols. +% +% CHANESTSSPILOTS is a complex Nsp-by-Nltf-by-Nr array containing the +% channel gains at pilot subcarrier locations for each symbol, assuming +% one space-time stream at the transmitter. Nltf is the number of HE-LTF +% symbols. +% +% CFGHE is a format configuration object of type wlanHESUConfig, +% wlanHETBConfig or wlanHERecoveryConfig. +% +% NEST = heNoiseEstimate(X,CHANESTSSPILOTS,CFGMU,RUIDX) performs noise +% power estimation for the multi user HE format input X. +% +% CFGMU is the format configuration object of type wlanHEMUConfig or +% heTBSystemConfig. +% +% RUIDX is the RU (resource unit) index. +% +% [NEST,SIGEST] = heNoiseEstimate(...) additionally returns an estimate +% of the signal power. + +% Copyright 2018-2019 The MathWorks, Inc. + +%#codegen + +validateattributes(cfg,{'wlanHESUConfig','wlanHEMUConfig','wlanHETBConfig','wlanHERecoveryConfig','heTBSystemConfig'},{'scalar'},mfilename,'format configuration object'); + +numOFDMSym = size(x,2); +n = (0:numOFDMSym-1); + +ruIdx = 1; +if isa(cfg,'wlanHEMUConfig') + narginchk(4,4) + ruIdx = varargin{1}; + sigbInfo = wlan.internal.heSIGBCodingInfo(cfg); + numHESIGB = sigbInfo.NumSymbols; + pktFormat = packetFormat(cfg); + allocInfo = ruInfo(cfg); + ruSize = allocInfo.RUSizes(ruIdx); +elseif isa(cfg,'heTBSystemConfig') + numHESIGB = 0; + ruIdx = varargin{1}; + pktFormat = packetFormat(cfg); + allocInfo = ruInfo(cfg); + ruSize = allocInfo.RUSizes(ruIdx); +elseif isa(cfg,'wlanHERecoveryConfig') + ruSize = cfg.RUSize; + pktFormat = cfg.PacketFormat; + if strcmp(pktFormat,'HE-MU') + s = getSIGBLength(cfg); + numHESIGB = s.NumSIGBSymbols; + else + numHESIGB = 0; + end +else + % SU, EXT SU, TB + numHESIGB = 0; + pktFormat = packetFormat(cfg); + allocInfo = ruInfo(cfg); + ruSize = allocInfo.RUSizes(ruIdx); +end + +if strcmp(pktFormat,'HE-EXT-SU') + numHESIGA = 4; +else % SU or MU + numHESIGA = 2; +end + +z = 2+numHESIGA+numHESIGB; % Pilot symbol offset +% Get the reference pilots for one space-time stream, pilot sequence same +% for all space-time streams +refPilots = wlan.internal.hePilots(ruSize,1,n,z); % Nsp-by-Nsym-by-1 + +% Average single-stream pilot estimates over symbols (2nd dimension) +avChanEstSSPilots = mean(chanEstSSPilots,2); % Nsp-by-1-by-Nrx + +% Estimate channel at pilot location using least square estimates +chanEstPilotsLoc = bsxfun(@rdivide,x,refPilots); % Nsp-by-Nsym-by-Nrx + +% Subtract the noisy least squares estimates of the channel at pilot symbol +% locations from the noise averaged single stream pilot symbol estimates of +% the channel +error = bsxfun(@minus,chanEstPilotsLoc,avChanEstSSPilots); % Nsp-by-Nsym-by-Nrx + +% Get power of error and average over pilot symbols, subcarriers and +% receive antennas +useIdx = ~isnan(error); % NaNs may exist in 1xHELTF +nest = real(mean(error(useIdx).*conj(error(useIdx)),'all')); % For codegen + +if nargout>1 + % Get power of channel estimate at pilot locations + sigest = real(mean(chanEstPilotsLoc(:).*conj(chanEstPilotsLoc(:)))); % For codegen +end + +end \ No newline at end of file diff --git a/hePlotEQConstellation.m b/hePlotEQConstellation.m new file mode 100644 index 0000000..43ce46c --- /dev/null +++ b/hePlotEQConstellation.m @@ -0,0 +1,50 @@ +function hePlotEQConstellation(eqDataSym,cfgRx,ConstellationDiagram,varargin) +% hePlotEQConstellation Plot equalized constellation for all spatial streams +% +% hePlotEQConstellation(EQDATASYM,CFGRX,CONSTELLATIONDIAGRAM) plots +% equalized constellation for all spatial streams. +% +% EQDATASYM are the demodulated HE-Data field OFDM symbols for a user, +% specified as a Nsd-by-Nsym-by-Nss matrix of real or complex values, +% where Nsd is the number of data subcarriers in the HE-Data field and +% Nsym is the number of OFDM symbols, and Nss is the number of spatial +% streams. +% +% CFGRX is the format configuration object of type wlanHERecoveryConfig. +% +% CONSTELLATIONDIAGRAM is a system object of type comm.ConstellationDiagram. +% +% hePlotEQConstellation(...,USERIDX,NUMUSERS) displays user number and +% number of user information in the figure title. + +% Copyright 2019 The MathWorks, Inc. + +if nargin==5 + userIdx = varargin{1}; + numUsers = varargin{2}; +elseif nargin==4 + userIdx = varargin{1}; + numUsers = 1; +end + +[Nsd,Nsym,Nss] = size(eqDataSym); +eqDataSymPerSS = reshape(eqDataSym,Nsd*Nsym,Nss); + +str = cell(Nss,1); +for iss=1:Nss + str{iss} = sprintf('Spatial stream %d',iss); +end + +ConstellationDiagram.ReferenceConstellation = wlanReferenceSymbols(cfgRx); +ConstellationDiagram(eqDataSymPerSS); +show(ConstellationDiagram); + +if nargin>3 + ConstellationDiagram.Name = sprintf('Equalized data symbols, user #%d/%d',userIdx,numUsers); +else + ConstellationDiagram.Name = sprintf('Equalized data symbols'); +end +ConstellationDiagram.ChannelNames = str; +release(ConstellationDiagram); + +end \ No newline at end of file diff --git a/hePlotEVMPerSubcarrier.m b/hePlotEVMPerSubcarrier.m new file mode 100644 index 0000000..c6ef09d --- /dev/null +++ b/hePlotEVMPerSubcarrier.m @@ -0,0 +1,76 @@ +function rmsEVM = hePlotEVMPerSubcarrier(eqDataSym,cfgRx,evmSubcarrierPlot,varargin) +% hePlotEVMPerSubcarrier Plots EVM per subcarrier for all spatial streams +% +% RMSEVM = hePlotEVMPerSubcarrier(EQDATASYM,CFGRX,EVMSUBCARRIERPLOT) +% plots EVM per subcarriers averaged over symbols for all spatial +% streams. +% +% RMSEVM is the EVM of EQDATASYM in decibels. +% +% EQDATASYM are the demodulated HE-Data field OFDM symbols for a user, +% specified as a Nsd-by-Nsym-by-Nss matrix of real or complex values, +% where Nsd is the number of data subcarriers in the HE-Data field and +% Nsym is the number of OFDM symbols, and Nss is the number of spatial +% streams. +% +% CFGRX is the format configuration object of type wlanHERecoveryConfig, +% wlanHESUConfig, or wlanHETBConfig. +% +% EVMSUBCARRIERPLOT is a system object of type dsp.ArrayPlot. +% +% RMSEVM = hePlotEVMPerSubcarrier(...,USERIDX,NUMUSERS) displays user +% number and number of user information in the figure title. + +% Copyright 2019-2020 The MathWorks, Inc. + +[Nsd,~,Nss] = size(eqDataSym); +rmsEVMPerSC = zeros(Nsd,Nss); + +if nargin == 5 + userIdx = varargin{1}; + numUsers = varargin{2}; +elseif nargin == 4 + userIdx = varargin{1}; + numUsers = 1; % Number of users are unknown, prefix this to 1 +end + +EVM = comm.EVM; +EVM.ReferenceSignalSource = 'Estimated from reference constellation'; +EVM.ReferenceConstellation = wlanReferenceSymbols(cfgRx); + +for iss = 1:Nss + for isd = 1:Nsd + rmsEVMPerSC(isd,iss) = EVM(eqDataSym(isd,:,iss).'); + release(EVM); + end +end + +if isa(cfgRx,'wlanHERecoveryConfig') + ofdmInfo = wlanHEOFDMInfo('HE-Data',cfgRx.ChannelBandwidth,cfgRx.GuardInterval,[cfgRx.RUSize cfgRx.RUIndex]); +else + ofdmInfo = wlanHEOFDMInfo('HE-Data',cfgRx); +end +dataInd = ofdmInfo.ActiveFFTIndices(ofdmInfo.DataIndices); +Nfft = ofdmInfo.FFTLength; + +evmFFT = nan(Nfft,Nss); +rmsEVM = 20*log10(rmsEVMPerSC/100); +evmFFT(dataInd,:) = rmsEVM; +evmSubcarrierPlot.XOffset = -Nfft/2; + +str = cell(Nss,1); +for iss=1:Nss + str{iss} = sprintf('Spatial stream %d',iss); +end + +evmSubcarrierPlot.ChannelNames = str; +if nargin>3 + evmSubcarrierPlot.Name = sprintf('EVM per subcarrier, user#%d/%d',userIdx,numUsers); +else + evmSubcarrierPlot.Name = sprintf('EVM per subcarrier'); +end +evmSubcarrierPlot(evmFFT) +evmSubcarrierPlot.show +release(evmSubcarrierPlot); + +end \ No newline at end of file diff --git a/hePlotEVMPerSymbol.m b/hePlotEVMPerSymbol.m new file mode 100644 index 0000000..a343503 --- /dev/null +++ b/hePlotEVMPerSymbol.m @@ -0,0 +1,57 @@ +function rmsEVM = hePlotEVMPerSymbol(eqDataSym,cfgRx,evmSymPlot,varargin) +% hePlotEVMPerSymbol Plots EVM per symbols for all spatial streams +% +% RMSEVM = hePlotEVMPerSymbol(EQDATASYM,CFGRX,EVMSYMPLOT) plots EVM per +% symbol averaged over subcarriers for all spatial streams. +% +% RMSEVM is the EVM of EQDATASYM in decibels. +% +% EQDATASYM are the demodulated HE-Data field OFDM symbols for a user, +% specified as a Nsd-by-Nsym-by-Nss matrix of real or complex values, +% where Nsd is the number of data subcarriers in the HE-Data field and +% Nsym is the number of OFDM symbols, and Nss is the number of spatial +% streams. +% +% CFGRX is the format configuration object of type wlanHERecoveryConfig. +% +% EVMSYMPLOT is a system object of type dsp.ArrayPlot. +% +% RMSEVM = hePlotEVMPerSymbol(...,USERIDX,NUMUSERS) displays user +% number and number of user information in the figure title. + +% Copyright 2019 The MathWorks, Inc. + +[~,~,Nss] = size(eqDataSym); + +if nargin == 5 + userIdx = varargin{1}; + numUsers = varargin{2}; +elseif nargin == 4 + userIdx = varargin{1}; + numUsers = 1; % Number of users are unKnown, prefix this to 1 +end + +EVM = comm.EVM; +EVM.ReferenceSignalSource = 'Estimated from reference constellation'; +EVM.ReferenceConstellation = wlanReferenceSymbols(cfgRx); + +rmsEVMPerSym = permute(EVM(eqDataSym),[2 3 1]); + +str = cell(Nss,1); +for iss=1:Nss + str{iss} = sprintf('Spatial stream %d',iss); +end + +evmSymPlot.ChannelNames = str; +if nargin>3 + evmSymPlot.Name = sprintf('EVM per symbol, user#%d/%d',userIdx,numUsers); +else + evmSymPlot.Name = sprintf('EVM per symbol'); +end + +rmsEVM = 20*log10(rmsEVMPerSym/100); +evmSymPlot(rmsEVM); +evmSymPlot.show +release(evmSymPlot); + +end \ No newline at end of file diff --git a/heSIGBCommonFieldDecode.m b/heSIGBCommonFieldDecode.m new file mode 100644 index 0000000..d41ce32 --- /dev/null +++ b/heSIGBCommonFieldDecode.m @@ -0,0 +1,91 @@ +function [status,cfgRx,commonBits,eqCommonSym,failInterpretation] = heSIGBCommonFieldDecode(rx,chanEst,noiseVar,cfgRx,varargin) +%heSIGBCommonFieldDecode Decode HE-SIG-B common field +% +% [STATUS,CFGRX] = heSIGBCommonFieldDecode(RX,CHANEST,NOISEVAR,CFGRX) +% decode the HE-SIG-B common field given the HE-SIG-B common field +% samples, channel estimate, CHANEST, noise variance, NOISEVAR and +% recovery configuration object CFGRX. +% +% STATUS represents the result of content channel decoding, and is +% returned as a character vector. The STATUS output is determined by the +% combination of cyclic redundancy check (CRC) per content channel and +% the number of HE-SIG-B symbols signaled in HE-SIG-A field: +% +% Success - CRC passed for all content channels +% ContentChannel1CRCFail - CRC failed for content channel-1 and +% the number of HE-SIG-B symbols is less +% than 16. +% ContentChannel2CRCFail - CRC failed for content channel-2 and +% the number of HE-SIG-B symbols is less +% than 16. +% UnknownNumUsersContentChannel1 - CRC failed for content channel-1 and +% the number of HE-SIG-B symbols is +% equal to 16. +% UnknownNumUsersContentChannel2 - CRC failed for content channel-2 and +% the number of HE-SIG-B symbols is +% equal to 16. +% AllContentChannelCRCFail - CRC failed for all content channels. +% +% CFGRX is an updated format configuration object of type +% wlanHERecoveryConfig after HE-SIG-B common field decoding. +% +% RX are the HE-SIG-B common field samples. The number of common field +% samples depends on the channel bandwidth as defined in Table 27-23 of +% IEEE P802.11ax/D4.1. +% +% CHANEST is a complex Nst-by-1-by-Nr array containing the estimated +% channel at data and pilot subcarriers, where Nst is the number of +% occupied subcarriers and Nr is the number of receive antennas. +% +% NOISEVAR is the noise variance estimate, specified as a nonnegative +% scalar. +% +% CFGRX is the format configuration object of type wlanHERecoveryConfig +% and specifies the parameters for the HE-MU format. +% +% [...,FAILINTERPRETATION] = heSIGBCommonFieldDecode(...,SUPPRESSERROR) +% controls the behavior of the function due to an unexpected value of the +% interpreted HE-SIG-B common field bits. SUPPRESSERROR is logical. When +% SUPPRESSERROR is true and the function cannot interpret the recovered +% HE-SIG-B common field bits due to an unexpected value, the function +% returns FAILINTERPRETATION as true and cfgMU is unchanged. When +% SUPPRESSERROR is false and the function cannot interpret the recovered +% HE-SIG-B common field bits due to an unexpected value, an exception is +% issued, and the function does not return an output. The default is +% false. + +% Copyright 2018-2020 The MathWorks, Inc. + +suppressError = false; % Control the validation of the interpreted HE-SIG-B common field bits +failInterpretation = false; +if nargin>4 + suppressError = varargin{1}; +end +chanBW = cfgRx.ChannelBandwidth; + +% Demodulate HE-SIG-B Common field +demodCommonSym = wlanHEDemodulate(rx,'HE-SIG-B',chanBW); + +% Extract data and pilots symbols +preheInfo = wlanHEOFDMInfo('HE-SIG-A',chanBW); +demodCommonData = demodCommonSym(preheInfo.DataIndices,:,:); +demodCommonPilot = demodCommonSym(preheInfo.PilotIndices,:,:); + +% Estimate and correct common phase error +demodCommonData = heCPECorrection(demodCommonData,demodCommonPilot,chanEst(preheInfo.PilotIndices,:,:),chanBW); + +% Merge channels +[commonOne20MHz,chanEstOne20MHz] = heSIGBMergeSubchannels(demodCommonData,chanEst(preheInfo.DataIndices,:,:),chanBW); + +% Perform equalization +[eqCommonSym,csiData] = preHESymbolEqualize(commonOne20MHz,chanEstOne20MHz,noiseVar); + +% Decode HE-SIG-B common field +if suppressError + [commonBits,status] = wlanHESIGBCommonBitRecover(eqCommonSym,noiseVar,csiData,cfgRx); + [cfgRx,failInterpretation] = interpretHESIGBCommonBits(cfgRx,commonBits,status); +else + [commonBits,status,cfgRx] = wlanHESIGBCommonBitRecover(eqCommonSym,noiseVar,csiData,cfgRx); +end + +end \ No newline at end of file diff --git a/heSIGBMergeSubchannels.m b/heSIGBMergeSubchannels.m new file mode 100644 index 0000000..2e712df --- /dev/null +++ b/heSIGBMergeSubchannels.m @@ -0,0 +1,44 @@ +function [demodContentCh,chanEstContentCh] = heSIGBMergeSubchannels(rx,chanEst,chanBW) +%heSIGBMergeSubchannels Merge 20MHz HE-SIG-B subchannels +% +% [DEMODCONTENTCH,CHANESTCONTENTCH] = +% heSIGBMergeSubchannels(RX,CHANEST,CHANBW) returns the demodulated +% HE-SIG-B symbols and channel estimates after merging 20MHz subchannels +% for the given channel bandwidth. +% +% DEMODCONTENTCH and CHANESTCONTENTCH are the merged 20MHz subchannels +% for the channel bandwidth of interest. +% +% RX are the demodulated HE-SIG-B samples. +% +% CHANEST is a real or complex array containing the channel estimates for +% each carrier. It is of size Nst-by-1-by-Nr. +% +% CHANBW is a character vector or string. The allowed channel bandwidth +% are 'CBW20', 'CBW40', 'CBW80' and 'CBW160'. + +% Copyright 2018 The MathWorks, Inc. + +numST = size(rx,1); +numSym = size(rx,2); + +if any(numST==[52 104 208 416]) + % Input is data + pilots + per20 = 104; +else + % Input is only data + per20 = 112; +end + +switch chanBW + case {'CBW80','CBW160'} + demodContentCh = permute(reshape(permute(rx,[1 3 2]),per20,[],numSym),[1 3 2]); + numTx = 1; % For L-LTF + chanEstContentCh = permute(reshape(permute(chanEst,[1 3 2]),per20,[],numTx),[1 3 2]); + otherwise + demodContentCh = rx; + % numTx = 1; % For L-LTF + chanEstContentCh = chanEst; +end + +end \ No newline at end of file diff --git a/heSIGBUserFieldDecode.m b/heSIGBUserFieldDecode.m new file mode 100644 index 0000000..59e5081 --- /dev/null +++ b/heSIGBUserFieldDecode.m @@ -0,0 +1,77 @@ +function [failCRC,cfgUsers,bitsUsers,eqUserSym,failInterpretation] = heSIGBUserFieldDecode(rx,chanEst,noiseVar,cfgRx,varargin) +%heSIGBUserFieldDecode Decode HE-SIG-B user field +% +% [FAILCRC,CFGUSERS] = +% heSIGBUserFieldDecode(RX,CHANEST,NOISEVAR,CFGUSERS) decode the HE-SIG-B +% user field given the HE-SIG-B field samples, RX, channel estimate, +% CHANEST, noise variance, NOISEVAR, and recovery configuration object +% CFGRX. +% +% FAILCRC represents the result of the CRC for each user. It is true if +% the user fails the CRC. It is a logical row vector of size +% 1-by-NumUsers. +% +% Returned CFGUSERS is a cell array of size 1-by-NumUsers. CFGUSERS is +% the updated format configuration object after HE-SIG-B user field +% decoding, of type wlanHERecoveryConfig. The updated format +% configuration object CFGUSERS is only returned for the users who pass +% the CRC. +% +% RX are the HE-SIG-B field samples. +% +% CHANEST is a complex Nst-by-1-by-Nr array containing the estimated +% channel at data and pilot subcarriers, where Nst is the number of +% occupied subcarriers and Nr is the number of receive antennas. +% +% NOISEVAR is the noise variance estimate, specified as a nonnegative +% scalar. +% +% The input CFGRX is the format configuration object of type +% wlanHERecoveryConfig, which specifies the parameters for the HE-MU format. +% +% [...,FAILINTERPRETATION] = heSIGBUserFieldDecode(...,SUPPRESSERROR) +% controls the behavior of the function due to an unexpected value of the +% interpreted HE-SIG-B user field bits. SUPPRESSERROR is logical. When +% SUPPRESSERROR is true and the function cannot interpret the recovered +% HE-SIG-B user field bits due to an unexpected value, the function +% returns FAILINTERPRETATION as true and the returned object is unchanged +% for the user. When SUPPRESSERROR is false and the function cannot +% interpret the recovered HE-SIG-B user field bits due to an unexpected +% value, an exception is issued and the function does not return. The +% default is false. + +% Copyright 2018-2020 The MathWorks, Inc. + +suppressError = false; % Control the validation of the interpreted HE-SIG-B user field bits +failInterpretation = false; +if nargin>4 + suppressError = varargin{1}; +end +chanBW = cfgRx.ChannelBandwidth; + +% Demodulate HE-SIGB field +demodUserFieldData = wlanHEDemodulate(rx,'HE-SIG-B',chanBW); + +% Extract data and pilots symbols +preheInfo = wlanHEOFDMInfo('HE-SIG-A',chanBW); +demodUserData = demodUserFieldData(preheInfo.DataIndices,:,:); +demodUserPilot = demodUserFieldData(preheInfo.PilotIndices,:,:); + +% Estimate and correct common phase error +demodUserData = heCPECorrection(demodUserData,demodUserPilot,chanEst(preheInfo.PilotIndices,:,:),chanBW); + +% Merge channels +[userOne20MHz,chanEstOne20MHz] = heSIGBMergeSubchannels(demodUserData,chanEst(preheInfo.DataIndices,:,:),chanBW); + +% Perform equalization +[eqUserSym,csi] = preHESymbolEqualize(userOne20MHz,chanEstOne20MHz,noiseVar); + +% Return a cell array of objects each representing a user +if suppressError + [bitsUsers,failCRC] = wlanHESIGBUserBitRecover(eqUserSym,noiseVar,csi,cfgRx); + [cfgUsers,failInterpretation] = interpretHESIGBUserBits(cfgRx,bitsUsers,failCRC); +else + [bitsUsers,failCRC,cfgUsers] = wlanHESIGBUserBitRecover(eqUserSym,noiseVar,csi,cfgRx); +end + +end \ No newline at end of file diff --git a/heSURx.m b/heSURx.m new file mode 100644 index 0000000..7358d8a --- /dev/null +++ b/heSURx.m @@ -0,0 +1,125 @@ +function [rxPSDU,bitErrorRate,eqSymPlot,txPSDUByte] = heSURx(rx,cfgHE,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot,txPSDUByte) +chanBW = cfgHE.ChannelBandwidth; +ind = wlanFieldIndices(cfgHE); +fs = wlanSampleRate(cfgHE); +ofdmInfo = wlanHEOFDMInfo('HE-Data',cfgHE); + + % Packet detect and determine coarse packet offset +coarsePktOffset = wlanPacketDetect(rx,chanBW); +LSTF = wlanLSTF(wlanNonHTConfig); +if isempty(coarsePktOffset) + coarsePktOffset = 0; +end +% Extract L-STF and perform coarse frequency offset correction +lstf = rx(coarsePktOffset+(ind.LSTF(1):ind.LSTF(2)),:); +tmp_xcorr = abs(xcorr(LSTF,lstf)); +coarseFreqOff = wlanCoarseCFOEstimate(lstf,chanBW); +rx = helperFrequencyOffset(rx,fs,-coarseFreqOff); + +% Extract the non-HT fields and determine fine packet offset +nonhtfields = rx(coarsePktOffset+(ind.LSTF(1):ind.LSIG(2)),:); +finePktOffset = wlanSymbolTimingEstimate(nonhtfields,chanBW); + +% Determine final packet offset +pktOffset = coarsePktOffset+finePktOffset; +if max(tmp_xcorr)>50 +rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:); +lltfDemod = wlanLLTFDemodulate(rxLLTF,chanBW); +lltfChanEst = wlanLLTFChannelEstimate(lltfDemod,chanBW); +noiseVar = helperNoiseEstimate(lltfDemod); + +rxSIGA = rx(pktOffset+(ind.LSIG(1):ind.HESIGA(2)),:); +pktFormat = wlanFormatDetect(rxSIGA,lltfChanEst,noiseVar,chanBW); +fprintf(' %s packet detected\n\n',pktFormat); + +% If packet detected outwith the range of expected delays from + +% Extract L-LTF and perform fine frequency offset correction +rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:); +fineFreqOff = wlanFineCFOEstimate(rxLLTF,chanBW); +rx = helperFrequencyOffset(rx,fs,-fineFreqOff); + +% Display estimated carrier frequency offset +cfoCorrection = coarseFreqOff + fineFreqOff; % Total CFO +fprintf('Estimated CFO: %5.1f Hz\n\n',cfoCorrection); + +% HE-LTF demodulation and channel estimation +rxHELTF = rx(pktOffset+(ind.HELTF(1):ind.HELTF(2)),:); +heltfDemod = wlanHEDemodulate(rxHELTF,'HE-LTF',cfgHE); +[chanEst,pilotEst] = heLTFChannelEstimate(heltfDemod,cfgHE); + +% Data demodulate +rxData = rx(pktOffset+(ind.HEData(1):ind.HEData(2)),:); +demodSym = wlanHEDemodulate(rxData,'HE-Data',cfgHE); + +% Pilot phase tracking +demodSym = heCommonPhaseErrorTracking(demodSym,chanEst,cfgHE); + +% Estimate noise power in HE fields +nVarEst = heNoiseEstimate(demodSym(ofdmInfo.PilotIndices,:,:),pilotEst,cfgHE); + +% Extract data subcarriers from demodulated symbols and channel +% estimate +demodDataSym = demodSym(ofdmInfo.DataIndices,:,:); +chanEstData = chanEst(ofdmInfo.DataIndices,:,:); + +% Equalization and STBC combining +[eqDataSym,csi] = heEqualizeCombine(demodDataSym,chanEstData,nVarEst,cfgHE); +% eqDataOut = eqDataSym(1:end); + +% Recover data +rxPSDUbits = wlanHEDataBitRecover(eqDataSym,nVarEst,csi,cfgHE,'LDPCDecodingMethod','layered-bp'); +% [rxPSDU(:,frameIdx),~] = RX_CRC32(double(rxPSDUbits)); +rxPSDU(:,frameIdx) = rxPSDUbits; + +EVM = comm.EVM; +EVM.ReferenceSignalSource = 'Estimated from reference constellation'; +EVM.Normalization = 'Average constellation power'; +release(EVM); +EVM.ReferenceConstellation = wlanReferenceSymbols(cfgHE); +Evm = EVM(eqDataSym(:)); +fprintf(' HE-Data EVM:%2.2fdB\n\n',20*log10(Evm)); + +rx = rx(pktOffset+ind.HEData(2):end,:); +frameIdx = frameIdx+1; +[rxPSDU,bitErrorRate,eqSymPlot] = heSURx(rx,cfgHE,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot); +else + [~,numPkt] = size(rxPSDU); + psduLength = length(rxPSDU(:,1))/8; + switch cfgUI.DataType + case 'test' + txPSDU = DataGenerate(cfgUI,numPkt,SEED,psduLength,txPSDUByte); + txPSDU = txPSDU{1}; + bitError = sum(txPSDU(1:end - 32,:)~=rxPSDU); + bitErrorRate = bitError/length(rxPSDU); + ACK = (bitErrorRate == 0); + case 'text' + case 'photo' + [~,numPkt] = size(rxPSDU); + bitstream = reshape(rxPSDU,1,[]); % 鎭㈠姣旂壒鍙樻瘮鐗规祦 + imgLen = bin2dec(num2str(bitstream(1:16))); + bit_array = reshape(bitstream,8,[])'; + byte_array = num2str(bit_array); + img = bin2dec(byte_array); % 浜岃繘鍒惰浆鍗佽繘鍒 + if imgLen+3 > length(img) + imgLen = length(img)-3; + end + rxPSDU = int16(img(1:imgLen+3)); % double杞琲nt16 +% rxCRC = 0; +% for i = 1:imgLen+2 +% rxCRC = mod(rxCRC+rxPSDU(i),256); +% end + rxCRC = mod(sum(rxPSDU(1:length(rxPSDU)-1)),256); + if rxCRC == rxPSDU(end) + disp('Pass RX Sum Check!'); + bitErrorRate = zeros(1,numPkt); + else + disp('Fail RX Sum Check!'); + bitErrorRate = ones(1,numPkt); + end + ACK = (rxCRC == rxPSDU(end)); + end + + save(['TXPackets\ACKfeedback_for_User' num2str(userIdx) '.mat'],'ACK'); +end +end diff --git a/heSUspecial.m b/heSUspecial.m new file mode 100644 index 0000000..ce389d1 --- /dev/null +++ b/heSUspecial.m @@ -0,0 +1,227 @@ +function [rxPSDU,bitErrorRate,eqSymPlot] = heSUspecial(rx,cfgHE,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot) +% chanBW = cfgHE.ChannelBandwidth; +% ind = wlanFieldIndices(cfgHE); +% fs = wlanSampleRate(cfgHE); +% ofdmInfo = wlanHEOFDMInfo('HE-Data',cfgHE); + +searchOffset = 0; % Offset from start of waveform in samples +% rxWaveLen = length(rx); +cfg = wlanNonHTConfig('SignalChannelBandwidth',true, ... + 'BandwidthOperation','Static'); + +% Generate field indices +ind = wlanFieldIndices(cfg); +chanBW = cfg.ChannelBandwidth; +sr = wlanSampleRate(cfg); % Sample rate + +% while (searchOffset + minPktLen) <= rxWaveLen + +% Packet detection +pktOffset = wlanPacketDetect(rx,chanBW,searchOffset); +% Adjust packet offset +pktOffset = searchOffset + pktOffset; +% if isempty(pktOffset) || (pktOffset + ind.LSIG(2) > rxWaveLen) +% error('** No packet detected **'); +% end +LSTF = wlanLSTF(wlanNonHTConfig); +if isempty(pktOffset) + pktOffset = 0; +end +% Coarse frequency offset estimation and correction using L-STF +rxLSTF = rx(pktOffset+(ind.LSTF(1):ind.LSTF(2)), :); +tmp_xcorr = abs(xcorr(LSTF,rxLSTF)); +if max(tmp_xcorr)>50 +coarseFreqOffset = wlanCoarseCFOEstimate(rxLSTF,chanBW); +rx = helperFrequencyOffset(rx,sr,-coarseFreqOffset); +% Symbol timing synchronization +searchBufferLLTF = rx(pktOffset+(ind.LSTF(1):ind.LSIG(2)),:); +pktOffset = pktOffset+wlanSymbolTimingEstimate(searchBufferLLTF,chanBW); +% Fine frequency offset estimation and correction using L-STF +rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:); + +fineFreqOffset = wlanFineCFOEstimate(rxLLTF,chanBW); +rx = helperFrequencyOffset(rx,sr,-fineFreqOffset); + +% Scale the waveform based on L-STF power (AGC) +gain = 1./(sqrt(mean(rxLSTF.*conj(rxLSTF)))); +rx = rx.*gain; + +% Packet Format Detection +rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:); +lltfDemod = wlanLLTFDemodulate(rxLLTF,chanBW); +lltfChanEst = wlanLLTFChannelEstimate(lltfDemod,chanBW); +noiseVar = helperNoiseEstimate(lltfDemod); + +% Recover L-SIG field bits +[rxLSIGBits, failCheck, ~] = wlanLSIGRecover(rx(pktOffset + (ind.LSIG(1):ind.LSIG(2)), :), lltfChanEst, noiseVar, chanBW); +[MCS_No,PSDULength] = interpretLSIG(rxLSIGBits); +estNumerlogy = bi2de(rxLSIGBits(19:24).','right-msb'); +MCS_set=[2,2;4,2;4,4;16,2;16,4;64,3;64,4;64,6]; +MCS = MCS_set(MCS_No+1,:); +if failCheck % Skip L-STF length of samples and continue searching + disp('** L-SIG check fail **'); + else + disp('L-SIG check pass'); +end + +%% + load NumerologySet.mat NumerologySet + NumerologySel = NumerologySet(estNumerlogy,:); + [~,WaveformPara] = WaveformNumerologyCal(PSDULength,NumerologySel,MCS); + +% output data field +% rx_datafield = rx(pktOffset+(ind.NonHTData(1):ind.NonHTData(2)), :); +rx_datafield = rx(pktOffset+ind.NonHTData(1):pktOffset+ind.NonHTData(1)+WaveformPara.FrameLen-1, :); + + + + +%% 鐢熸垚淇¢亾鍝嶅簲 +% N_carriers = WaveformPara.N_FFT; +% rng(2); +% channel_response=sqrt(1/2)*(randn(1,N_carriers)+1i*randn(1,N_carriers)); +% N_windows = 8; +% N_level = 10; +% min_len_CP = 30; +% CPset = Generate_CP(channel_response,N_windows,N_level,min_len_CP); + + +% [rxDataModLs] = rx_PilotBlockChanEstForLabview(rx_datafield,WaveformPara,CPset); % 鍘籆P鐨勫湴鏂 + + [rxDataModLs] = rx_PilotBlockChanEst(rx_datafield,WaveformPara); +% scatterplot(rxDataModLs(1:end)); + dataDemod = qamdemod(rxDataModLs,WaveformPara.Mod,'OutputType','bit','UnitAveragePower',true); +% scatter(real(rxDataModLs),imag(rxDataModLs)) +% dataDemod = reshape(dataDemod,1,[]); + dataDePadded = dataDemod(1:end - WaveformPara.DataPadding); + tb = 8; + trellis = poly2trellis(7,[171 133]); +% decoded = vitdec(dataDePadded,trellis,tb,'trunc','unquant',WaveformPara.puncpat); + decoded = vitdec(dataDePadded,trellis,tb,'trunc','hard',WaveformPara.puncpat); + + [rxPSDU(:,frameIdx),PER] = RX_CRC32_deScramble(decoded); +% rng(SEED); +% dataPacketBits = randi([0 1],WaveformPara.Payload,1); +% txPSDU = DataGenerate(cfgUI,'test',numpkt,SEED,psduLength); +% [numErrors,BER] = biterr(dataPacketBits,rx); + % Recover data +rxPSDUbits = decoded; +eqSymPlot = rxDataModLs; +% [rxPSDU(:,frameIdx),~] = RX_CRC32(double(rxPSDUbits)); +%% + +% Recover data +EVM = comm.EVM; +EVM.ReferenceSignalSource = 'Estimated from reference constellation'; +EVM.Normalization = 'Average constellation power'; +release(EVM); +switch WaveformPara.Mod + case 16 + refsym = '16QAM'; + case 4 + refsym = 'QPSK'; + case 2 + refsym = 'BPSK'; + case 64 + refsym = '64QAM'; +end +EVM.ReferenceConstellation = wlanReferenceSymbols(refsym); +Evm = EVM(rxDataModLs(:)); +fprintf(' HE-Data EVM:%2.2fdB\n\n',20*log10(Evm)); + +rx = rx(pktOffset+ind.NonHTData(1)+WaveformPara.FrameLen:end,:); +frameIdx = frameIdx+1; +[rxPSDU,bitErrorRate,eqSymPlot] = heSUspecial(rx,cfgHE,userIdx,frameIdx,rxPSDU,cfgUI,SEED,bitErrorRate,eqSymPlot); +else + [~,numpkt] = size(rxPSDU); + psduLength = length(rxPSDU(:,1))/8; + txPSDU = DataGenerate(cfgUI,numpkt,SEED,psduLength,0); + txPSDU = txPSDU{1}; + bitError = sum(txPSDU(1:end,:)~=rxPSDU); + bitErrorRate = bitError/length(rxPSDU); + ACK = (bitErrorRate == 0); + save(['TXPackets\ACKfeedback_for_User' num2str(userIdx) '.mat'],'ACK'); +% scatterplot(rxDataModLs(1:end)); + eqSymPlot = eqSymPlot(1:end); +end +end + +%% +function [MCS,PSDULength] = interpretLSIG(recLSIGBits) +% InterpretLSIG Interprets recovered L-SIG bits +% +% [MCS,PSDULENGTH] = interpretLSIG(RECLSIGBITS) returns the +% modulation and coding scheme and PSDU length given the recovered L-SIG +% bits + +% Rate and length are determined from bits +rate = double(recLSIGBits(1:3)); +length = double(recLSIGBits(5+(1:12))); + +% MCS rate table, IEEE Std 802.11-2016, Table 17-6. +R = wlan.internal.nonHTRateSignalBits(); +mcstmp = find(all(bsxfun(@eq,R(1:3,:),rate)))-1; +MCS = mcstmp(1); % For codegen +PSDULength = bi2de(length.'); + +end + +%% + +function [rxDataModLs] = rx_PilotBlockChanEst(rAddAWGN,WaveformPara) +N = WaveformPara.N_FFT; +N_Used = WaveformPara.UsedSubcarrier; +L = WaveformPara.CP_Len; +m = WaveformPara.PilotNum; +pnSequence = comm.PNSequence('Polynomial',[8 6 5 4 0], ... + 'SamplesPerFrame',N_Used,'InitialConditions',[0 0 0 0 0 0 0 1]); +pilot = generatePilot(pnSequence,1); + +rxOFDMsym = reshape(rAddAWGN,WaveformPara.SymbolLen,WaveformPara.FrameSymNum); +rxOFDMcore = rxOFDMsym(L+1:L+N,:); +rxDataModInMatrix = fft(rxOFDMcore,[],1)/sqrt(N); + +rxDataModInMatrix_shift = [rxDataModInMatrix(2:2+(N_Used-1)/2-1,:);rxDataModInMatrix(end - (N_Used-1)/2:end,:)]; + +rxPilot = rxDataModInMatrix_shift(:,1:WaveformPara.PilotInterval+1:WaveformPara.FrameSymNum); + +hLs = zeros(N_Used,m); + +rxDataRemovPilot = zeros(N_Used,WaveformPara.DataFrame); + + +for i = 1:m-1 + rxDataRemovPilot(:,(i-1)*WaveformPara.PilotInterval+1:i*(WaveformPara.PilotInterval)) = ... + rxDataModInMatrix_shift(:,(i-1)*(WaveformPara.PilotInterval+1)+2:i*(WaveformPara.PilotInterval+1)); + hLs(:,i) = rxPilot(:,i)./pilot; +end + +rxDataRemovPilot(:,(m-1)*WaveformPara.PilotInterval+1:end) = ... + rxDataModInMatrix_shift(:,(m-1)*(WaveformPara.PilotInterval+1)+2:end); +hLs(:,m) = rxPilot(:,m)./pilot; + + + +if mod(WaveformPara.DataFrame,WaveformPara.PilotInterval)>0 + hLs = [kron(hLs(:,1:m-1),ones(1,WaveformPara.PilotInterval)),kron(hLs(:,m),ones(1,mod(WaveformPara.DataFrame,WaveformPara.PilotInterval)))]; +else + hLs = kron(hLs(:,1:m),ones(1,WaveformPara.PilotInterval)); +end + +eqDataModLs = zeros(N_Used,WaveformPara.DataFrame); + +for i = 1:WaveformPara.DataFrame + eqDataModLs(:,i) = rxDataRemovPilot(:,i)./hLs(:,i); +end + +rxDataModLs = reshape(eqDataModLs,[],1); + +end + +function pilot = generatePilot(pnSequence,Power) + + pilot = pnSequence(); + + bpskModulator = comm.BPSKModulator; + pilot = bpskModulator(pilot)*sqrt(Power); +end diff --git a/heSigRecGenerateWaveform.m b/heSigRecGenerateWaveform.m new file mode 100644 index 0000000..26deebb --- /dev/null +++ b/heSigRecGenerateWaveform.m @@ -0,0 +1,161 @@ +function rxWaveform = heSigRecGenerateWaveform(cfgMU,numRx,delayProfile,noisePower,cfo) +%heSigRecGenerateWaveform Generate an impaired waveform for the featured example + +% Copyright 2018-2019 The MathWorks, Inc. + +% Specify waveform parameters +numTxPkt = 1; % Number of transmitted packets +idleTime = 20e-6; % Idle time before and after each packet + +% Setup TGax Indoor channel +chanBW = cfgMU.ChannelBandwidth; +numTx = cfgMU.NumTransmitAntennas; +sr = wlanSampleRate(cfgMU); + +% Create channel configuration common for all users +tgaxChannel = wlanTGaxChannel; +tgaxChannel.DelayProfile = delayProfile; % Delay profile +tgaxChannel.NumTransmitAntennas = numTx; % Number of transmit antennas +tgaxChannel.NumReceiveAntennas = numRx; % Each user has two receive antennas +tgaxChannel.TransmitReceiveDistance = 10; % Non-line of sight distance +tgaxChannel.ChannelBandwidth = chanBW; +tgaxChannel.SampleRate = sr; +% Set a fixed seed for the channel +tgaxChannel.RandomStream = 'mt19937ar with seed'; +tgaxChannel.Seed = 1; + +rngState = rng(120); % Set random state + +% Get allocation info +allocInfo = ruInfo(cfgMU); +muPSDULengths = getPSDULength(cfgMU); +% Create a MAC frame +[macFrames,cfgMU] = createMACFrames(cfgMU,allocInfo); +macFrames = addMUPadding(macFrames,muPSDULengths); +txPSDUPerUser = macFrames; + +% Generate HE-MU waveform with idle period +txWaveform = wlanWaveformGenerator(txPSDUPerUser,cfgMU, ... + 'NumPackets',numTxPkt,'IdleTime',idleTime); + +% Add channel +chanOut = tgaxChannel([zeros(round(idleTime*sr),numTx); txWaveform]); + +% Add carrier frequency offset +chanOut = helperFrequencyOffset(chanOut,sr,cfo); + +% Add noise +awgnChannel = comm.AWGNChannel( ... + 'NoiseMethod', 'Variance', ... + 'Variance', 10^(noisePower/10)); + +rxWaveform = awgnChannel(chanOut); +rng(rngState); % Restore random state + + +function [macFrames,cfgHE] = createMACFrames(cfgHE,allocInfo) +%createMACFrames Creates HE-MU MAC frames + +% Limitation: +% ----------- +% * wlanMACFrame does not support wlanHEMUConfig object. The MAC +% frame format is same for HE-SU and HE-MU packet, we are using +% wlanHESUConfig object to create a separate MAC frame for each user in +% an HE-MU configuration. +% +% * Channel coding option "BCC" is not supported for bandwidth > 20MHz +% in HE-SU format. If the bandwidth is > 20 MHz, we are forcing the channel +% coding value to "LDPC". This does not affect the generated MAC frame +% unless "MinimumMPDUStartSpacing" parameter is set to a value > 0. + +if isa(cfgHE,'wlanHEMUConfig') + numUsers = allocInfo.NumUsers; + numRUs = allocInfo.NumRUs; + apepLength = zeros(numUsers,1); + cfgSU = cell(numUsers,1); + for ru = 1:numRUs + for u = 1:numUsers + apepLength(u) = cfgHE.User{u}.APEPLength; + if strcmp(cfgHE.ChannelBandwidth,'CBW20') + chCoding = cfgHE.User{u}.ChannelCoding; + else + chCoding = 'LDPC'; + end + cfgSU{u} = wlanHESUConfig(... + 'APEPLength', apepLength(u), ... + 'ChannelBandwidth', cfgHE.ChannelBandwidth, ... + 'SpatialMapping', cfgHE.RU{ru}.SpatialMapping, ... + 'NumSpaceTimeStreams', allocInfo.NumSpaceTimeStreamsPerRU(ru), ... + 'MCS', cfgHE.User{u}.MCS, ... + 'DCM', cfgHE.User{u}.DCM, ... + 'ChannelCoding', chCoding, ... + 'NumTransmitAntennas', cfgHE.NumTransmitAntennas, ... + 'STBC', cfgHE.STBC, ... + 'GuardInterval', cfgHE.GuardInterval, ... + 'HELTFType', cfgHE.HELTFType, ... + 'SpatialReuse', cfgHE.SpatialReuse); + end + end +else % HE-SU, HE-EXT-SU + numUsers = 1; + apepLength = cfgHE.APEPLength; + cfgSU = {cfgHE}; +end + +% Create a MAC frame configuration +cfgMAC = wlanMACFrameConfig('FrameType','QoS Data','FrameFormat',cfgSU{1}.packetFormat); + +macFrames = cell(numUsers, 1); +for i = 1:numUsers + % Create payload (MSDUs) for the MAC frame + msduLen = wlanMSDULengths(apepLength(i),cfgMAC,cfgSU{i}); + msdu = cell(1, numel(msduLen)); + for j = 1:numel(msduLen) + msdu{j} = randi([0,255],msduLen(j),1); + end + + % Create the MAC frame bits + [macFrames{i},length] = wlanMACFrame(msdu,cfgMAC,cfgSU{i},'OutputFormat','bits'); + + % Update the APEP length. APEP length is always rounded off to a + % multiple of 4-octets. + if isa(cfgHE, 'wlanHEMUConfig') + cfgHE.User{i}.APEPLength = length; + else + cfgHE.APEPLength = length; + end +end + +end + +end + +function macFrames = addMUPadding(macFrames, muPSDULengths) +%addMUPadding Adds or removes the padding difference between an HE-SU and HE-MU PSDU + + numFrames = numel(macFrames); + eofDelimiter = [1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 1 0 0 1 1 1 0 0 1 0]'; + delimiterLength = 4; + + for i = 1:numFrames + assert(rem(numel(macFrames{i}),8) == 0,'Number of bits in the MAC frame is not a multiple of 8') + psdu = macFrames{i}; + psdu = psdu(1 : end-rem(numel(macFrames{i})/8,4)*8); + psduLength = numel(psdu)/8; + % Difference between the required padding for SU and MU formats + muPadding = muPSDULengths(i) - psduLength; + + % Calculate the number of EOF delimiters and zero padding needed + % for the MU padding + numEOFDelimiters = floor(abs(muPadding)/4); + wordPadding = rem(abs(muPadding),4); + + if muPadding < 0 + % Delete the extra padding created with wlanHESUConfig object + macFrames{i} = psdu(1 : end - (numEOFDelimiters*delimiterLength*8 + wordPadding*8)); + else + % Add MU padding to the PSDU + macFrames{i} = [psdu; repmat(eofDelimiter, numEOFDelimiters,1); zeros(wordPadding*8,1)]; + end + end +end \ No newline at end of file diff --git a/heSigRecSetupPlots.m b/heSigRecSetupPlots.m new file mode 100644 index 0000000..b88e3f4 --- /dev/null +++ b/heSigRecSetupPlots.m @@ -0,0 +1,66 @@ +function [SpectrumAnalyzer,TimeScope,ConstellationDiagram,EVMPerSubcarrier,EVMPerSymbol] = heSigRecSetupPlots(sr) +%heSigRecSetupPlots Create measurement plots for the featured example + +% Copyright 2018-2019 The MathWorks, Inc. + +% Scale and position plots on the screen +[repositionPlots,position] = helperPlotPositions(); + +% Create spectrum analyzer to plot spectrum of packet +SpectrumAnalyzer = dsp.SpectrumAnalyzer; +SpectrumAnalyzer.Name = 'Detected packet signal spectrum'; +SpectrumAnalyzer.SampleRate = sr; +SpectrumAnalyzer.ShowLegend = true; +SpectrumAnalyzer.RBWSource = 'Property'; +SpectrumAnalyzer.RBW = 100e3; +SpectrumAnalyzer.SpectralAverages = 3; +if repositionPlots + SpectrumAnalyzer.Position = position(1,:); +end + +% Create time scope to plot detected packet +TimeScope = timescope; +TimeScope.Name = 'Detected packet'; +TimeScope.YLabel = 'Magnitude'; +TimeScope.PlotType = 'Line'; +TimeScope.ShowGrid = true; +TimeScope.ShowLegend = true; +TimeScope.SampleRate = sr; +TimeScope.TimeSpanSource = 'property'; +if repositionPlots + TimeScope.Position = position(2,:); +end + +% Create a constellation diagram to plot equalized symbols +ConstellationDiagram = comm.ConstellationDiagram; +ConstellationDiagram.ShowReferenceConstellation = true; +ConstellationDiagram.ShowGrid = true; +ConstellationDiagram.Name = 'Equalized data symbols'; +if repositionPlots + ConstellationDiagram.Position = position(5,:); +end +ConstellationDiagram.ShowLegend = 1; + +% Display EVM plot per subcarrier +EVMPerSubcarrier = dsp.ArrayPlot; +EVMPerSubcarrier.PlotType = 'Line'; +EVMPerSubcarrier.ShowLegend = true; +EVMPerSubcarrier.XLabel = 'Subcarrier index'; +EVMPerSubcarrier.YLabel = 'EVM (dB)'; +EVMPerSubcarrier.Name = 'EVM per subcarrier'; +if repositionPlots + EVMPerSubcarrier.Position = position(3,:); +end + +% Display EVM plot per symbol +EVMPerSymbol = dsp.ArrayPlot; +EVMPerSymbol.PlotType = 'Line'; +EVMPerSymbol.ShowLegend = true; +EVMPerSymbol.XLabel = 'Symbol index'; +EVMPerSymbol.YLabel = 'EVM (dB)'; +EVMPerSymbol.Name = 'EVM per symbol'; +if repositionPlots + EVMPerSymbol.Position = position(4,:); +end + +end \ No newline at end of file diff --git a/heTrackingOFDMDemodulate.m b/heTrackingOFDMDemodulate.m new file mode 100644 index 0000000..8f754ee --- /dev/null +++ b/heTrackingOFDMDemodulate.m @@ -0,0 +1,329 @@ +function [ofdmDemod,cpe,peg,pilotGain] = heTrackingOFDMDemodulate(rxData,chanEst,numOFDMSym,cfgHE,cfgTrack,varargin) +%heTrackingOFDMDemodulate OFDM demodulation with pilot tracking +% +% Note: This is an internal undocumented function and its API and/or +% functionality may change in subsequent releases. +% +% [DATASYM,CPE,PEG,PILOTGAIN] = trackingOFDMDemodulate(RXDATA,CHANEST, +% NUMOFDMSYM, CFGSU,CFGTRACK) performs OFDM demodulation of data +% symbols with optional pilot tracking. +% +% DATASYM is a complex Nsd-by-Nsym-by-Nr array containing the demodulated +% symbols at data carrying subcarriers. Nsd represents the number of data +% subcarriers, Nsym represents the number of OFDM symbols in the Data +% field, and Nr represents the number of receiver antennas. +% +% CPE is a column vector of length Nsym containing the common phase error +% between each received and expected OFDM symbol. +% +% PEG is a column vector of length Nsym containing the phase error +% gradient per OFDM symbol in degrees per subcarrier. This error is +% caused by a sample rate offset between transmitter and receiver. +% +% PILOTGAIN is an Nsym-by-Nsp array containing the gain of pilots. Nsp is +% the number of pilot subcarriers. +% +% RXDATA is the received time-domain Data field signal, specified as an +% Ns-by-Nr matrix of real or complex values. Ns represents the number of +% time-domain samples in the Data field and Nr represents the number of +% receive antennas. Ns can be greater than the Data field length; in this +% case additional samples at the end of RXDATA, if not required, are not +% used. When sample rate offset tracking is enabled using the optional +% CFTRACK argument, additional samples may be required in RXDATA. This is +% to allow for the receiver running at a higher sample rate than the +% transmitter and therefore more samples being required. +% +% CHANEST is a complex Nst-by-Nsts-by-Nr array containing the channel +% gains at active subcarriers, or a Nsp-by-Nsts-by-Nr array containing +% the channel gains at pilot subcarriers. Nsts is the number of +% space-time streams. +% +% NUMOFDMSYM is the number of OFDM symbols expected. +% +% CFGSU is a format configuration object of type wlanHESUConfig or +% wlanHERecoveryConfig. +% +% CFGTRACK is a pilot tracking configuration structure. +% +% [...] = trackingOFDMDemodulate(RXDATA,CHANEST,NUMOFDMSYM,CFGMU, +% CFGTRACK,RUNUMBER) performs OFDM demodulation of data symbols with +% optional pilot tracking for a multi-user configuration. As a +% multi-user configuration is provided and resource unit number is +% required. +% +% CFGMU is the format configuration object of type wlanHEMUConfig. +% +% RUNUMBER is the RU (resource unit) number. + +% Copyright 2018-2020 The MathWorks, Inc. + +cpe = nan(numOFDMSym,1); % Initialize in case not calculated +peg = nan(numOFDMSym,1); % Initialize in case not calculated +pilotGain = nan(numOFDMSym,1); +if strcmp(cfgTrack.PilotTracking,'Joint') + % Perform joint measurement and optionally correction + + % Reduce the size of the averaging window if it exceeds the number of + % OFDM symbols. If it is even then use the largest odd window we can. + if cfgTrack.PilotTrackingWindow>numOFDMSym + cfgTrack.PilotTrackingWindow = numOFDMSym-(rem(numOFDMSym,2)==0); + end + [ofdmDemod,peg,cpe,pilotGain] = demodulateWithPhaseTracking(rxData,chanEst,numOFDMSym,cfgHE,cfgTrack,varargin{:}); +else + % OFDM demodulate only when no joint tracking + if isa(cfgHE,'wlanHERecoveryConfig') + ofdmDemod = wlanHEDemodulate(rxData,'HE-Data',cfgHE.ChannelBandwidth,cfgHE.GuardInterval, ... + [cfgHE.RUSize cfgHE.RUIndex],'OFDMSymbolOffset',cfgTrack.OFDMSymbolOffset); + else + ofdmDemod = wlanHEDemodulate(rxData,'HE-Data',cfgHE,varargin{:},'OFDMSymbolOffset',cfgTrack.OFDMSymbolOffset); + end + ofdmDemod = ofdmDemod(:,1:numOFDMSym,:); % Truncate output as extra samples may be passed + + if strcmp(cfgTrack.PilotTracking,'CPE') + % Pilot phase tracking + [ofdmDemod,cpe] = heCommonPhaseErrorTracking(ofdmDemod,chanEst,cfgHE,varargin{:}); + cpe = cpe.'; % Permute to return + end +end + +end + +function [ofdmDemod,peg,cpe,pilotGain] = demodulateWithPhaseTracking(rxData,chanEst,numOFDMSym,cfg,cfgTrack,varargin) + +if isa(cfg,'wlanHERecoveryConfig') + pktFormat = cfg.PacketFormat; + if strcmp(pktFormat,'HE-MU') + numSpaceTimeStreamsPerRU = cfg.RUTotalSpaceTimeStreams; + s = getSIGBLength(cfg); + numHESIGB = s.NumSIGBSymbols; + + else % SU or EXT_SU + numSpaceTimeStreamsPerRU = cfg.NumSpaceTimeStreams; + numHESIGB = 0; + end + ruSize = cfg.RUSize; + + ofdmInfo = wlanHEOFDMInfo('HE-Data',cfg.ChannelBandwidth,cfg.GuardInterval,[cfg.RUSize cfg.RUIndex]); +else + pktFormat = packetFormat(cfg); + allocInfo = ruInfo(cfg); + if isa(cfg,'wlanHEMUConfig') + ruNumber = varargin{1}; + ofdmInfo = wlanHEOFDMInfo('HE-Data',cfg,ruNumber); + sigbInfo = wlan.internal.heSIGBCodingInfo(cfg); + numHESIGB = sigbInfo.NumSymbols; + numSpaceTimeStreamsPerRU = allocInfo.NumSpaceTimeStreamsPerRU(ruNumber); + ruSize = allocInfo.RUSizes(ruNumber); + else + % SU or EXT_SU + ofdmInfo = wlanHEOFDMInfo('HE-Data',cfg); + numHESIGB = 0; + numSpaceTimeStreamsPerRU = allocInfo.NumSpaceTimeStreamsPerRU; + ruSize = allocInfo.RUSizes; + end +end + +if strcmp(pktFormat,'HE-EXT-SU') + numHESIGA = 4; +else % SU or MU + numHESIGA = 2; +end + +% OFDM demodulate configuration +prmStr = struct; +prmStr.NumReceiveAntennas = size(rxData,2); +prmStr.FFTLength = ofdmInfo.FFTLength; +prmStr.NumSymbols = 1; +prmStr.SymbolOffset = cfgTrack.OFDMSymbolOffset*ofdmInfo.CPLength(1); +prmStr.CyclicPrefixLength = ofdmInfo.CPLength(1); + +N = ofdmInfo.FFTLength; % FFT length is samples +Ng = ofdmInfo.CPLength; % Number of samples in GI +Ns = (N+Ng); % Number of samples per symbols +kst = ofdmInfo.ActiveFrequencyIndices; % Indices of all active subcarriers +kd = kst(ofdmInfo.DataIndices); % Indices of data carrying subcarriers +kp = kst(ofdmInfo.PilotIndices); % Indices of pilot carrying subcarriers +Nd = numel(kd); % Number of data carrying subcarriers +Np = numel(kp); % Number of pilot carrying subcarriers +Nr = size(chanEst,3); % Number of receive antennas + +n = (0:numOFDMSym-1); +z = 2+numHESIGA+numHESIGB; % Pilot symbol offset + +if numel(ofdmInfo.PilotIndices)==size(chanEst,1) + % Assume channel estimate is only for pilots + chanEstPilots = chanEst; +else + % Otherwise extract pilots from channel estimate + chanEstPilots = chanEst(ofdmInfo.PilotIndices,:,:); +end + +nsts = min(numSpaceTimeStreamsPerRU,size(chanEstPilots,2)); % Allow for single-stream or MIMO pilots +refPilots = wlan.internal.hePilots(ruSize,nsts,n,z); + +% Reshape for computation +chanEstPilotsR = permute(chanEstPilots,[3 2 1]); +refPilotsR = permute(refPilots,[3 1 2]); % Generate reference pilots + +% Calculate expected pilot values +pilotExp = complex(zeros(Np,numOFDMSym)); +for n = 1:numOFDMSym + for p=1:Np + pilotExp(p,n) = sum(reshape(chanEstPilotsR(:,:,p)*refPilotsR(:,p,n),[],1)); + end +end +ofdmDemodPilots = complex(zeros(Np,numOFDMSym,Nr)); +ofdmDemod = complex(zeros(Nd+Np,numOFDMSym,Nr)); +perr = complex(zeros(Np,numOFDMSym)); % Pilot error +delta = zeros(numOFDMSym,1); +omega = zeros(numOFDMSym,1); +skipDupStore = zeros(numOFDMSym,1); +skipdup = 0; +nD = 0; % Number of OFDM symbols demodulated +for n = 1:numOFDMSym + % Get index of samples to demodulate in current symbol + skipDupStore(n) = skipdup; + idx = (n-1)*Ns+(1:Ns)+skipdup; + if any(idx>size(rxData,1)) + % Break from loop if we run out of data + coder.internal.warning('wlan:trackingOFDMDemodulate:NotEnoughSamples',numOFDMSym,n-1); + break; + end + % OFDM demodulation + demod = commonDemod(rxData(idx,:),ofdmInfo,prmStr); + ofdmDemodPilots(:,n,:) = demod(ofdmInfo.PilotIndices,1,1:Nr); % for codegen + ofdmDemod(:,n,:) = demod(:,1,1:Nr); % for codegen + + % Calculate pilot error + ofdmDemodPilotsR = permute(ofdmDemodPilots(:,n,:),[2 3 1]); + for p = 1:Np + perr(p,n) = reshape(conj(ofdmDemodPilotsR(1,:,p))*chanEstPilotsR(:,:,p)*refPilotsR(:,p,n),1,1); + end + + % Average pilots over time window + perridx = max((n-cfgTrack.PilotTrackingWindow+1),1):n; + % Find indices which span across a skip/dup + spanSkipDup = perridx(skipDupStore(perridx)~=skipDupStore(perridx(end))); + if any(spanSkipDup) + % Remove phase shift offset caused by skip/dup and average + skipdupVal = (skipDupStore(perridx)-skipDupStore(perridx(end))).'; + perrav = sum(perr(:,perridx).*exp(1i*2*pi*bsxfun(@times,skipdupVal,kp)/N),2); + else + perrav = sum(perr(:,perridx),2); + end + if n>1 + % Subtract the previous common phase from the current to avoid + % needing to wrap (use phasor to avoid angles wrapping across + % pilots before subtraction) + perrav = perrav*exp(-1i*omega(n-1)); + end + + % Least square estimation with covariance estimate per symbol + j = lscov([kp ones(size(kp))],angle(perrav),abs(perrav)); + delta(n) = j(1); % Time offset + + % If subtracting previous common phase then add it back on to + % common phase error + if n>1 + omega(n) = j(2)+omega(n-1); + else + omega(n) = j(2); + end + + % Skip or duplicate a sample in the next OFDM symbol if + % required + if delta(n)>=(2*pi/N)*0.9 + skipdup = skipdup+1; % Skip + elseif delta(n)<=-(2*pi/N)*0.9 + skipdup = skipdup-1; % Duplicate + end + nD = n; % Record number of demodulated symbols +end + +pilotGain = movmean(abs(perr).',cfgTrack.PilotTrackingWindow/2); + +% The averaging causes a delay which we correct for before applying +% correction +delay = (cfgTrack.PilotTrackingWindow-1)/2; + +% When a skip-dup occurred we changed the phase to allow averaging over +% the skip/dup. Now correct for any phase change applied +skipindTmp = bsxfun(@plus,(find((diff(skipDupStore))==1)+1),(0:delay-1)); +skipind = skipindTmp(:); % for codegen +dupindTmp = bsxfun(@plus,(find((diff(skipDupStore))==-1)+1),(0:delay-1)); +dupind = dupindTmp(:); % for codegen +skipCorrIdx = skipind(skipind<=numOFDMSym); +delta(skipCorrIdx) = delta(skipCorrIdx)+2*pi/N; +dupCorrIdx = dupind(dupind<=numOFDMSym); +delta(dupCorrIdx) = delta(dupCorrIdx)-2*pi/N; + +% Use shrinking window at end of waveform to average pilots and account +% for delay +keepIdx = setdiff(1:numOFDMSym,2:2:cfgTrack.PilotTrackingWindow); % Remove even averages at start when growing window +deltaTmp = [delta(keepIdx); zeros(delay,1)]; +omegaTmp = [omega(keepIdx); zeros(delay,1)]; +extDelta = zeros(delay,1); +for i = 1:delay + % Remove difference of phases due to skip/dup over averaging window + skipdupVal = (skipDupStore(nD-(cfgTrack.PilotTrackingWindow-2*i)+1:nD)-skipDupStore(nD)).'; + perrav = sum(perr(:,nD-(cfgTrack.PilotTrackingWindow-2*i)+1:nD).*exp(1i*2*pi.*bsxfun(@times,skipdupVal,kp)/N),2); + % Remove previous CPE before LS estimation + perrav = perrav*exp(-1i*omegaTmp(nD-delay+i-1)); + % Reapply phase offset removed for averaging due to skip/dup + angleperrav = angle(perrav)-skipdupVal(delay-i+1)*2*pi.*kp/N; + % Least-square estimation + jt = lscov([kp ones(size(kp))],angleperrav,abs(perrav)); + extDelta(i) = jt(1); + omegaTmp(nD-delay+i) = jt(2)+omegaTmp(nD-delay+i-1); % Add previous CPE +end +delta(1:nD) = [deltaTmp(1:nD-delay); extDelta]; +omega = omegaTmp; + +% Apply correction if requested +if strcmp(cfgTrack.PilotTracking,'Joint') + % Correction for timing + corrst = exp(1i*bsxfun(@times,delta.',kst)); + % Correction for phase + corrst = bsxfun(@times,corrst,exp(1i*omega.')); + % Apply per symbol correction of subcarriers + ofdmDemod(:,1:nD,:) = bsxfun(@times,ofdmDemod(:,1:nD,:),corrst(:,1:nD)); +end +% Return estimate of impairments +cpe = -omega; +peg = -delta; + +% Perform pilot gain tracking if requested +if cfgTrack.PilotGainTracking == true + normGain = pilotGain./pilotGain(1,:); + weightedAverages = pilotGain(1,:)./sum(pilotGain(1,:)); + gainTracking = sum(normGain.*weightedAverages,2); + ofdmDemod = ofdmDemod./gainTracking.'; +end + +end + +function x = lscov(A,b,V) +% Weights given, scale rows of design matrix and response. +D = sqrt(V(:)); +A(:,1) = A(:,1).*D; +A(:,2) = A(:,2).*D; +b = b.*D; + +% Factor the design matrix, incorporate covariances or weights into the +% system of equations, and transform the response vector. +[Q,R] = qr(A,0); +z = Q'*b; + +% Compute the LS coefficients +x = real(R\z); +end + +function demod = commonDemod(rx,cfgOFDM,prmStr) +fftout = comm.internal.ofdm.demodulate(rx,prmStr); + +% Extract active subcarriers from full FFT +demod = fftout(cfgOFDM.ActiveFFTIndices,:,:); + +% Scale by number of active tones and FFT length +demod = demod*sqrt(cfgOFDM.NumTones)/cfgOFDM.FFTLength; +end \ No newline at end of file diff --git a/heUserBeamformingFeedback.m b/heUserBeamformingFeedback.m new file mode 100644 index 0000000..df01571 --- /dev/null +++ b/heUserBeamformingFeedback.m @@ -0,0 +1,104 @@ +function steeringMat = heUserBeamformingFeedback(rx,cfgNDP,varargin) +%heUserBeamformingFeedback HE user beamforming feedback +% +% STEERINGMAT = heUserBeamformingFeedback(RX,CFGNDP) returns the steering +% matrix recommended to beamform towards the user of interest. The +% steering matrix is calculated using SVD. +% +% STEERINGMAT = heUserBeamformingFeedback(RX,CFGNDP,CFOCOMP) performs +% carrier frequency offset estimation and compensation on the received +% waveform before calculating the steering matrix if CFOCOMP is set to +% TRUE. +% +% STEERINGMAT is a Nst-by-Nr-by-Nsts array containing the recommended +% full band beamforming steering matrix. Nst is the number of occupied +% subcarriers, Nr is the number of receive antennas, and Nsts is the +% number of space-time-streams. +% +% RX is the received NDP at the station. +% +% CFGNDP is the format configuration object of type wlanHESUConfig. + +% Copyright 2017-2018 The MathWorks, Inc. + +narginchk(2,3); + +if nargin == 3 + cfoCompensate = varargin{1}; + validateattributes(cfoCompensate,{'logical'},{'nonnan','finite'},mfilename,'',3); +else + cfoCompensate = false; +end + +chanBW = string(cfgNDP.ChannelBandwidth); +ind = wlanFieldIndices(cfgNDP); +fs = wlanSampleRate(cfgNDP); + +% Packet detect and determine coarse packet offset +coarsePktOffset = wlanPacketDetect(rx,chanBW); +if isempty(coarsePktOffset) % If empty no L-STF detected; try 0 + % Synchronization failed, return empty steering matrix + steeringMat = []; + return; +end + +if cfoCompensate + % Extract L-STF and perform coarse frequency offset correction + lstf = rx(coarsePktOffset+(ind.LSTF(1):ind.LSTF(2)),:); + coarseFreqOff = wlanCoarseCFOEstimate(lstf,chanBW); + rx = helperFrequencyOffset(rx,fs,-coarseFreqOff); +end + +% Extract the non-HT fields and determine fine packet offset +nonhtfields = rx(coarsePktOffset+(ind.LSTF(1):ind.LSIG(2)),:); +finePktOffset = wlanSymbolTimingEstimate(nonhtfields,chanBW); + +% Determine final packet offset +pktOffset = coarsePktOffset+finePktOffset; + +% If packet detected outwith the range of expected delays from +% the channel modeling; packet error +if pktOffset>50 + % Synchronization failed, return empty steering matrix + steeringMat = []; + return; +end + +if cfoCompensate + % Extract L-LTF and perform fine frequency offset correction + rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:); + fineFreqOff = wlanFineCFOEstimate(rxLLTF,chanBW); + rx = helperFrequencyOffset(rx,fs,-fineFreqOff); +end + +% Demodulate HE-LTF with info for all RUs +rxHELTF = rx(pktOffset+(ind.HELTF(1):ind.HELTF(2)),:); +demodHELTF = wlanHEDemodulate(rxHELTF,'HE-LTF',cfgNDP); + +% Extract the demodulated HE-LTF RU +demodHELTFRU = demodHELTF; + +% Channel estimate with info from current RU +chanEstUser = heLTFChannelEstimate(demodHELTFRU,cfgNDP); + +% Get cyclic shift and inverse +numSTS = cfgNDP.NumSpaceTimeStreams; +csh = -wlan.internal.getCyclicShiftVal('VHT',numSTS,wlan.internal.cbwStr2Num(chanBW)); +Nfft = (fs/20e6)*256; +% Indices of active subcarriers in the NDP +ndpOFDMInfo = wlanHEOFDMInfo('HE-Data',cfgNDP); +k = ndpOFDMInfo.ActiveFrequencyIndices; +chanEstMinusCSD = permute(wlan.internal.cyclicShift(permute(chanEstUser,[1 3 2]),csh,Nfft,k),[1 3 2]); +chanEstPerm = permute(chanEstMinusCSD,[3 2 1]); % Nr-by-Nsts-by-Nst + +% Compute the feedback matrix using singular value decomposition +% for the streams allocated to the user +Nst = size(chanEstPerm,3); +numRx = size(demodHELTF,3); +V = complex(zeros(Nst,numSTS,numRx)); % Nst-by-Nsts-by-Nr +for ist = 1:Nst + [~,~,V(ist,:,:)] = svd(chanEstPerm(:,:,ist),'econ'); +end +steeringMat = permute(V,[1 3 2]); % Nst-by-Nr-by-Nsts + +end diff --git a/helperFrequencyOffset.m b/helperFrequencyOffset.m new file mode 100644 index 0000000..cd1db86 --- /dev/null +++ b/helperFrequencyOffset.m @@ -0,0 +1,27 @@ +function out = helperFrequencyOffset(in, fs, foffset) +%helperFrequencyOffset Apply a frequency offset to the input signal +% +% OUT = helperFrequencyOffset(IN, FS, FOFFSET) applies the specified +% frequency offset to the input signal. +% +% OUT is the frequency-offset output of the same size as IN. +% IN is the complex 2D array input. +% FS is the sampling rate in Hz (e.g. 80e6). +% FOFFSET is the frequency offset to apply to the input in Hz. +% +% See also comm.PhaseFrequencyOffset. + +% Copyright 2015-2019 The MathWorks, Inc. + +%#codegen + +% Initialize output +out = complex(zeros(size(in))); + +% Create vector of time samples +t = ((0:size(in,1)-1)/fs).'; + +% For each antenna, apply the frequency offset +for i = 1:size(in,2) + out(:,i) = in(:,i).*exp(1i*2*pi*foffset*t); +end \ No newline at end of file diff --git a/helperNoiseEstimate.m b/helperNoiseEstimate.m new file mode 100644 index 0000000..4705973 --- /dev/null +++ b/helperNoiseEstimate.m @@ -0,0 +1,171 @@ +function est = helperNoiseEstimate(rxSym,varargin) +%helperNoiseEstimate Estimate noise power using L-LTF (non-HT, HT, and VHT) +%and LTF1 in S1G +% +% EST = helperNoiseEstimate(RXSYM) estimates the mean noise power in +% watts using the demodulated L-LTF symbols (non-HT, HT, and VHT) or the +% demodulated S1G-LTF1 symbols in S1G, assuming 1ohm resistance. The +% estimated noise power in non-HT packet is averaged over the number of +% of receive antennas. +% +% RXSYM is the frequency-domain signal corresponding to the L-LTF or +% S1G-LTF1. It is a complex matrix or 3-D array of size Nst-by-2-by-Nr, +% where Nst represents the number of used subcarriers in the L-LTF or +% S1G-LTF1, and Nr represents the number of receive antennas. Two OFDM +% symbols in the L-LTF or S1G-LTF1 fields are used to estimate the noise +% power. Noise estimate from the S1G-LTF1 field for S1G 1MHz format is +% not supported. +% +% EST = helperNoiseEstimate(RXSYM,CHANBW,NUMSTS) returns the estimated +% noise power in beamformed fields using the specified channel bandwidth, +% CHANBW, and total number of space-time streams, NUMSTS. The number of +% subcarriers used within each field and the scaling applied during +% demodulation differs between the non-HT and HT/VHT fields for VHT, HT +% and non-HT. Therefore the estimated noise power after demodulation in +% HT/VHT fields is calculated by scaling the estimated noise power in the +% L-LTF. The number of space-time streams is required for scaling if the +% demodulated RXSYM is not scaled according to the number of space-time +% streams. +% +% EST = helperNoiseEstimate(...,'Per Antenna') specifies the option of +% estimating the noise for each receive antenna. When this option is +% specified EST is a row vector of length Nr. +% +% Example: +% % Estimate the noise variance of an HT packet. +% +% cfgHT = wlanHTConfig; % Create packet configuration +% chanBW = cfgHT.ChannelBandwidth; +% numSTS = cfgHT.NumSpaceTimeStreams; +% noisePower = -20; +% awgnChannel = comm.AWGNChannel; +% awgnChannel.NoiseMethod = 'Variance'; +% awgnChannel.Variance = 10^(noisePower/10); +% +% Nst = 56; % Data and pilot OFDM subcarriers in 20MHz, HT format +% Nfft = 64; % FFT size for 20MHz bandwidth +% nVarHT = 10^(noisePower/10)*(Nst/Nfft); % non-HT noise variance +% +% NumRxAnts = 1; +% % Average noise estimate over 100 independent noise realization +% for n=1:100 +% % Generate LLTF and add noise +% rxSym = awgnChannel(wlanLLTF(cfgHT)); +% y = wlanLLTFDemodulate(rxSym,cfgHT); +% noiseEst(n) = helperNoiseEstimate(y,chanBW,numSTS); +% end +% +% % Check noise variance estimates without Channel +% noiseEstError = 10*log10(mean(noiseEst))-10*log10(nVarHT); +% disp(['Error between noise variance and mean estimated noise ', ... +% 'power(dB): ' num2str(noiseEstError,'%2.2f ')]); +% +% See also wlanLLTF, wlanLLTFDemodulate. + +% Copyright 2015-2017 The MathWorks, Inc. + +%#codegen + +narginchk(1,4); + +% Validate symbol type +validateattributes(rxSym,{'double'},{'3d','finite'},mfilename,'OFDM symbol(s)'); + +% Two L-LTF symbols (non-HT, HT, and VHT) or two S1G-LTF1 symbols (S1G) are +% required to estimate the noise +coder.internal.errorIf(size(rxSym,2)~=2,'wlan:helperNoiseEstimate:IncorrectNumSyms'); + +numSC = size(rxSym,1); + +% Minimal optional parameter checks +if nargin == 2 + % (rxSym,'Per Antenna') + validateNType(varargin{1}); + average = false; + + scalingFactor = 1; % Noise scaling factor +elseif nargin == 3 + % (rxSym, chanBW, numSTSTotal) + chanBW = varargin{1}; % chanBW: CBW2/4/8/16/20/40/80/160 + coder.internal.errorIf(strcmp(chanBW,'CBW1'),'wlan:helperNoiseEstimate:InvalidS1G1M'); + validateInput(chanBW,numSC); + + numSTSTotal = varargin{2}; % numSTSTotal: 1,...,8 + average = true; + + % Noise scaling factor + scalingFactor = noiseScaling(chanBW,numSC,numSTSTotal); +elseif nargin == 4 + % (rxSym, chanBW, numSTSTotal, 'Per Antenna') + chanBW = varargin{1}; % chanBW: CBW2/4/8/16/20/40/80/160 + coder.internal.errorIf(strcmp(chanBW,'CBW1'),'wlan:helperNoiseEstimate:InvalidS1G1M'); + validateInput(chanBW,numSC); + + numSTSTotal = varargin{2}; % numSTSTotal: 1,...,8 + + validateNType(varargin{3}); + average = false; + + % Noise scaling factor + scalingFactor = noiseScaling(chanBW,numSC,numSTSTotal); +else + % (rxSym) + average = true; + scalingFactor = 1; +end + +% Noise estimate +noiseEst = sum(abs(rxSym(:,1,:)-rxSym(:,2,:)).^2,1)/(2*numSC); +if average + noise = mean(noiseEst); +else + noise = squeeze(noiseEst).'; +end + +% Scale +est = noise*scalingFactor; + +end + +%------------------------------------------------------------------------- +function out = noiseScaling(chanBW,numSC,numSTSTotal) + +if any(strcmp(chanBW,{'CBW2','CBW4','CBW8','CBW16'})) + % In S1G, Data and LTF1 fields have the same number of occupied + % subcarriers. Only apply scaling by the number of space-time streams. + Nst = numSC; +else + % Get the number of occupied subcarriers in HT and VHT fields. + % The number of used subcarriers for HT and VHT are same therefore + % fix the character vector input of the following helper function to + % VHT. The guard type is not relevant for numbers alone. + [~,vhtData,vhtPilots] = wlan.internal.wlanGetOFDMConfig(chanBW,'Long','VHT'); + Nst = numel(vhtData)+numel(vhtPilots); +end +out = (Nst/numSC)*numSTSTotal; + +end + +%------------------------------------------------------------------------- +function validateInput(chanBW,numSC) + +if any(strcmp(chanBW,{'CBW2','CBW4','CBW8','CBW16'})) % S1G + [~,s1gData,s1gPilots] = wlan.internal.s1gOFDMConfig(chanBW,'Long','LTF1'); + Nst = numel(s1gData)+numel(s1gPilots); +else % nonHT, HT, and VHT + % Get number of used subcarriers in NonHT format + [~,nonhtData,nonhtPilots] = wlan.internal.wlanGetOFDMConfig(chanBW,'Long','Legacy'); + Nst = numel(nonhtData)+numel(nonhtPilots); +end + +% Validate number of subcarriers in input +coder.internal.errorIf(numSC~=Nst,'wlan:helperNoiseEstimate:IncorrectNumSC',Nst,numSC); + +end + +%------------------------------------------------------------------------- +function validateNType(nType) + +coder.internal.errorIf(~(strcmpi(nType,'Per Antenna')),'wlan:helperNoiseEstimate:InvalidNoiseEstType'); + +end diff --git a/helperPlotPositions.m b/helperPlotPositions.m new file mode 100644 index 0000000..4ec6f3f --- /dev/null +++ b/helperPlotPositions.m @@ -0,0 +1,45 @@ +%helperPlotPositions returns plot sizes and positions based on screen resolution +% [REPOSITIONPLOTS,POSITION] = helperPlotPositions() determines whether +% plot tiling can be supported based upon the user's display resolution. +% If plot tiling can be supported, REPOSITIONPLOTS is returned as true +% and POSITION contains an array of vectors of the form [left bottom +% width height] indicating the X-Y position and size for each plot. The +% plots are arranged in a 3x2 tiled arrangement. If plot tiling cannot be +% supported, REPOSITIONPLOTS is returned as false and POSITION is not +% valid. +% +% The plots are arranged in the following manner: +% 3x2 Tile +% Plot 5 Plot 1 Plot 2 +% Plot 6 Plot 3 Plot 4 + +% Copyright 2017 The MathWorks, Inc. + +function [repositionPlots,position] = helperPlotPositions() + + su=get(0,'Units'); + set(0,'Units','pixels'); + res = get(0,'ScreenSize'); + set(0,'Units',su); + + if ismac + minres = 1440; + else % ispc,isunix + minres = 1280; + end + + if (res(3)>minres) + xpos = fix(res(3)*[1/2; 3/4; 1/2; 3/4; 1/4; 1/4]); + ypos = fix(res(4)*[1/2; 1/2; 1/16; 1/16; 1/2; 1/16]); + xsize = (xpos(2) - xpos(1) - 20)*[1; 1; 1; 1; 1; 1]; + ysize = fix(xsize(1) * 5 / 6)*[1; 1; 1; 1; 1; 1]; + position = [xpos ypos xsize ysize]; + + repositionPlots = true; + else + position = zeros(6,4); + + repositionPlots = false; + end + +end diff --git a/helperSymbolEqualize.m b/helperSymbolEqualize.m new file mode 100644 index 0000000..f1a5fb7 --- /dev/null +++ b/helperSymbolEqualize.m @@ -0,0 +1,48 @@ +function [y, csi] = helperSymbolEqualize(x,chanEst,varargin) +%helperSymbolEqualize MIMO frequency domain channel equalization +% +% [Y,CSI] = helperSymbolEqualize(X,CHANEST,NOISEVAR) performs +% minimum-mean-square-error (MMSE) frequency domain equalization using +% the signal input X, the channel estimate, CHANEST, and noise variance, +% NOISEVAR. +% +% Y is an estimate of the transmitted frequency domain signal and is of +% size Nsd-by-Nsym-by-Nsts, where Nsd represents the number of carriers +% (frequency domain), Nsym represents the number of symbols (time +% domain), and Nsts represents the number of space-time streams (spatial +% domain). It is complex when either X or CHANEST is complex, or is real +% otherwise. +% +% CSI is a real matrix of size Nsd-by-Nsts containing the soft channel +% state information. +% +% X is a real or complex array containing the frequency domain signal to +% equalize. It is of size Nsd-by-Nsym-by-Nr, where Nr represents the +% number of receive antennas. +% +% CHANEST is a real or complex array containing the channel estimates for +% each carrier and symbol. It is of size Nsd-by-Nsts-by-Nr. +% +% NOISEVAR is a nonnegative scalar representing the noise variance. +% +% [Y,CSI] = helperSymbolEqualize(X,CHANEST) performs zero-forcing (ZF) +% equalization. + +% Copyright 2017 The MathWorks, Inc. + +%#codegen + +% Input validation +narginchk(2,3); + +if nargin==2 + eqMethod = 'ZF'; + noiseVarEst = 0; +else + eqMethod = 'MMSE'; + noiseVarEst = varargin{1}; +end + +[y,csi] = wlan.internal.wlanEqualize(x,chanEst,eqMethod,noiseVarEst); + +end diff --git a/image.png b/image.png new file mode 100644 index 0000000..ceb9ded Binary files /dev/null and b/image.png differ diff --git a/int2bit.m b/int2bit.m new file mode 100644 index 0000000..08c0f59 --- /dev/null +++ b/int2bit.m @@ -0,0 +1,186 @@ +function y = int2bit(x, N, MSBFirst) + %INT2BIT Convert integers to bits + % + % Y = INT2BIT(X,N) converts each integer element in X to N + % column-wise bits in Y in 2's complement form, with the first or the + % top-most bit being the MSB (most significant bit). X can be a + % scalar, vector, matrix or an array with 3 dimensions. Y has same + % dimensions as X, except that the number of rows in Y are N times + % the number of rows in X. The datatype of X can be any of the + % built-in numeric types. When the datatype of X is - + % - a floating-point type, Y is of the same datatype. + % - a built-in unsigned integer type, datatype of Y is uint8. + % - a built-in signed integer type, datatype of Y is int8. + % - double, N must be no larger than 53. + % - single, N must be no larger than 24. + % When N is less than the minimum number of bits required to + % represent values in X i.e. N < ceil(log2(max(X)+1)), the least + % significant N bits are returned in Y. + % + % Y = INT2BIT(X,N,MSBFIRST) specifies MSB orientation in MSBFIRST. + % When MSBFIRST is true, the first bit in each set of N column-wise + % bits in Y is the MSB; when MSBFIRST is false, the first bit in each + % set of N column-wise bits in Y is the LSB (least significant bit). + % + % Examples: + % + % A1 = [12 5]; + % N1 = 4; + % B1 = int2bit(A1, N1); + % + % A2 = int8([32 6 104; 120 66 9]); + % N2 = 8; + % msbFirst = false; + % B2 = int2bit(A2, N2, msbFirst); + % + % A3 = randi([0,255], 4, 3, 2, 'uint16'); + % N3 = 10; + % B3 = int2bit(A3, N3); + % + % A4 = [153, -103, 103, -128]; + % B4 = int2bit(A4, 8); % 153 and -103 represent same bits + % B5 = int2bit(A4, 9); % Use additional bit to have unique bits + % + % See also BIT2INT. + + % Copyright 2021-2022 The MathWorks, Inc. + + %#codegen + + narginchk(2,3); + if nargin == 2 + MSBFirst = true; + else + validateattributes(MSBFirst, {'logical','numeric'}, {'scalar','binary'}, '', 'MSBFirst'); + end + + if isfloat(x) + % Output datatype is same as input's + validateattributes(x, {'numeric'}, {'3d','nonempty','integer'}, '', 'X'); + if isa(x, 'double') + maxN = 53; + else + maxN = 24; + end + validateattributes(N, {'numeric'}, {'scalar','positive','integer','<=',maxN}, '', 'N'); + useN = N; + yType = x(1); + isInputFloat = true; + else + % Output datatype is uint8 or int8 + validateattributes(x, {'numeric'}, {'3d','nonempty'}, '', 'X'); + validateattributes(N, {'numeric'}, {'scalar','positive','integer'}, '', 'N'); + intClass = class(x); + integerClasses = {'int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32', 'int64', 'uint64'}; + integerNumBits = [8, 8, 16, 16, 32, 32, 64, 64]; + maxN = integerNumBits(strcmp(intClass, integerClasses)); + useN = min(N, maxN); + if intClass(1) == 'u' + yType = zeros(1, 'uint8'); + else + yType = zeros(1, 'int8'); + end + isInputFloat = false; + end + + ySize = size(x); + ySize(1) = ySize(1) * N; + + if coder.target('MATLAB') + if isInputFloat + % Matrix operations are faster, compared to element-wise + % operations, in MATLAB simulation. + + if MSBFirst + powOf2 = pow2(cast(-N+1:0, 'like', x)); + else + powOf2 = pow2(cast(0:-1:(-N+1), 'like', x)); + end + y = reshape(mod(floor(x(:) * powOf2), 2)', ySize); + else + % Use bitget() for built-in integers in sim to avoid + % floating-point operations. + if useN < N + % When input integer value is negative and useN3 + numSubchannels = varargin{1}; +else + numSubchannels = 1; +end + +% Treat each 20 MHz subchannel as a receive antennas for diversity +[symMerge,chanMerge] = wlan.internal.mergeSubchannels(x,chanEst,numSubchannels); + +% Equalize +[y,csi] = helperSymbolEqualize(symMerge,chanMerge,noiseVar); + +end \ No newline at end of file diff --git a/rxChannelSounding.m b/rxChannelSounding.m new file mode 100644 index 0000000..1680065 --- /dev/null +++ b/rxChannelSounding.m @@ -0,0 +1,95 @@ +function [PDP, rmsDelay, psd_max] = rxChannelSounding(rx,cfg,numSeq,K) +chanBW = cfg.ChannelBandwidth; +T_sample = 1/str2double(chanBW(4:end))/1e6; % Duration of each sample/pulse +% K = 255; % Length of a pseudonoise sequence +T_seq = K*T_sample; % Duration of a pseudonoise sequence + +soundingSeqMod_1 = zadoffChuSeq(29,255); +soundingSeqMod_2 = zadoffChuSeq(47,255); +%% 璁$畻PDP +% Concatenate multiple sequences to sound the channel periodically. +% Preprocessing of the Received Data +MISO_channel_index = 1 ;%/ 2 +if MISO_channel_index == 1 + soundingSeqMod = soundingSeqMod_1; +else + soundingSeqMod = soundingSeqMod_2; +end + +% Create a matched filter of the modulated sounding sequence. +h_mf = conj(flip(soundingSeqMod)); + +% Pass the received signal through the matched filter +output_mf = conv(h_mf,rx)/K; +output_mf = output_mf(K:end); + +% Store the real part and imaginary part of the received data respectively. +data_I = real(output_mf); % I route for recorded data +data_Q = imag(output_mf); % Q route for recorded data +% figure; +% Power Delay Profiles (PDPs) +PDP = mean(reshape(data_I.^2+data_Q.^2,K,numSeq),2); +x = (0:K-1)*T_sample*1e6; +% stem(x,PDP); +% axis([0 T_seq*1e5 0 1.2*max(PDP)]); +% xlabel('delay (us)'); +% ylabel('power (W)'); +PDP2d = reshape(data_I.^2+data_Q.^2,K,numSeq); +% pause(0.5); +% close; +% [x,y] = meshgrid(1:numSeq,(0:K-1)*T_sample*1e6); +% mesh(x,y,PDP) +% axis([1 numSeq 0 T_seq*1e5 0 1.5]); +% xlabel('Index of PDP') +% ylabel('delay (us)') +% zlabel('power (W)') +% title('Power Delay Profiles (PDP)'); + +%% 璁$畻鏃跺欢鎵╁睍 +% Compute the temporal parameters. +delay_average_vector = zeros(numSeq,1); +delay_spread_vector = zeros(numSeq,1); + +for i = 1:numSeq + [delay_average_vector(i), delay_spread_vector(i)] = findTemporalParameters(PDP2d(:,i), T_sample, K); +end +range_delay = 0:T_sample:T_seq; +% pdf_delay_average = findPDF(range_delay,delay_average_vector); +pdf_delay_spread = findPDF(range_delay,delay_spread_vector); +rmsDelay = range_delay(pdf_delay_spread==max(pdf_delay_spread))*1e6; +% figure; +% subplot(1,2,1) +% plot(range_delay(1:length(range_delay)-1)*1e6,pdf_delay_average,'LineWidth',2); +% axis([0 1.25*max(delay_average_vector)*1e6 0 1.25*max(pdf_delay_average)]) +% xlabel('Delay (us)') +% ylabel('Average Delay') +% grid on +% subplot(1,2,2) +% plot(range_delay(1:length(range_delay)-1)*1e6,pdf_delay_spread,'LineWidth',2); +% xlabel('Delay (us)') +% ylabel('Delay Spread') +% axis([0 1.25*max(delay_spread_vector)*1e6 0 1.25*max(pdf_delay_spread)]) +% grid on + +%% 璁$畻澶氭櫘鍕掗绉 +% Truncate the measurement data into individual impulse response for further analysis +dataBlk = reshape(data_I+sqrt(-1)*data_Q,K,numSeq); + +% Compute the Doppler power spectral density through FFT of the ACF. +% What if the delay of particular path varies with time? +psd = zeros(K,2*numSeq-1); % Doppler PSD +for i = 1:K + temp = xcorr(dataBlk(i,:)); + psd(i,:) = fftshift(abs(fft(temp)/(2*numSeq-1))); +end +f = ((0:2*numSeq-2)/(2*numSeq-1)-0.5)/T_seq; +psd_max = f(max(psd)==max(psd,[],'all')); +% delay = [0 0.2 0.4 0.6]*1e-6; +% figure; +% [x,y] = meshgrid(((0:2*numSeq-2)/(2*numSeq-1)-0.5)/T_seq,(0:K-1)*T_sample*1e6); +% mesh(x,y,psd) +% axis([-1000 1000 0 1.5*max(delay)*1e6 0 1.5*max(max(psd))]) +% xlabel('Doppler Shift (Hz)') +% ylabel('Delay (us)') +% zlabel('Doppler Power Spectral Density') +end diff --git a/rxFrontEnd.m b/rxFrontEnd.m new file mode 100644 index 0000000..3371cfb --- /dev/null +++ b/rxFrontEnd.m @@ -0,0 +1,30 @@ +function [rx,pktOffset,gain] = rxFrontEnd(rx,pktOffset,ind,cfgRx) + + sr = wlanSampleRate(cfgRx); % Sample rate + + % Coarse frequency offset estimation and correction using L-STF + rxLSTF = rx(pktOffset+(ind.LSTF(1):ind.LSTF(2)), :); + coarseFreqOffset = wlanCoarseCFOEstimate(rxLSTF,cfgRx.ChannelBandwidth); + rx = helperFrequencyOffset(rx,sr,-coarseFreqOffset); + + % Symbol timing synchronization + searchBufferLLTF = rx(pktOffset+(ind.LSTF(1):ind.LSIG(2)),:); + pktOffset = pktOffset+wlanSymbolTimingEstimate(searchBufferLLTF,cfgRx.ChannelBandwidth); + + % Fine frequency offset estimation and correction using L-STF + rxLLTF = rx(pktOffset+(ind.LLTF(1):ind.LLTF(2)),:); + fineFreqOffset = wlanFineCFOEstimate(rxLLTF,cfgRx.ChannelBandwidth); + rx = helperFrequencyOffset(rx,sr,-fineFreqOffset); + + % Timing synchronization complete: packet detected + fprintf('Packet detected at index %d\n',pktOffset + 1); + + % Display estimated carrier frequency offset + cfoCorrection = coarseFreqOffset + fineFreqOffset; % Total CFO + fprintf('Estimated CFO: %5.1f Hz\n\n',cfoCorrection); + + % Scale the waveform based on L-STF power (AGC) + gain = 1./(sqrt(mean(rxLSTF.*conj(rxLSTF)))); + rx = rx.*gain; +end + diff --git a/showResourceMatrix.m b/showResourceMatrix.m new file mode 100644 index 0000000..0b81e67 --- /dev/null +++ b/showResourceMatrix.m @@ -0,0 +1,160 @@ +function showResourceMatrix(cfgHE,cfgUI,RU_index,varargin) +% **杈撳叆锛氬惈鏈塕U鍒嗛厤淇℃伅鐨凥E缁撴瀯浣撱佹潵鑷猆I鐨勯澶栨帶鍒跺弬鏁 +% **杈撳嚭锛氬惈鏈'鏃堕棿*棰戠巼*鐢ㄦ埛'涓夌淮鐨勮祫婧愬垎閰嶇ず鎰忓浘 + +% 鑾峰彇RU鍒嗛厤淇℃伅 +if cfgUI.numUsers == 1 + cfgHE = wlanHEMUConfig(RU_index); +end + +allocInfo = ruInfo(cfgHE); + +% 鐢诲竷鍒濆鍖 +if nargin == 3 +% fig = figure('Name',getString(message('wlan:hePlotAllocation:FigureTitle'))); + fig = figure('visible','off'); + ax = axes(fig); +else + ax = varargin{1}; +end +cla(ax,'reset') + +% 缁樺浘鍙傛暟 +dpi = 96; +cbw = wlan.internal.cbwStr2Num(cfgHE.ChannelBandwidth); +Nfft = cbw/20*256; +isHETBNDP = isa(cfgHE,'wlanHETBConfig') && cfgHE.FeedbackNDP; +centerColor = [1 0 0; 0.5 0 0; 0.25 0 0]; % Colors for special split RUs + +% infoLims contains tuples of channel bandwidth and the minimum RU size for +% which RU information is to be displayed on a patch: [cbw rusize]. +% tuple涓褰曠粰瀹氬甫瀹戒笅鍙垎閰嶇殑鏈灏廟U澶у皬 +infoLims = ... + [20 26; ... + 40 52; ... + 80 106; ... + 160 242 ]; + +% 鏋勫缓sequencial allocation matrix +time_sequence_len = length(cfgUI.user1.t); +frequency_sequence_len = length(cfgUI.user1.f); +allo_sequencial=zeros(time_sequence_len,frequency_sequence_len,cfgUI.numUsers); +for t = 1 : time_sequence_len + for f = 1 : frequency_sequence_len + for n = 1 : cfgUI.numUsers + if n == 1 + if cfgUI.user1.t(t) == 1 && cfgUI.user1.f(f) == 1 + allo_sequencial(t,f,n) = 1; + end + elseif n == 2 + if cfgUI.user2.t(t) == 1 && cfgUI.user2.f(f) == 1 + allo_sequencial(t,f,n) = 1; + end + end + end + end +end + +% Plot the HE occupied subcarriers +kRUPuncture = wlan.internal.hePuncturedRUSubcarrierIndices(cfgHE); +bi = 1; +icenter = 1; +startEndIdx = zeros(0,2); +hRU = gobjects(allocInfo.NumRUs,2); % At most 2 patches per RU + +% 鍏辨湁5涓椂闅欙紝姣忎釜鏃堕殭閮藉皢鐩稿悓鐨凴U璧勬簮鍧楀垎閰嶇粰鍥哄畾鐨勫嚑涓敤鎴 +for seq = 1:size(allo_sequencial,1) + i_accu = 0; + for i = 1:allocInfo.NumRUs + % Get the active subcarrier indices for the RU + % 鍙敤瀛愯浇娉㈠簭鍙 + if isHETBNDP + kFull = wlan.internal.heTBNDPSubcarrierIndices(cbw,cfgHE.RUToneSetIndex,cfgHE.FeedbackStatus); + else + kFull = wlan.internal.heRUSubcarrierIndices(cbw,allocInfo.RUSizes(i),allocInfo.RUIndices(i)); + end + if ~isempty(kRUPuncture) + k = setdiff(kFull,kRUPuncture); % Discard punctured subcarriers + else + k = kFull; + end + + + + % 閰嶇疆patch鐨勯鑹插拰璇存槑鏂囧瓧 + UserIndex = 'User #'; + colorToUse = 0; + firstUser = 0; + for n = 1 : cfgUI.numUsers + if allo_sequencial(seq,i + i_accu,n) == 1 + if colorToUse == 0 + firstUser = n; % 鏂逛究鍚庤竟鏋勫缓瀛楃锛歎ser #a-b + UserIndex = [UserIndex,num2str(n)]; + end + colorToUse = colorToUse + n^2;% 浣跨敤骞虫柟閬垮厤棰滆壊閲嶅 + end + end + UserInRU = sum(allo_sequencial(seq,i + i_accu,:));% 褰撳墠RU涓敤鎴锋绘暟 + % RU涓敤鎴锋暟瓒呰繃1鏃讹紝闇瑕侀澶栨瀯閫犱笉鍚屽舰寮忕殑瀛楃涓 + if UserInRU > 1 + UserIndex = [UserIndex,'-',num2str(firstUser + UserInRU - 1)]; + end + + + + + % Get the end index of each continuous block of subcarriers within the RU + % 鍥犱负鍒╃敤patch鍑芥暟鐢诲浘锛岄渶瑕佷互瀛愯浇娉㈡槸鍚﹁繛缁垝鍒唒atch + nonContigEnd = [find(diff(k)~=1); numel(k)]; + startIdx = 1; % The start index of the continuous block of subcarriers + for j = 1:numel(nonContigEnd) + % Plot a patch of the continuous block of subcarriers + blkIdx = startIdx:nonContigEnd(j); + startEndIdx(bi,:) = [k(blkIdx(1)) k(blkIdx(end))]; + % y = [k(blkIdx(1)) k(blkIdx(end)) k(blkIdx(end)) k(blkIdx(1))]; + % x = [0 0 1.5 1.5 ]; + y = [k(blkIdx(1)) k(blkIdx(end)) k(blkIdx(end)) k(blkIdx(1))]; + x = [0.05+1.6*(seq-1) 0.05+1.6*(seq-1) 1.55+1.6*(seq-1) 1.55+1.6*(seq-1)]; + + + hold(ax,'on'); + startIdx = nonContigEnd(j)+1; + bi = bi+1; + + if colorToUse == 0 + hRU(i,j) = patch(ax,x,y,'w');% 鏃犵敤鎴蜂娇鐢ㄨRU鏃跺~鍏呬负鐧借壊 + UserIndex = ''; + else + hRU(i,j) = patch(ax,x,y,colorToUse);% 鏈夌敤鎴蜂娇鐢ㄨRU鏃跺嵆浣跨敤colorToUse瀵瑰簲鐨勯鑹 + end + + % 鏍囪鐢ㄦ埛搴忓彿 + xMiddle = hRU(i,j).Vertices(1,1)+(hRU(i,j).Vertices(end,1)-hRU(i,j).Vertices(1,1))/2; + yMiddle = hRU(i,j).Vertices(1,2)+(hRU(i,j).Vertices(2,2)-hRU(i,j).Vertices(1,2))/2; + htext = text(ax,xMiddle,yMiddle,UserIndex,'HorizontalAlignment','center','FontSize',9); + htext.PickableParts = 'none'; + end + + switch length(k) + case 242 + i_offset = 3; + case 106 + i_offset = 1; + case 52 + i_offset = 0; + end + i_accu = i_accu + i_offset; + + end +end +% ylabel(ax,getString(message('wlan:hePlotAllocation:SubcarrierIndex'))) +ylabel('Subcarrier Index'); +xticks(ax,[]) +ylim(ax,[-Nfft/2 Nfft/2-1]); +ax.Box = 'on'; +h = plot(ax,NaN,NaN,'ow'); % Placeholder for empty legend +print(fig, 'RU_Alloc.png', '-dpng', ['-r', num2str(dpi)]); +% pause(0.5); +% close; + +end \ No newline at end of file diff --git a/sprectrum.m b/sprectrum.m new file mode 100644 index 0000000..f3e5858 --- /dev/null +++ b/sprectrum.m @@ -0,0 +1,11 @@ +function [pxx,freq] = sprectrum(x,mode,N) + switch mode + case 1 + [pxx,freq] = periodogram(x *sqrt(2*pi),[],N,'centered'); + case 2 + [pxx,freq] = periodogram(x *sqrt(2*pi),hamming(length(x)),N,'centered'); + case 3 + [pxx,freq] = pwelch(x *sqrt(2*pi),[],[],N,'centered'); + end + pxx = mean(pxx,2); +end diff --git a/test.m b/test.m new file mode 100644 index 0000000..f5e3d70 --- /dev/null +++ b/test.m @@ -0,0 +1,7 @@ +a = [1 1]; +try +a == []; +diary('TXPackets\log.txt'); +catch ErrorInfo +disp(ErrorInfo.message); +end \ No newline at end of file diff --git a/trackingRecoveryConfig.m b/trackingRecoveryConfig.m new file mode 100644 index 0000000..63c9840 --- /dev/null +++ b/trackingRecoveryConfig.m @@ -0,0 +1,188 @@ +classdef trackingRecoveryConfig < comm.internal.ConfigBase +%trackingRecoveryConfig Construct a configuration object for data recovery +% CFGREC = trackingRecoveryConfig constructs a configuration object for +% recovering the data fields using function which perform sample rate +% offset tracking: trackingNonHTDataRecover, trackingHTDataRecover, and +% trackingVHTDataRecover. Adjust the property values of the object, +% which indicate different algorithm parameters or operations at the +% receiver, to achieve optimal recovery performance. +% +% CFGREC = trackingRecoveryConfig(Name,Value) constructs a recovery +% configuration object, CFGREC, with the specified property Name set to +% the specified Value. You can specify additional name-value pair +% arguments in any order as (Name1,Value1,...,NameN,ValueN). +% +% trackingRecoveryConfig properties: +% +% OFDMSymbolOffset - OFDM symbol sampling offset +% EqualizationMethod - Equalization method +% PilotTracking - Pilot tracking +% PilotTrackingWindow - Pilot tracking averaging window +% PilotGainTracking - Pilot gain tracking +% LDPCDecodingMethod - LDPC decoding algorithm +% MinSumScalingFactor - Scaling factor for normalized min-sum LDPC +% decoding algorithm +% MinSumOffset - Offset for offset min-sum LDPC decoding +% algorithm +% MaximumLDPCIterationCount - Maximum number of decoding iterations +% EarlyTermination - Enable early termination of LDPC decoding +% +% % Example: +% % Create a trackingRecoveryConfig object for performing ZF +% % equalization, OFDM symbol sampling offset of 0.5, and not pilot +% % tracking in a recovery process. +% +% cfgRec = trackingRecoveryConfig( ... +% 'OFDMSymbolOffset', 0.5, ... +% 'EqualizationMethod', 'ZF', ... +% 'PilotTracking', 'None') +% +% See also trackingVHTDataRecover, trackingHTDataRecover, +% trackingNonHTDataRecover. + +% Copyright 2016-2022 The MathWorks, Inc. + +%#codegen +%#ok<*EMCA> + +properties (Access = 'public') + %OFDMSymbolOffset OFDM symbol offset + % Specify the sampling offset as a fraction of the cyclic prefix (CP) + % length for every OFDM symbol, as a double precision, real scalar + % between 0 and 1, inclusive. The OFDM demodulation is performed + % based on Nfft samples following the offset position, where Nfft + % denotes the FFT length. The default value of this property is 0.75, + % which means the offset is three quarters of the CP length. + OFDMSymbolOffset = 0.75; + %EqualizationMethod Equalization method + % Specify the equalization method as one of 'MMSE' | 'ZF'. The + % default value of this property is 'MMSE'. + EqualizationMethod = 'MMSE'; + %PilotTracking Pilot tracking + % Specify the pilot phase tracking performed as one of 'Joint', + % 'CPE', or 'None'. 'Joint' pilot tracking estimates and corrects for + % a sample rate offset and residual carrier frequency offset across + % all receive antennas for each received OFDM symbol before + % equalization. 'CPE' pilot tracking estimates and corrects for a + % common phase error caused by residual carrier frequency offset. The + % default is 'Joint'. + PilotTracking = 'Joint'; + %PilotTrackingWindow Pilot tracking averaging window + % Specify the pilot phase tracking averaging window in OFDM symbols, + % as an odd, integer scalar greater than 0. When set to 1, no + % averaging is applied. The default is 9. Within the tracking + % algorithm the window is truncated to the number of OFDM symbols to + % demodulate if required. + PilotTrackingWindow = 9; + %PilotGainTracking Pilot gain tracking + % Enable or disable pilot gain tracking. The default is false. + PilotGainTracking (1,1) logical = false; + %LDPCDecodingMethod LDPC decoding algorithm + % Specify the LDPC decoding algorithm as one of these values: + % - 'bp' : Belief propagation (BP) + % - 'layered-bp' : Layered BP + % - 'norm-min-sum' : Normalized min-sum + % - 'offset-min-sum': Offset min-sum + % The default is 'norm-min-sum'. + LDPCDecodingMethod = 'norm-min-sum'; + %MinSumScalingFactor Scaling factor for normalized min-sum LDPC decoding algorithm + % Specify the scaling factor for normalized min-sum LDPC decoding + % algorithm as a scalar in the interval (0,1]. This argument applies + % only when you set LDPCDecodingMethod to 'norm-min-sum'. The default + % is 0.75. + MinSumScalingFactor = 0.75; + %MinSumOffset Offset for offset min-sum LDPC decoding algorithm + % Specify the offset for offset min-sum LDPC decoding algorithm as a + % finite real scalar greater than or equal to 0. This argument + % applies only when you set LDPCDecodingMethod to 'offset-min-sum'. + % The default is 0.5. + MinSumOffset = 0.5; + %MaximumLDPCIterationCount Maximum number of decoding iterations + % Specify the maximum number of iterations in LDPC decoding as an + % integer valued numeric scalar. This applies when you set the + % channel coding property to LDPC. The default is 12. + MaximumLDPCIterationCount = 12; + %EarlyTermination Enable early termination of LDPC decoding + % Set this property to true to enable early termination of LDPC + % decoding if all parity-checks are satisfied. If set to false, the + % decoding process will iterate for a fixed number of iterations + % specified by MaximumLDPCIterationCount. This property applies when + % ChannelCoding is set to LDPC for a user. The default is false. + EarlyTermination (1,1) logical = false; +end + +properties(Constant, Hidden) + EqualizationMethod_Values = {'MMSE', 'ZF'}; + PilotTracking_Values = {'Joint', 'CPE', 'None'}; + LDPCDecodingMethod_Values = {'bp','layered-bp','norm-min-sum','offset-min-sum'}; +end + +methods + function obj = trackingRecoveryConfig(varargin) + obj = obj@comm.internal.ConfigBase('EqualizationMethod', 'MMSE', ... + 'PilotTracking','Joint',varargin{:}); + end + + function obj = set.OFDMSymbolOffset(obj, val) + prop = 'OFDMSymbolOffset'; + validateattributes(val, {'double'}, ... + {'real','scalar','>=',0,'<=',1}, ... + [class(obj) '.' prop], prop); + obj.(prop) = val; + end + + function obj = set.EqualizationMethod(obj, val) + prop = 'EqualizationMethod'; + validateEnumProperties(obj, prop, val); + obj.(prop) = ''; + obj.(prop) = val; + end + + function obj = set.PilotTracking(obj,val) + prop = 'PilotTracking'; + validateEnumProperties(obj, prop, val); + obj.(prop) = ''; + obj.(prop) = val; + end + + function obj = set.PilotTrackingWindow(obj,val) + prop = 'PilotTrackingWindow'; + validateattributes(val,{'numeric'}, ... + {'real','integer','odd','scalar','>',0}, ... + [class(obj) '.' prop], prop); + obj.(prop) = val; + end + + function obj = set.LDPCDecodingMethod(obj, val) + prop = 'LDPCDecodingMethod'; + validateEnumProperties(obj, prop, val); + obj.(prop) = ''; + obj.(prop) = val; + end + + function obj = set.MinSumScalingFactor(obj, val) + prop = 'MinSumScalingFactor'; + validateattributes(val, {'double'}, ... + {'real','scalar','>',0,'<=',1}, ... + [class(obj) '.' prop], prop); + obj.(prop) = val; + end + + function obj = set.MinSumOffset(obj, val) + prop = 'MinSumOffset'; + validateattributes(val, {'double'}, ... + {'real','scalar','>',0}, ... + [class(obj) '.' prop], prop); + obj.(prop) = val; + end + + function obj = set.MaximumLDPCIterationCount(obj, val) + prop = 'MaximumLDPCIterationCount'; + validateattributes(val, {'double'}, ... + {'real','integer','scalar','>',0}, ... + [class(obj) '.' prop], prop); + obj.(prop) = val; + end +end + +end diff --git a/updatecfgMUMIMO.m b/updatecfgMUMIMO.m new file mode 100644 index 0000000..2aa5013 --- /dev/null +++ b/updatecfgMUMIMO.m @@ -0,0 +1,8 @@ +function output = updatecfgMUMIMO(input) + load ('TXPackets\NDPfeedback.mat') + % Apply the steering matrix to the RU + input.RU{1}.SpatialMapping = 'Custom'; + input.RU{1}.SpatialMappingMatrix = steeringMatrix; + + output = input; +end \ No newline at end of file