概述
程序阅读_全面详解LTE:MATLAB建模仿真与实现_自学笔记(1)调制与编码_程序阅读
在粗浅地掌握了LTE知识后,从今天开始对《全面详解LTE:MATLAB建模仿真与实现》一书的学习。在此做个记录,供今后学习与参考。
开始之前,附上源程序的下载路径(源自《全面详解LTE:MATLAB建模仿真与实现》一书)
https://pan.baidu.com/s/11oyJoH8pjUfZvPhD8M3E2A?pwd=1111 (含电子版教材)
写在开始:由于《全面详解LTE:MATLAB建模仿真与实现》一书已经对部分内容有了介绍,但是作为最初接触程序的我,在理解上还是有些困难,因此,在学习过程中,我尽可能的捋清楚代码的目的/逻辑,以及部分语句的应用。
该自学笔记系列将按照教材章节顺序学习,尽量按照章节分类记录,自学内容从第三章开始。【学习程序前,需提前具备一定的移动通信(LTE)知识】
文章目录
- 程序阅读_全面详解LTE:MATLAB建模仿真与实现_自学笔记(1)调制与编码_程序阅读
- 第三章例程注解
- 例程一:chap3_ex01程序理解
- Eb/No与SNR的关系
- Y = step(H,X)语法
- comm.PSKModulator用法
- 仿真结果:
- 解调过程中的软判决与硬判决
- chap3_ex02_qpsk程序解读
- 第四章例程分析
- 例程1:Modulator程序解读
- comm.RectangularQAMModulator
- 例程3:DemodulatorSoft程序解读
- comm.PSKDemodulator系统对象
- comm.RectangularQAMDemodulator系统对象
- 例程5:Scrambler程序解读
- 扰码生成的原理
- 例程7:DescramblerHard程序解读
- comm.GoldSequence系统对象
- 例程8:chap4_ex02程序解读
- 噪声方差和信噪比的关系
- Turbo编码
- Turbo编码原理
- Turbo编码程序
- 例程14:chap4_ex03_performanceBER运行结果
- 例程16~26程序略读
- 例程16:TurboDecoder_crc
- 例程17:CbCRCGenerator
- 例程18:CbCRCDetector
- 例程19:chap4_ex04
- 例程20:chap4_ex04_crc
- 例程21:
- 例程22:RateMatcher
- 例程23:RateDematcher
- 例程24:chap4_ex05_crc
- 例程25:CblkSegParams分块选择函数
- 例程26:CbBitSelection
- 例程27~29:详解LTE传输信道处理MATLAB实例
- chap4_ex06_performanceBER.m
- TbChannelCoding程序解读
- chap4_ex06(important)
第三章例程注解
由于第三章内容是介绍MATLAB的建模/仿真/执行方法及验证通信系统的功能的,因此不涉及系统的知识体系,均以例程形式呈现,故下面将通过不同例程,初步了解LTE的MATLAB通信系统设计过程。
例程一:chap3_ex01程序理解
该例程使用系统对象对一个简单收发系统进行BER分析。收发器由一个QPSK调制器、加性白噪声信道、一个QPSK正交相移键控解调器构成。
4 个通信系统工具箱的系统对象∶
comm.QPSKModulator QPSK正交相移键控调制器
comm. AWGNChannel 加性白噪声信道
**comm. QPSKDemodulator ** QPSK正交相移键控解调器
comm. ErrorRate 误码率
首先附上原文程序(加注释版)
%% Constants
FRM=2048; % Size of bit frame 我们在这里定义一段二进制码流的长度
MaxNumErrs=200; % 最大噪声的承受能力_停止边界条件(仿真停止之前可观察的最大错误字符数)
MaxNumBits=1e7; % 最大比特数_停止边界条件(作为循环结束的依据)_仿真停止之前可处理的最大字符数
EbNo_vector=0:10; % 比特信噪比Eb/No(dB)为信道译码之后,比特能力和干扰信号能量的比值
% Eb/No是指单个比特的信号功率与噪声功率谱密度之比,即一个比特的信号功率与一个Hz内的噪声功率之比;
% Eb/No解调门限(参见:《LTE教程:结构与实施》)
BER_vector=zeros(size(EbNo_vector)); %bit error rate误码率
%% Initializations 初始化
Modulator = comm.QPSKModulator('BitInput',true); % QPSK正交相移键控调制器
AWGN = comm.AWGNChannel; % 加性白噪声
DeModulator = comm.QPSKDemodulator('BitOutput',true); % QPSK正交相移键控解调器
BitError = comm.ErrorRate; % bit error rate误码率
%% Outer Loop computing Bit-error rate as a function of EbNo 外层循环计算误码率作为EbNo的函数
for EbNo = EbNo_vector
snr = EbNo + 10*log10(2); %将Eb/No转化为信噪比,只是经过QPSK调制
% EbN0与SNR 参考链接https://wenku.baidu.com/view/f5bc366219e8b8f67c1cb9c1.html
AWGN.EbNo=snr;
numErrs = 0; numBits = 0;results=zeros(3,1);
%% Inner loop modeling transmitter, channel model and receiver for each EbNo
% 内层循环 对每个EbNo(0到10)的发射机、信道模型和接收机进行建模
while ((numErrs < MaxNumErrs) && (numBits < MaxNumBits))
% Transmitter
u = randi([0 1], FRM,1); % Generate random bits 产生一组随机数据信号
mod_sig = step(Modulator, u); % QPSK Modulator 将u进行QPSK调制
% Channel
rx_sig = step(AWGN, mod_sig); % AWGN channel加上噪声,噪声的参数snr刚刚设置过了
% Receiver
y = step(DeModulator, rx_sig); % QPSK Demodulator QPSK解调
results = step(BitError, u, y); % Update BER 更新误码率
numErrs = results(2);
numBits = results(3);
end
% Compute BER
ber = results(1); bits= results(3);
%% Clean up & collect results
reset(BitError);
BER_vector(EbNo+1)=ber;
end
%% Visualize results 绘制结果图
EbNoLin = 10.^(EbNo_vector/10);
theoretical_results = 0.5*erfc(sqrt(EbNoLin));
semilogy(EbNo_vector, BER_vector)
grid;title('BER vs. EbNo - QPSK modulation');
xlabel('Eb/No (dB)');ylabel('BER');hold;
semilogy(EbNo_vector,theoretical_results,'dr');hold;
legend('Simulation','Theoretical');
通过对上述程序的阅读,明确该程序的主要内容:
仿真思路:
step1:初始化参数以及QPSK正交相移键控调制器、加性白噪声信道、QPSK正交相移键控解调器
step2:生成随机bit作为待调制信号sig;
step3:将信道编码后的bit stream经QPSK调制;
step4:调制信号过awgn信道,改变snr,观察过信道后信号变化;
step5:接收端QPSK解调;
step6:计算误码率,并绘图;
针对上述程序,做如下笔记:
Eb/No与SNR的关系
Eb/N0定义为单位比特能量与噪声谱密度的比值,可以作为信噪比的表示方式,反映的是信号的质量,Eb/N0越高,信号质量越好,Eb/N0与SINR是等价的。
信号质量Eb/N0与调制效率K密切相关,通过以下的香农公式进行关联,lb表示以2为底取对数
K
=
l
b
(
1
+
E
b
/
N
0
)
K=mathrm{lb}left(1+E_{mathrm{b}} / N_{0}right)
K=lb(1+Eb/N0)
根据香农公式,调制效率K与信号质量Eb/N0的关系如图6.10所示。通过采用不同的调制与编码方式,可以得到不同的调制效率。在LTE上行业务中,业务信道普遍采用QPSK或者16QAM的调制方式,显然,16QAM需要更高的Eb/N0。
Q1:对于程序中为什么要将EbNo转换为SNR呢?
A1:因为要加入高斯白噪声信道,高斯白噪声信道的噪声参数是与SNR直接相关的,所以要将EbNo转换为SNR
Q2:为什么仿真要用EbNo,而不用SNR呢?
A2:因为用EbNo可以直观的看到系统性能,比如采用只采用QPSK,那么BER达到千分之一时,EbNo大约为7。因为SNR和EbNo是线性关系,所以用SNR为参数看BER只能看到大概的趋势,不能看到系统性能到底如何。
举例说明:
假如要传送一些速率为1kb/s的数据,信道编码采用1/3卷积编码,每秒在这些编码数据前添加200bit的训练序列,星座映射采用QPSK,波形成形采用因子为0.25的升余弦函数,上采样倍数为10。
首先来看一下经过各模块速率的变化,原始信息速率为1kb/s,1/3卷积编码后变为3kb/s,也就是每秒传3000bit数据,添加200bit的训练序列后,变为每秒传3200bit,此时速率变为3.2kb/s,采用QPSK调制后,速率变为1.6ksymble/s。
SNR和EbNo的转换如下
S
N
R
=
E
b
N
o
⋅
(
1
/
3
)
⋅
(
3000
/
3200
)
⋅
2
⋅
(
1
/
10
)
⋅
(
1
/
1
+
0.25
)
SNR=EbNo·(1/3)·(3000/3200)·2·(1/10)·(1/1+0.25)
SNR=EbNo⋅(1/3)⋅(3000/3200)⋅2⋅(1/10)⋅(1/1+0.25)
用dB表示,就是
SNR=EbNo+10log(1/3)+10log(3000/3200)+10log(2)+10log(1/10)+10log(1/1+0.25)
其中1/3是卷积码引入的,3000/3200是因为添加了训练序列这个额外的开销而引入,2是QPSK引入的,1/10是成形前上采样引入的,1/1+0.25是升余弦波形引入的。
在本程序中,由于只考虑了QPSK调制,因此没有其余项,随着考虑的内容越来越多,这些项会依次出现。
Y = step(H,X)语法
comm.PSKModulator用法
例如:
QPSK = comm.PSKModulator(4, 'BitInput', true, ...
'PhaseOffset', pi/4, 'SymbolMapping', 'Custom', ...
'CustomSymbolMapping', [0 2 3 1]);
4 ModulationOrder:信号星座图中的点数;
‘BitInput’ 选择是否以bit的形式进行输入:
0 (false),则输入值必须是2位输入段的整数表示,范围在0到3之间。
1 (true),则输入必须是均匀长度的二进制向量。元素对是整数的二进制表示。
‘PhaseOffset’ 相位星座图中,以弧度表示的第零点的相位
‘SymbolMapping’ 信号星座位映射,有以下值:
‘Custom’ 通过使用CustomSymbolMapping属性,使用这个值来指定信号星座映射。
‘Binary’
‘Gray’ 使用此值指定灰度编码信号星座映射
‘CustomSymbolMapping’ 自定义星座编码,指定为范围[0,ModulationOrder-1]的值的行向量或列向量。该矢量的第一个元素对应于以PhaseOffset为角度的星座点,随后的元素则逆时针方向运行。
仿真结果:
解调过程中的软判决与硬判决
-
软判决就是解调器将解调后的模拟信号直接接入到译码器来实现解码。
-
硬判决就是对解调器输出信号做N比特量化,分量高于门限就认为输出为1,否则输出为0(根据调制方式,如MPSK,M=2^N,则N比特量化)
在数字通信系统,可以认为硬判决就是N比特量化,软判决就是多比特量化(>>N)
chap3_ex02_qpsk程序解读
输入变量
EbNo 比特信噪比Eb/No(dB)
maxNumErrs 仿真停止之前可观察的最大错误字符数
maxNumBits 仿真停止之前可处理的最大字符数
function [ber, bits]=chap3_ex02_qpsk(EbNo, maxNumErrs, maxNumBits)
%% Initializations
persistent Modulator AWGN DeModulator BitError
if isempty(Modulator)
Modulator = comm.QPSKModulator('BitInput',true);
AWGN = comm.AWGNChannel;
DeModulator = comm.QPSKDemodulator('BitOutput',true);
BitError = comm.ErrorRate;
end
%% Constants
FRM=2048;
M=4; k=log2(M);
snr = EbNo + 10*log10(k);
AWGN.EbNo=snr;
%% Processsing loop modeling transmitter, channel model and receiver 发射器、信道和接收机的回路建模
numErrs = 0; numBits = 0;results=zeros(3,1);
while ((numErrs < maxNumErrs) && (numBits < maxNumBits))
% Transmitter
u = randi([0 1], FRM,1); % Random bits generator
mod_sig = Modulator.step(u); % QPSK Modulator
% Channel
rx_sig = AWGN.step(mod_sig); % AWGN channel
% Receiver
demod = DeModulator.step(rx_sig); % QPSK Demodulator
y = demod(1:FRM); % Compute output bits
results = BitError.step(u, y); % Update BER
numErrs = results(2);
numBits = results(3);
end
%% Clean up & collect results
ber = results(1); bits= results(3);
reset(BitError);
% 运行结果图:https://file2.kaopuke.com:8081/files_image/2023060222/202306022227445800005.png
% https://file2.kaopuke.com:8081/files_image/2023060222/202306022227441742496.png
使用通信工具箱BERTool作为BER仿真的集成测试平台
通过调用该函数,仿真出在不同EbNo条件下的QPSK的BER
第三章其他例程注释,参见百度云文件夹 https://pan.baidu.com/s/1Lyyt5qndIYLjOnBv-7ZFEw?pwd=1111
第四章例程分析
例程1:Modulator程序解读
主要功能:
function y=Modulator(u, Mode)
%% Initialization
persistent QPSK QAM16 QAM64
if isempty(QPSK)
QPSK = comm.PSKModulator(4, 'BitInput', true, ...
'PhaseOffset', pi/4, 'SymbolMapping', 'Custom', ...
'CustomSymbolMapping', [0 2 3 1]);
QAM16 = comm.RectangularQAMModulator(16, 'BitInput',true,...
'NormalizationMethod','Average power',...
'SymbolMapping', 'Custom', ...
'CustomSymbolMapping', [11 10 14 15 9 8 12 13 1 0 4 5 3 2 6 7]);
QAM64 = comm.RectangularQAMModulator(64, 'BitInput',true,...
'NormalizationMethod','Average power',...
'SymbolMapping', 'Custom', ...
'CustomSymbolMapping', [47 46 42 43 59 58 62 63 45 44 40 41 ...
57 56 60 61 37 36 32 33 49 48 52 53 39 38 34 35 51 50 54 55 7 ...
6 2 3 19 18 22 23 5 4 0 1 17 16 20 21 13 12 8 9 25 24 28 29 15 ...
14 10 11 27 26 30 31]);
end
%% Processing
switch Mode
case 1
y=step(QPSK, u);
case 2
y=step(QAM16, u);
case 3
y=step(QAM64, u);
otherwise
error('Invalid Modulation Mode. Use {1,2, or 3}');
end
comm.PSKModulator的使用方法见上文。
comm.RectangularQAMModulator
QAM16 = comm.RectangularQAMModulator(16, 'BitInput',true,...
'NormalizationMethod','Average power',...
'SymbolMapping', 'Custom', ...
'CustomSymbolMapping', [11 10 14 15 9 8 12 13 1 0 4 5 3 2 6 7]);
16 信号星座中的点数
PhaseOffset 星座相位偏移
BitInput 设置是位输入还是整数输入。
当属性设置为true时,step方法输入需要一个0/1值的列向量
当属性设置为false时,这个向量包含0和ModulationOrder-1之间的整数的位表示
NormalizationMethod 星座归一化方法。信号星座归一化方法为:符号间最小距离|平均功率|峰值功率。 默认值是符号之间的最小距离。
SymbolMapping 信号星座位映射,有以下值:
‘Custom’ 通过使用CustomSymbolMapping属性,使用这个值来指定信号星座映射。
‘Binary’
‘Gray’ 使用此值指定灰度编码信号星座映射
例程3:DemodulatorSoft程序解读
软判决解码时,输出为对数似然估计比(LLR)向量
函数包括三个输入:
接收到的调制符号流(u)
当前子帧的噪声变量估计(NoiseVar)
反应调制模式的参数(Mode)
函数计算 LLR作为输出
function y=DemodulatorSoft(u, Mode, NoiseVar)
%% 软判决解码时,输出为对数似然估计比(LLR)向量
%% Initialization
persistent QPSK QAM16 QAM64
if isempty(QPSK)
QPSK = comm.PSKDemodulator(...
'ModulationOrder', 4, ...
'BitOutput', true, ...
'PhaseOffset', pi/4, 'SymbolMapping', 'Custom', ...
'CustomSymbolMapping', [0 2 3 1],...
'DecisionMethod', 'Approximate log-likelihood ratio', ...
'VarianceSource', 'Input port');
QAM16 = comm.RectangularQAMDemodulator(...
'ModulationOrder', 16, ...
'BitOutput', true, ...
'NormalizationMethod', 'Average power', 'SymbolMapping', 'Custom', ...
'CustomSymbolMapping', [11 10 14 15 9 8 12 13 1 0 4 5 3 2 6 7],...
'DecisionMethod', 'Approximate log-likelihood ratio', ...
'VarianceSource', 'Input port');
QAM64 = comm.RectangularQAMDemodulator(...
'ModulationOrder', 64, ...
'BitOutput', true, ...
'NormalizationMethod', 'Average power', 'SymbolMapping', 'Custom', ...
'CustomSymbolMapping', ...
[47 46 42 43 59 58 62 63 45 44 40 41 57 56 60 61 37 36 32 33 ...
49 48 52 53 39 38 34 35 51 50 54 55 7 6 2 3 19 18 22 23 5 4 0 1 ...
17 16 20 21 13 12 8 9 25 24 28 29 15 14 10 11 27 26 30 31],...
'DecisionMethod', 'Approximate log-likelihood ratio', ...
'VarianceSource', 'Input port');
end
%% Processing
switch Mode
case 1
y=step(QPSK, u, NoiseVar);
case 2
y=step(QAM16,u, NoiseVar);
case 3
y=step(QAM64, u, NoiseVar);
otherwise
error('Invalid Modulation Mode. Use {1,2, or 3}');
end
comm.PSKDemodulator系统对象
comm.RectangularQAMDemodulator系统对象
例程5:Scrambler程序解读
生成LTE扰码的函数,产生过程如下:
首先生成一个 Gold 序列产生器系统对象
随后按照 LTE 的准确定义确定 Gold 序列对象的各个属性
使用定义的多项式,在每个子帧开始时调用c_ init 变量进行初始化(设置小区识别码、码字数和子帧引导符等参数)
每个输入采样和 Gold 序列生成器采样相乘得到绕码输出
function y = Scrambler(u, nS)
% Downlink scrambling
persistent hSeqGen hInt2Bit
if isempty(hSeqGen)
maxG=43200;
hSeqGen = comm.GoldSequence('FirstPolynomial',[1 zeros(1, 27) 1 0 0 1],...
'FirstInitialConditions', [zeros(1, 30) 1], ...
'SecondPolynomial', [1 zeros(1, 27) 1 1 1 1],...
'SecondInitialConditionsSource', 'Input port',...
'Shift', 1600,...
'VariableSizeOutput', true,...
'MaximumOutputSize', [maxG 1]);
hInt2Bit = comm.IntegerToBit('BitsPerInteger', 31);
end
% Parameters to compute initial condition
RNTI = 1;
NcellID = 0;
q =0;
% Initial conditions
c_init = RNTI*(2^14) + q*(2^13) + floor(nS/2)*(2^9) + NcellID;
% Convert initial condition to binary vector
iniStates = step(hInt2Bit, c_init);
% Generate the scrambling sequence
nSamp = size(u, 1);
seq = step(hSeqGen, iniStates, nSamp);
seq2=zeros(size(u));
seq2(:)=seq(1:numel(u),1);
% Scramble input with the scrambling sequence
y = xor(u, seq2); %当绕码输入信号生成信道编码输出比特时,进行异或相乘运算
扰码生成的原理
绕码由两部分构成∶伪随机序列生成和比特相乘。
伪随机序列由长度 31 的Gold 序列生成。输出序列定义为两个序列进行异或操作的输出。两个序列的生成多项式为:
X
1
(
x
)
=
x
31
+
x
3
+
1
X
2
(
x
)
=
x
31
+
x
3
+
x
2
+
x
+
1
begin{gathered} X_{1}(x)=x^{31}+x^{3}+1 \ X_{2}(x)=x^{31}+x^{3}+x^{2}+x+1 end{gathered}
X1(x)=x31+x3+1X2(x)=x31+x3+x2+x+1
原理图:
-
X1序列的初始值是固定的,第0位为1,其他各位均为0,也就是说初始值总是“0000000000000000000000000000001”
-
X2序列的初始值cinit是可变的,由循环前缀CP类型、小区ID、时隙号和OFDM符号的序号等参数共同决定
扰码生成公式:
c init = n R N T I × 2 14 + q × 2 13 + ⌊ n s / 2 ⌋ × 2 9 + N I D cell c_{text {init }}=n_{mathrm{RNTI}} times 2^{14}+q times 2^{13}+leftlfloor n_{mathrm{s}} / 2rightrfloor times 2^{9}+N_{mathrm{ID}}^{text {cell }} cinit =nRNTI×214+q×213+⌊ns/2⌋×29+NIDcell
n R N T I n_{mathrm{RNTI}} nRNTI就是终端的C-RNTI;q代表传输块(TB)的编号,取值为0或1;ns代表无线帧内时隙的序号,取值为0~19; N I D cell N_{mathrm{ID}}^{text {cell }} NIDcell 是小区的PCI,长9bit,取值范围为0~503。
注:comm.GoldSequence系统对象见下文介绍
例程7:DescramblerHard程序解读
使用通信系统工具箱组件,执行LTE扰码的解调,DescramblerHard函数是使用硬判决进行解调的,函数对解调输出的比特流和Gold序列进行异或运算。先附程序:
function y = DescramblerHard(u, nS)
% nS当前子帧索引的参数
% Downlink descrambling 接收端解码
persistent hSeqGen hInt2Bit;
if isempty(hSeqGen)
maxG=43200; %最大输出长度'MaximumOutputSize'系统对象中要用
hSeqGen = comm.GoldSequence('FirstPolynomial',[1 zeros(1, 27) 1 0 0 1],...
'FirstInitialConditions', [zeros(1, 30) 1], ...
'SecondPolynomial', [1 zeros(1, 27) 1 1 1 1],...
'SecondInitialConditionsSource', 'Input port',...
'Shift', 1600,...
'VariableSizeOutput', true,...
'MaximumOutputSize', [maxG 1]);
hInt2Bit = comm.IntegerToBit('BitsPerInteger', 31); %将输入向量中的每个整数映射成31位的bit0/1类型,并输出向量
end
%% LTE下行共享物理信道的处理流程(LTE硬解调)
% Parameters to compute initial condition计算初始条件的参数
RNTI = 1; %RNTI,是终端无线侧的标识,由基站通过随机接入过程分配,长度为16bit,在基站范围内唯一
NcellID = 0; %小区的物理层ID
q=0; %q代表传输块(TB)的编号
% Initial conditions
c_init = RNTI*(2^14) + q*(2^13) + floor(nS/2)*(2^9) + NcellID;
% Convert to binary vector
iniStates = step(hInt2Bit, c_init);
% Generate scrambling sequence 生成加扰序列
nSamp = size(u, 1);
seq = step(hSeqGen, iniStates, nSamp);
% Descramble
y = xor(u(:,1), seq(:,1));
扰码生成公式:
c
init
=
n
R
N
T
I
×
2
14
+
q
×
2
13
+
⌊
n
s
/
2
⌋
×
2
9
+
N
I
D
cell
c_{text {init }}=n_{mathrm{RNTI}} times 2^{14}+q times 2^{13}+leftlfloor n_{mathrm{s}} / 2rightrfloor times 2^{9}+N_{mathrm{ID}}^{text {cell }}
cinit =nRNTI×214+q×213+⌊ns/2⌋×29+NIDcell
comm.GoldSequence系统对象
从序列集中生成Gold序列,代码例子如下:
hSeqGen = comm.GoldSequence('FirstPolynomial',[1 zeros(1, 27) 1 0 0 1],...
'FirstInitialConditions', [zeros(1, 30) 1], ...
'SecondPolynomial', [1 zeros(1, 27) 1 1 1 1],...
'SecondInitialConditionsSource', 'Input port',...
'Shift', 1600,...
'VariableSizeOutput', true,...
'MaximumOutputSize', [maxG 1]);
Gold序列通过阶数、生成多项式和初始值三个关键的参数来定义。
设置属性值:
FirstPolynomial 第一个PN序列的生成多项式(Gold序列),在此处相当于’z^31 + z^3 + 1’
FirstInitialConditions 初始值:移位寄存器初值可以理解为Gold序列的起始相位,X1的初始值总是固定的
SecondPolynomial 第二个PN序列的生成多项式
SecondInitialConditionsSource 用于第二首选PN序列发生器的移位寄存器的初始条件的来源,分别为:
‘Property’ 通过使用SecondInitialConditions属性指定PN序列生成器的初始条件
‘Input port’ 使用secondinitcond输入参数指定PN序列生成器的初始条件。
Shift 输出序列到起始点的偏移量
VariableSizeOutput 启用可变大小的输出,指定为数字或逻辑0 (false)或1 (true)。
true:要使用outputsize输入参数控制Gold序列输出样本数量。
false:SamplesPerFrame属性指定输出样本的数量。
MaximumOutputSize 最大输出帧大小,以向量[m,1]的形式指定,其中m是一个正整数
例程8:chap4_ex02程序解读
function [ber, numBits]=chap4_ex02(EbNo, maxNumErrs, maxNumBits)
%% Constants
FRM=2400; % Size of bit frame
clear functions
% ModulationMode=1; % QPSK
% ModulationMode=2; % QAM16
ModulationMode=3; % QAM64
k=2*ModulationMode; % Number of bits per modulation symbol
snr = EbNo + 10*log10(k);
noiseVar = 10.^(0.1.*(-snr)); % Compute noise variance计算噪声方差
% 根据SNR_in = 10*log10(signal_power/nosie_power),当信号功率为1时,噪声方差为上式
%% Processsing loop: transmitter, channel model and receiver
numErrs = 0;
numBits = 0;
nS=0;
while ((numErrs < maxNumErrs) && (numBits < maxNumBits))
% Transmitter
u = randi([0 1], FRM,1); % Randomly generated input bits
t0 = Scrambler(u, nS); % Scrambler
t1 = Modulator(t0, ModulationMode); % Modulator
% Channel
c0 = AWGNChannel(t1, snr); % AWGN channel
% Receiver
r0 = DemodulatorSoft(c0, ModulationMode, noiseVar); % Demodulator
r1 = DescramblerSoft(r0, nS); % Descrambler
y = 0.5*(1-sign(r1)); % Recover output bits
% Measurements
numErrs = numErrs + sum(y~=u); % Update number of bit errors
numBits = numBits + FRM; % Update number of bits processed
% Manage slot number with each subframe processed
nS = nS + 2;
nS = mod(nS, 20);
end
%% Clean up & collect results
ber = numErrs/numBits; % Compute Bit Error Rate (BER)
噪声方差和信噪比的关系
首先知道信噪比是信号功率与噪声功率之比
S
N
R
=
10
∗
l
o
g
10
(
S
i
g
n
a
l
p
o
w
e
r
N
o
i
s
e
p
o
w
e
r
)
SNR = 10*log_{10}(frac{Signal_{power}}{Noise_{power}})
SNR=10∗log10(NoisepowerSignalpower)
因此,噪声功率(即噪声方差)Nosie_power可以表示为:
N
o
i
s
e
p
o
w
e
r
=
S
i
g
n
a
l
p
o
w
e
r
×
1
0
−
S
N
R
10
Noise_{power}=Signal_{power}times10^{-frac{SNR}{10}}
Noisepower=Signalpower×10−10SNR
观察程序,
noiseVar = 10.^(0.1.*(-snr)); % Compute noise variance计算噪声方差
可知其来历,因为在设计随即比特信号时,将其功率设置为1,因此Signal_{power}省略了。
Turbo编码
Turbo编码巧妙地将两个简单分量码通过伪随机交织器并行级联来构造具有伪随机特性的长码,并通过在两个软入/软出(SISO)译码器之间进行多次迭代实现了伪随机译码。
Turbo编码原理
参考《差错控制编码》第16章
Turbo 编码器的输出为三个比特流。第一个的比特通常是系统比特。基本的编码器系统包括信息序列(输入序列),两个(2,1,v)系统反馈(递归)卷积编码器和一个交织器(用 π 表示)。这里假设信息序列包含K‘个信息比特加上使得第一个编码器回到全零状态S0=0的v个收尾比特,其中v是第一个编码器的约束长度。信息序列(包含收尾比特)被认为是一个长度为K=K’+v的分组,并且由下面的向量表示
u
=
(
u
0
,
u
1
,
⋯
,
u
K
−
1
)
boldsymbol{u}=left(u_{0}, u_{1}, cdots, u_{K-1}right)
u=(u0,u1,⋯,uK−1)
由于编码采用系统形式,因此信息序列u是首先被传输的序列,即
$$
begin{gathered}
boldsymbol{u}=boldsymbol{v}{(0)}=left(v_{0}{(0)}, v_{1}^{(0)}, cdots, v_{K-1}^{(0)}right)
end{gathered}
第一个编码器产生校验序列
第一个编码器产生校验序列
第一个编码器产生校验序列
begin{gathered}
boldsymbol{v}{(1)}=left(v_{0}{(1)}, v_{1}^{(1)}, cdots, v_{K-1}^{(1)}right)
end{gathered}
∗
∗
交织器对信息分组中的
K
个比特进行次序重排或者置换
∗
∗
,以便第二个编码器接收的置换后的信息序列
u
′
不同于第一个。(注意到第二个编码器未必会收尾。)第二个编码器产生的校验序列可以表示为
**交织器对信息分组中的 K个比特进行次序重排或者置换**,以便第二个编码器接收的置换后的信息序列 u'不同于第一个。(注意到第二个编码器未必会收尾。)第二个编码器产生的校验序列可以表示为
∗∗交织器对信息分组中的K个比特进行次序重排或者置换∗∗,以便第二个编码器接收的置换后的信息序列u′不同于第一个。(注意到第二个编码器未必会收尾。)第二个编码器产生的校验序列可以表示为
boldsymbol{v}{(2)}=left(v_{0}{(2)}, v_{1}^{(2)}, cdots, v_{kappa-1}^{(2)}right)
最终被传输的序列为:
最终被传输的序列为:
最终被传输的序列为:
boldsymbol{v}=left(v_{0}^{(0)} v_{0}^{(1)} v_{0}^{(2)}, v_{1}^{(0)} v_{1}^{(1)} v_{1}^{(2)}, cdots, v_{K-1}^{(0)} v_{K-1}^{(1)} v_{K-1}^{(2)}right)
$$
【注:由于RSC编码器不能如非递归编码器一样通过输入连“0”序列来使编码器复位(网格终止),因此通过设计如图8所示的A、B间的开关来控制编码器终止(AB连接时,发现相同的反馈进行异或送进存储器时数据为0),当一帧结束时,开关由A打到B,则经过m时刻后,编码器复位,可以对下一帧数据进行编码。这里m=2。只有RSC编码器1(外编码器)进行了网络终止,RSC编码器2保持开放。】
说明:根据Turbo编码器结构图,在第一个数据输入前,认为编码器内全0.
举例假设:
假设输入序列为: U = (1 0 1 0 1 1 0 1)
第一个子码的校验序列为 v1 = (1 1 0 0 0 1 1 1)
假设交织后的第二个子码的输入序列为:U’ = (1 1 0 1 1 0 0 1)
第二个子码的校验序列为: v2 = (1 0 0 1 1 0 1 1)
Turbo码的输出序列为:V = (111,010,100,001,101,110,011,111)
(暂时看不懂)LTE Turbo编码器是一种使用QPP交织的无竞争编码器,它通过在交织过程中流水线式访问内存提高编码器性能。卷积编码器的网格结构由下面两个多项式表示(详细解释见《全面详解LTE:MATLAB建模仿真与实现》P80):
G
0
(
z
)
=
1
+
z
−
2
+
z
−
3
G
1
(
z
)
=
1
+
z
−
1
+
z
−
3
begin{aligned} &G_{0}(z)=1+z^{-2}+z^{-3} \ &G_{1}(z)=1+z^{-1}+z^{-3} end{aligned}
G0(z)=1+z−2+z−3G1(z)=1+z−1+z−3
它描述了一个1/3Turbo编码器有3个状态,且其网格结构可用两个前馈和反馈多项式描述,它各拥有13和15倍频程。
通过考察上图中 Turbo编码器的区块图,我们可以看到编码器约束长度为4,生成多项式矩阵13,57,反馈多项式迭代次数 13。因此,为了设置网格结构的阶数,我们设置 poly2trellis 函数为 poly2trellis(4,[13,15],13)
Turbo编码程序
Turbo = comm.TurboEncoder('TrellisStructure', poly2trellis(4, [13 15], 13), ...
'InterleaverIndicesSource','Input port');
参数的使用:
TrellisStructure 组成卷积码的网格描述,指定为包含速率K/N码的网格描述的结构。
K为输入bit streams的个数,N为输出bit streams的个数。
poly2trellis(4, [13 15], 13) 将卷积码多项式转换为网格描述(此处未看懂)
trellis = poly2trellis(ConstraintLength,CodeGenerator,FeedbackConnection)
ConstraintLength指定输入位流到编码器的延迟时间。
CodeGenerator指定到编码器的输入位流的输出连接。
FeedbackConnection为每个到编码器的K个输入位流指定反馈连接。
InterleaverIndicesSource 指定交织索引的来源。
’ property ',通过InterleaverIndices属性指定的interleaver索引执行。
‘Input port’,使用输入参数interlvrindices(交织指数)执行。
交错器索引和二进制输入消息的向量长度和值可以随着每次调用而改变。
例程14:chap4_ex03_performanceBER运行结果
通过运行chap4_ex03_performanceBER程序,绘制出Turbo码随迭代次数增加性能的改变,结果图如下
例程16~26程序略读
+++
例程16:TurboDecoder_crc
TurboDecoder_crc:
执行LTE turbo译码器时在输入帧结尾检查 CRC 比特以确定是否在所有迭代完成之前终止译码操作。
LTETurboDecoder系统对象:
在 LTETurboDecoder中,在每次译码迭代时,对最后 24 个输出采样对应的CRC 比特进行校验以确定是否有错误。假如没有错误被查出,我们跳出循环并终止 Turbo 译码运算。
+++
例程17:CbCRCGenerator
CbCRCGenerator:在 Turbo 编码输入的传输块结尾附加了24bit的 CRC
+++
例程18:CbCRCDetector
CbCRCDetector:在译码之后,在传输块结尾解出24bit的 CRC
+++
例程19:chap4_ex04
在这个程序中,包含了CRC生成、Turbo编码、绕码,和调制及其反过程,但不包括早期终止机制。
+++
例程20:chap4_ex04_crc
此程序包括早期终止机制,书中将例程19和例程20的运算时间和ber性能做了对比。
+++
例程21:
计算使用早期终止机制的程序和不使用早期终止机制的时间,并作对比。
+++
例程22:RateMatcher
RateMatcher:三个码率匹配的特征∶子块交织、奇偶校验比特隔行和比特收集。
码率匹配的输入:1/3Turbo 编码器的输出。对于长度为 K的输入块,码率匹配器的输入为3(K+4),包括系统和两个奇偶校验共三个比特流。
function y= RateMatcher(in, Kplus, Rate)
输入的三个参数分别为:
in 经过CRC编码后生成Turbo码,2432bit数据变为7308bit
Kplus 每个子码块的长度反映在参数 Kplus
Rate 需要匹配的码率
码率匹配流程:
首先,我们分隔每三个流为32bit 的区块,并对它们进行交织。考虑到每个流可能无法每 32bit分隔,我们在每个流的开始增加一些哑比特,以使向量可以分隔成整32bit。子块交织过程对系统和奇校验比特相同,但对偶校验比特不同。
随后,生成包括附加哑比特的系统比特和隔行奇偶校验比特的输出向量。
最后,通过移除哑比特,我们生成循环缓冲器进行码率收缩。码率匹配的最后一步是比特收集,在此步骤中循环缓冲器中的哑比特被移除,最初的几个比特被收集。
收集比特与 Turbo 编码器输入长度的比值就是码率匹配之后的新码率。
+++
例程23:RateDematcher
反向码率匹配
+++
例程24:chap4_ex05_crc
通过在 Turbo 编码器之前添加码率匹配以及在译码器之前添加码率去匹配操作,我们可以仿真出任意不同于1/3码率的情况,较低码率在传输中用于较好的信道条件,只需要较少的纠错过程。通过更新函数中的变量CodingRate,我们可以进行码率匹配操作并考察多种码率情况下 BER 随SNR 变化的情况。1/3码率收发端要比 1/2码率收发端性能好很多。
debug:
在学习《全面详解LTE:MATLAB建模仿真与实现》第四章时,运行chap4_ex04_performanceBER.m,MATLAB出现如下报错:
错误使用 commLTETurboDecoder/step
没有为类 'commLTETurboDecoder' 定义方法 'stepImpl',或者该方法已从 MATLAB 的搜索路径中删除。
出错 TurboDecoder_crc (第 10 行)
[y, flag, iters] = step(TurboCrc, u, intrlvrIndices);
出错 chap4_ex05_rate (第 28 行)
[y, ~, iters] = TurboDecoder_crc(-r2, Indices); % Turbo Deocder
出错 chap4_ex05_performanceBER (第 13 行)
ber_third(n)=chap4_ex05_rate(EbNo, maxNumErrs, maxNumBits, CodingRate);
function commLTETurboDecoder used in “Understanding LTE with Matlab” is missing
原因:由于matlab版本过高,matlab官方在2016版之后弃用了commLTETurboDecoder函数,因此在运行该段程序时会报错。
解决方法:在matlab2015b中安装communications system toolbox 6.1工具箱,找到该路径下的函数
/Applications/MATLAB_R2015b.app/toolbox/comm/commdemos/commLTETurboDecoder.m
下载并安装MATLAB_R2015b软件后,找到对应路径下的 commLTETurboDecoder.m 文件,放置在当前文件夹下,即可正常运行。
我已经将commLTETurboDecoder.m 文件下载好了,无需再安装matlab2015b,放在网盘上,供大家下载。
https://pan.baidu.com/s/1MvyWgsR2UeMgWCDZDaX6gw?pwd=1111
+++
除此之外,此函数还存在缺陷,运行中会出现如下报错:将zVisualize_ex05.m文件中的snr修改为snr6,如图。
错误使用 snr (第 87 行)
输入参数的数目不足。
出错 zVisualize_ex05 (第 3 行)
semilogy(snr,ber6,'ob')
出错 chap4_ex05_performanceBER (第 28 行)
zVisualize_ex05(snr, ber_third, ber_half);
解决方案:将zVisualize_ex05.m文件中的snr修改为snr6,如图。
修改上述两处地方,即可正常运行,画出图像,如图所示:
例程25:CblkSegParams分块选择函数
对于该函数的解释,翻译版《全面详解LTE:MATLAB建模仿真与实现》一书,翻译的确实不够准确(稀烂)。
原版书是这么写的:
If the input frame to the turbo encoder exceeds the maximum size, the transport block is usually divided into multiple smaller blocks known as codeblocks. Since the internal interleaver of the turbo encoder is only defined for 188 input block sizes, the sizes of these codeblocks need to match the set of codeblock sizes supported by the turbo coder.
意思是:如果输入帧大于Turbo编码器的最大处理长度,那么就分成若干小块处理。Turbo编码器内部的交织器仅仅只定义了188种长度类型(看程序也可以看得出来,validK是188个长度类型,最小的是40,最长的是6144,因此当长度大于6144时,就要分块处理,如果不大于,就选择合适的Turbo编码器长度)
function [C, Kplus] = CblkSegParams(tbLen)
%#codegen
%% Code block segmentation
blkSize = tbLen + 24;
maxCBlkLen = 6144; % 判断Turbo编码器的最大处理长度是否大于6144
if (blkSize <= maxCBlkLen)
C = 1; % number of code blocks
b = blkSize; % total bits
else
L = 24;
C = ceil(blkSize/(maxCBlkLen-L));
b = blkSize + C*L;
end
% Values of K from table 5.1.3-3 Turbo编码器内部的交织器定义的188种长度类型
validK = [40:8:512 528:16:1024 1056:32:2048 2112:64:6144].';
% First segment size
temp = find(validK >= b/C);
Kplus = validK(temp(1), 1); % minimum K 找到合适的且最小的K值
例程26:CbBitSelection
CbBitSelection:
中文版《全面详解LTE:MATLAB建模仿真与实现》翻译的稀碎,看一下原版对于该函数的解释:
The following MATLAB function calculates the sizes of subblocks and determines how many are processed in parallel to reconstitute the channel coding outputs. First we divide the total number of codeword bits by the number of subblocks. For each subblock, we ensure that the number of output bits is divisible by the number of modulation bits and the resulting number of multi-antenna layers.
通过第四章前26个例程的分析,分别仿真了调制编码过程中的扰码生成与解码、信道编码与解码、Turbo编码与译码、早期终止机制、码率匹配、码块分段等内容,下面通过例程27~29,将这些知识串联在一起。
+++
例程27~29:详解LTE传输信道处理MATLAB实例
通过对之前程序的学习,目前可以完整的仿真出LTE传输信道的处理流程,根据所给例程,逐一解读其含义:
chap4_ex06_performanceBER.m
该段函数,算是ex06的主程序,通过运行该函数(赋值后),将参数传入chap4_ex06中,实现调用。
ex06主要实现了:探究不同迭代次数对ber(误码率)的影响。
主要功能写在了注释中:
%% Comparing BER performance of transport block processing 比较传输块处理的误码率性能
%% as a function of max. number of iterations
maxNumErrs=1e4; % 仿真停止之前可观察的最大错误字符数
maxNumBits=1e7; % 最大比特数_停止边界条件(作为循环结束的依据)
%% Adaptive modulation and coding parameters
CodingRate=1/2; % Choose any coding rate between 1/3 to 0.99 匹配1/2码率
% 不同码率的效果:
% 较低码率在传输中用于较好的信道条件,它只需要较少的纠错过程。
% 通过更新函数中的变量 CodingRate,我们可以进行码率匹配操作并考察多种码率情况下 BER 随SNR变化的情况。
% 1/3码率收发端要比1/2码率收发端性能好很多。
Mode=1; % Choose either of 1 for QPSK or 2 for QAM16 or 3 for QAM64 选择QPSK
maxEbNo=2; % Range of SNR values examined 最大EbNo值为2
Modulation={'QPSK','QAM16','QAM64'};
snr=linspace(0,maxEbNo,6); % 生成从0-2等间隔的6个数(矩阵)
ber=zeros(6,numel(snr));
%%
for nIter=1:6 %迭代次数从1到6
prmLTE.Mode=Mode; %将调制模式、码率、迭代次数写入prmLTE结构体中
prmLTE.Rate=CodingRate;
prmLTE.maxIter=nIter;
for n=1:numel(snr)
EbNo=snr(n);
fprintf(1,'Modulation = %s; Rate = %8.4f ; Iters = %4.0f ; EbNo = %8.5fn',Modulation{Mode}, CodingRate, nIter, EbNo);
ber(nIter, n)=chap4_ex06(EbNo, maxNumErrs, maxNumBits, prmLTE);
fprintf(1,'BER = %12.8fn',ber(nIter,n));
end
end
%% Visualize results
zVisualize_ex06(snr, ber);
TbChannelCoding程序解读
包含的模块
- 传输块添加 CRC;
- 码块分段和码块CRC添加;
- 1/3码率Turbo编码;
- 匹配任意所需码率;
- 码块连接。
Transport block channel coding 传输码块分段的编码
输入:
in 经过CRC编码后的数据
prmLTE结构体:在chap4_ex06_performanceBER.m中定义
prmLTE.Mode 调制模式
prmLTE.Rate 码率
prmLTE.maxIter 迭代次数
function [out, Kplus, C] = TbChannelCoding(in, prmLTE)
%#codegen
%% Initializations
inLen = size(in, 1); %确定in数据长度
[C, Kplus] = CblkSegParams(inLen-24); %码块分段函数:一个码块内子码块的数量反映在参数 C
%每个子码块的长度反映在参数 Kplus(不懂)
%判断数据长度是否大于6144,如果大于,分块,K值表有定义,支持188种输入长度
intrlvrIndices = lteIntrlvrIndices(Kplus); %使用ltelntrlvrIndices 函数构建使用QPP的 LTE 交织器。
%这个函数可查找LTE交织器表中支持的188个输入长度,并查找相应的 fl 和 f 常数,
%然后计算得到符合标准定义的序列向量。得到交织(打乱)后的排列序号。
CodingRate=prmLTE.Rate;
Qm=2*prmLTE.Mode;
NumLayers=1;
G=ceil((Kplus+4)/CodingRate); % 码率匹配器的输入为(K+4)/CodingRate,包括系统和两个奇偶校验共三个比特流。
E_CB=CbBitSelection(C, G, NumLayers, Qm); %(不理解)
% Initialize output
out = false(G, 1);
%% Processing: Channel coding the TB
if (C==1) % single CB, no CB CRC used传输块只包含单一码块,因此不需要在码字上添加CRC,因为在传输快已经添加过了
% Turbo encode
tEncCbData = TurboEncoder( in, intrlvrIndices); %生成Turbo码,2432bit数据变为7308bit
% Rate matching, with bit selection
rmCbData = RateMatcherTB(tEncCbData, Kplus, G);
% unify code paths
out = logical(rmCbData);
else % multiple CBs in TB
startIdx = 0;
for cbIdx = 1:C
% Code-block segmentation
cbData = in((1:(Kplus-24)) + (cbIdx-1)*(Kplus-24));
% Append checksum to each CB
crcCbData = CbCRCGenerator( cbData);
% Turbo encode each CB
tEncCbData = TurboEncoder(crcCbData, intrlvrIndices);
% Rate matching with bit selection
E=E_CB(cbIdx);
rmCbData = RateMatcherTB(tEncCbData, Kplus, E);
% Code-block concatenation
out((1:E) + startIdx) = logical(rmCbData);
startIdx = startIdx + E;
end
end
chap4_ex06(important)
函数主要功能(步骤):
发射端:
产生随机数据码(模拟真实数据)
为每个输入数据生成循环冗余校验(CRC)码位,并将其附加到数据帧中,由CbCRCGenerator完成
传输信道编码,由TbChannelCoding函数实现(后文细说)
扰码生成(加扰),由Scrambler完成
最后进行QPSK、16PSK等编码(暂未实现OFDM仿真),由Modulator完成
传输过程:
- 添加加性高斯白噪声
接收端:
QPSK解调(软判决),由DemodulatorSoft完成
解扰码,添加扰码的逆过程,采用软判决,由DescramblerSoft完成
传输信道解码,由TbChannelDecoding完成
使用CRC检测输入数据中的错误,由CbCRCDetector完成
最终算出误码率ber
function [ber, numBits]=chap4_ex06(EbNo, maxNumErrs, maxNumBits, prmLTE)
%% Constants 参数在chap4_ex06_performanceBER.m设置好了
clear functions
FRM=2432-24;
ModulationMode=prmLTE.Mode;
CodingRate=prmLTE.Rate;
k=2*ModulationMode;
snr = EbNo + 10*log10(k) + 10*log10(CodingRate);
noiseVar = 10.^(-snr/10);
%% Processsing loop modeling transmitter, channel model and receiver
numErrs = 0; numBits = 0; nS=0;
while ((numErrs < maxNumErrs) && (numBits < maxNumBits))
% Transmitter
u = randi([0 1], FRM,1); % Randomly generated input bits产生随机数据码
data= CbCRCGenerator(u); % 为每个输入数据生成循环冗余校验(CRC)码位,并将其附加到数据帧中
[t1, Kplus, C] = TbChannelCoding(data,prmLTE); % Transport Channel encoding 传输信道编码
% 通过TbChannelCodin后,生成t1是经过DLSCH处理后的比特编码,将t1传送至PDSCH信道发送
% 包含的模块
% 1)传输块添加 CRC;
% 2)码块分段和码块CRC添加;
% 3)1/3码率Turbo编码;
% 4)匹配任意所需码率;
% 5)码块连接。
t2 = Scrambler(t1, nS); % Scrambler扰码生成
t3 = Modulator(t2, ModulationMode); % Modulator QPSK调制
% Channel
c0 = AWGNChannel(t3, snr); % AWGN channel 加性高斯噪声
% Receiver
r0 = DemodulatorSoft(c0, ModulationMode, noiseVar); % Demodulator 软判决解调
r1 = DescramblerSoft(r0, nS); % Descrambler 解扰码
r2= TbChannelDecoding(r1, Kplus, C, prmLTE); % Transport Channel decoding 传输信道解码
y = CbCRCDetector(r2); % Code block CRC dtector 使用CRC检测输入数据中的错误
% Measurements
numErrs = numErrs + sum(y~=u); % Update number of bit errors检测输入和输出不相等的数量
numBits = numBits + FRM; % Update number of bits processed
% Manage slot number with each subframe processed
nS = nS + 2; nS = mod(nS, 20);
end
%% Clean up & collect results
ber = numErrs/numBits;
最终绘制出的,在不同迭代次数下,误码率的不同曲线。
到此,第三四章内容已全部学习完成。下周开始学习OFDM调制内容。
最后
以上就是昏睡冰棍为你收集整理的程序阅读_全面详解LTE:MATLAB建模仿真与实现_自学笔记(1)调制与编码_程序阅读程序阅读_全面详解LTE:MATLAB建模仿真与实现_自学笔记(1)调制与编码_程序阅读的全部内容,希望文章能够帮你解决程序阅读_全面详解LTE:MATLAB建模仿真与实现_自学笔记(1)调制与编码_程序阅读程序阅读_全面详解LTE:MATLAB建模仿真与实现_自学笔记(1)调制与编码_程序阅读所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复