
做光纤通信系统仿真一上来就对着“10Gbps 正交相位移键控QPSK光纤通信系统”这种标题很容易被吓到。其实拆开看核心就三件事搭一条能跑的QPSK光纤链路把真实光纤里的衰减、色散、非线性和ASE噪声建模进去然后在不同传输距离、不同入纤功率下把误码性能和星座图测出来。10Gbps是线路比特率QPSK一个符号带2bit所以符号率只有5Gbaud这会直接影响后面的滤波器带宽、采样率和色散评估。适合正在做光纤通信课程设计、Matlab仿真大作业或者想搞懂相干光通信链路到底怎么搭建的人来参考。我这次重写工程时没有把所有代码堆在一个main脚本里而是按“发射端、光纤信道、接收端、性能分析”拆了模块。这样后面你不管想换成16QAM、加WDM信道还是把单模光纤改成色散补偿链路都不需要重写主框架。下面我把设计思路、光纤损伤建模、源码关键片段、仿真结果和踩过的坑一起写清楚。1. 项目概述与核心需求拆解1.1 系统到底在做哪几件事整个10Gbps QPSK系统可以分成四个环节你脑子里先有这张图后面看代码就不会乱。发射端先把随机比特流做串并转换每2bit映射成一个QPSK符号比如00对应11j01对应-11j11对应-1-1j10对应1-1j这样形成I/Q两路信号。然后经过根升余弦脉冲成形变成5Gbaud的基带波形。如果仿真的是光域发射还要在Matlab里把这个复数基带信号加载到光载波上这里通常用IQ调制器模型严格一点可以加上消光比、相位偏置误差等非理想因素。光纤信道是这个项目里最有分量的一块。真实光纤不是简单加个高斯白噪声就行它至少包含三部分损伤线性衰减、色散、非线性效应。远距离传输还需要EDFA放大器所以ASE噪声也要加进来。我在工程里用的是分步傅里叶法把光纤切成很多小段每一段里先算衰减再算色散然后算自相位调制最后统一加ASE噪声。这样可以同时看到线性和非线性损伤而不是只有单一因素。接收端做的是相干接收。本振光和接收光混频得到I/Q两路基带信号再经过匹配滤波、时钟采样、载波相位估计、判决最后把恢复出的比特和发射比特对比统计误码率。过程中可以随时用scatterplot看星座图用eyediagram看眼图这是判断链路质量最直观的方式。1.2 为什么选QPSK而不是OOK或16QAM很多人会问10Gbps时代不是OOK最常用吗为什么要用QPSK答案很简单QPSK用相位携带信息符号率只有5Gbaud而OOK要做到10Gbps就得跑10Gbaud。符号率越高占用的带宽越大光纤色散对不同频率分量造成的延迟差也越明显系统容忍度就变差。用QPSK后信号带宽几乎减半在同样的色散限制下可以传更远。从频带利用率和抗噪能力来看QPSK处在一个很舒服的平衡点。16QAM虽然能用2.5Gbaud实现10Gbps但星座点密度高一倍要达到同样的误码率需要的OSNR比QPSK高很多。在实际工程里10G QPSK的意义在于它是从强度调制走向相位调制、从直接检测走向相干检测的第一道门槛。你把它吃透了后面再理解16QAM、偏振复用、100G相干系统都会顺很多。调制格式10Gbps对应符号率频带利用率相对OSNR代价工程复杂度OOK10 Gbaud约1 bit/s/Hz较高低直接检测QPSK5 Gbaud约2 bit/s/Hz中等中相干检测16QAM2.5 Gbaud约4 bit/s/Hz高高相位估计复杂1.3 仿真工程的结构怎么设计我拿到一个仿真项目第一件事不是写代码而是先把仿真层级拆清楚。这个工程我拆成了下面几个模块main主脚本设置参数、调用各模块、循环扫描条件、输出BER曲线。qpsk_mapper / qpsk_demapper比特映射与解映射使用Gray编码。pulse_shaping根升余弦成形和匹配滤波。fiber_channel分步傅里叶实现光纤链路内部处理衰减、色散、SPM和ASE。edfa_amp掺铒光纤放大器模型按目标OSNR或噪声指数加入ASE。dispersion_compensator频域色散补偿也可以换成DCF参数。ber_analysis统计误码、画星座图和眼图。这样拆的好处是你调误码率时不用去翻矩阵运算代码只要改fiber_channel里的参数你想看不同调制格式的性能只需要换mapper和demapper。后面章节我会直接把这些模块的关键代码拿出来讲。2. 核心细节解析光纤损伤是如何建模的2.1 衰减与ASE噪声先搞清楚dB/km和1/km单模光纤在1550nm窗口的典型衰减是0.2dB/km。很多人直接把衰减写进仿真时会犯一个单位错误0.2dB/km不能直接作为幅度衰减系数用。衰减定义是P_out P_in * 10^(-α_dB * L / 10)如果要在电场幅度上乘一个系数需要把dB/km换算成线性幅度单位。常用的换算关系是α_linear α_dB / (10 * log10(e))也就是除以约4.343。所以0.2dB/km对应的线性幅度衰减系数约0.046 /km。在分步傅里叶的每个步长里我对信号乘exp(-α_linear * h / 2)这里的h是步长单位是km。ASE噪声一般通过EDFA加进去。工程上常用OSNR来描述光信噪比OSNR定义在0.1nm参考带宽内在1550nm附近0.1nm大约对应12.5GHz。仿真时我会先根据目标OSNR算出噪声功率谱密度再在信道输出端乘上对应带宽的ASE噪声。要注意ASE噪声是加性的复高斯噪声它在I路和Q路都有分量所以代码里要用randn(1,N) 1j * randn(1,N)的形式生成。2.2 色散为什么单模光纤会让脉冲展宽色散虽然叫“色”但它对单波长信号照样有影响因为调制信号本身不是单频而是有一定带宽。1550nm处普通单模光纤的色散系数D大约是17ps/(nm·km)含义是一束光里波长差1nm的两个分量每传输1km会拉开17ps的时延差。QPSK符号率5Gbaud一个符号周期是200ps。如果信号谱宽大概0.04nm传输80km后谱内最高和最低频率分量的时延差大约是D * L * Δλ 17 * 80 * 0.04 ≈ 54ps。这个数字已经占到符号周期的四分之一以上眼图会明显闭合误码率迅速恶化。在频域建模时色散可以看成一个全通滤波器只改相位不改幅度。基带频域传递函数我写成function out add_dispersion(in, fs, lambda, D_ps_nm_km, L) N length(in); f fftshift((0:N-1) * fs / N - fs / 2); % 将 D 从 ps/(nm*km) 换算为 s/m^2 D_si D_ps_nm_km * 1e-6; % 色散相位因子 H exp(1j * pi * D_si * lambda^2 * f.^2 * L / 3e8); out ifft(fft(in) .* H); end这里的lambda必须用米作单位也就是1550e-9不能直接写1550。否则量纲一乱算出来的色散量级会差出十几倍。如果你要仿真色散补偿只需要在接收端乘上共轭相位因子exp(-1j * pi * D_si * lambda^2 * f.^2 * L / 3e8)这就是最理想的数字色散补偿。2.3 非线性效应不是功率越大越好光纤非线性会让信号产生强度相关的相位调制也就是自相位调制SPM。信号功率越高非线性相位累积越大星座点会绕着原点旋转、扩散严重时就像“炸开”。描述光脉冲在光纤中传输的方程是非线性薛定谔方程NLSE分步傅里叶法的核心就是在每个小步长里把线性和非线性效应分开处理。步长取0.05km时每一步里面非线性相位可以近似为gamma * |A|^2 * h。gamma在普通单模光纤里约1.3 /W/km但它会随着有效面积变化。仿真时如果你把入纤功率从0dBm加到6dBm非线性相位会明显增加这也是很多人仿真高功率时发现BER不降反升的原因。for z 0:h:total_len-h % 半段衰减 signal signal .* exp(-alpha_linear * h / 2); % 非线性相位 signal signal .* exp(1j * gamma * abs(signal).^2 * h); % 色散 signal ifft(fft(signal) .* H_disp_step); end严格讲分步傅里叶应该在色散算子两边各放半个衰减步长称为对称分步法但步长足够小时简化的误差可以接受。如果你把步长设到1km仿真速度上去了结果却会偏离真实光纤较远。我的经验是步长不要超过色散长度和非线性长度的较小值的四分之一。2.4 PMD、激光线宽与其它非理想因素除了衰减、色散和非线性真实系统还有偏振模色散PMD、激光器相位噪声、频偏、IQ不平衡等。在10G QPSK单信道仿真里PMD影响相对较小但激光线宽不能忽略。激光器不是理想单频存在随机的相位漂移接收端载波相位估计要能跟踪这种低频变化。我在接收端用四次方载波相位估计这是因为QPSK星座点经过四次方之后调制相位会被消除剩下的是4倍载波相位。估计公式是theta -angle(mean(rx_sym.^4)) / 4; rx_phase rx_sym .* exp(-1j * theta);这里有个坑四次方估计存在π/2的相位模糊因为-1j和1j都可能是估计结果。解决方法是发一段已知的训练序列或者用差分编码。工程里更稳的办法是插入导频符号先估计出模糊相位再用判决值持续纠正。我在这个工程里同时提供了导频方案建议你仿真时优先用导频。3. 实操过程与核心环节实现3.1 主脚本和发射端代码Matlab源码我按模块组织主脚本负责把所有东西串起来。先看参数设置这一段clear; clc; % 光通信系统参数 Rb 10e9; % 线路速率 10Gbps M 4; % QPSK Rs Rb / log2(M); % 符号率 5Gbaud sps 16; % 每符号采样数 fs Rs * sps; % 采样率 80GHz Nbits 1e6; % 参与统计的比特数 beta 0.2; % 根升余弦滚降因子 Lg 20; % 滤波器符号长度 % 光纤参数 lambda 1550e-9; % 波长单位m alpha_dB 0.2; % 光纤损耗 dB/km D_ps_nm_km 17; % 色散系数 ps/(nm*km) gamma 1.3; % 非线性系数 /W/km fiber_len 80; % km launch_power_dBm 0; % 入纤功率 dBm OSNR_dB 10; % 目标OSNR dB0.1nm参考带宽发射端关键步骤是比特映射、上采样、脉冲成形。QPSK映射我用Gray编码这样相邻符号只有1bit不同误判时只错一个比特BER统计更合理。data randi([0 1], Nbits, 1); % 每2bit映射成符号 sym_bits reshape(data, 2, []).; sym_idx bi2de(sym_bits, left-msb); const [11j, -11j, -1-1j, 1-1j] / sqrt(2); tx_sym const(sym_idx1).; % 上采样 tx_up upsample(tx_sym, sps); % 根升余弦成形 pulse rcosdesign(beta, Lg, sps, sqrt); tx_baseband conv(tx_up, pulse, same); % 功率调整 tx_baseband tx_baseband * sqrt(10^(launch_power_dBm/10));上采样后信号率从5Gbaud变成80GHz采样率。rcosdesign的滚降因子beta选0.2到0.5之间都可以想要带宽效率高就选0.2想要时域拖尾小就选0.45。成形脉冲的群延迟一定要记下来后面接收端采样要对准。3.2 光纤信道核心函数光纤信道是整个仿真里最影响速度的部分。我用一个函数封装分步傅里叶过程function [out, info] fiber_channel(in, fs, params) % params: lambda, alpha_linear, D_si, gamma, L, h N length(in); f fftshift((0:N-1) * fs / N - fs / 2); alpha params.alpha_dB / 4.343; D_si params.D_ps_nm_km * 1e-6; ... % 色散步长传递函数 H exp(1j * pi * D_si * params.lambda^2 * f.^2 * params.h / 3e8); signal in; for z 0:params.h:params.L-params.h signal signal .* exp(-alpha * params.h / 2); signal signal .* exp(1j * params.gamma * abs(signal).^2 * params.h); signal ifft(fft(signal) .* H); end signal signal .* exp(-alpha * params.h / 2); out signal; end循环里每次都做两次fft如果符号数设得很大计算量会非常可观。实测下来100万bit、sps16、80km、步长0.05km总采样点数约3200万在普通台式机上跑一次大约十几秒。如果觉得慢可以把步长改成0.1km误差在可接受范围内速度快一倍。进一步优化可以用MATLAB的fft2做并行或者直接下调Nbits到20万BER曲线依然有参考价值。3.3 接收端、色散补偿与BER统计接收端先做匹配滤波再按最佳采样时刻抽取。这里最容易出问题的是滤波器群延迟。如果用conv和same输出信号相对输入有一个固定延迟抽样点必须从正确位置开始。% 接收端匹配滤波 rx_filtered conv(rx_signal, fliplr(pulse), same); % 取符号中心点滤波器群延迟为 Lg*sps/2 start_idx Lg * sps / 2 1; rx_sym rx_filtered(start_idx : sps : end); rx_sym rx_sym(1:length(tx_sym)); % 导频辅助相位估计使用前200个符号 pilot_len 200; [~, est_idx] min(abs(rx_sym(1:pilot_len) - const.), [], 2); error_phase angle(const(est_idx).) - angle(rx_sym(1:pilot_len)); theta_est mean(error_phase); rx_sym_phase rx_sym .* exp(1j * theta_est); % 判决 [~, demap_idx] min(abs(rx_sym_phase - const.), [], 2); rx_bits reshape(de2bi(demap_idx-1, 2, left-msb)., [], 1); [~, err_bit] biterr(data(1:length(rx_bits)), rx_bits); BER err_bit / length(rx_bits);这里是理想导频估计实际操作时可以换成四次方估计加上相位模糊纠正。注意quiver等绘图放到BER统计之前先保存星座数据避免重复跑光纤信道。色散补偿我放在匹配滤波前频域乘一个共轭色散因子。因为我的fiber_channel里已经累计了全部传输距离的色散补偿时直接用总长度取负相位即可。补偿后眼图会重新张开星座点从一团“花瓣”恢复成四个清晰的点。4. 不同条件下的性能仿真与分析4.1 背靠背与理论误码曲线对比先做一个不加光纤、只加ASE噪声的背靠背仿真也就是传输距离为0km。这个结果用于验证接收机代码是否正确。QPSK相干解调在AWGN下的理论比特误码率可以近似写成BERQ(sqrt(2 * EbN0))。我仿真时固定OSNR然后换算成EbN0再和理论曲线放在同一张图里。实际仿真出来的BER会略高于理论值原因有三个根升余弦滤波器非理想、相位估计存在残余误差、有限长度的蒙特卡洛统计有波动。如果理论曲线和仿真曲线相差超过1dB大概率是接收机抽样时刻或相位估计出了问题这时候先别调整信道把背靠背调好再说。4.2 传输距离对性能的影响我把默认OSNR设成10dB发射功率0dBm分别跑0km、20km、40km、80km、120km每组都记录补偿前和补偿后的BER。结果整理如下传输距离无色散补偿BER数字色散补偿BER现象说明0km约4e-5约4e-5背靠背基准20km约8e-5约6e-5轻微色散眼图略闭合40km约7e-4约6e-5符号间干扰明显80km约0.03约5e-5补偿前几乎不可用120km约0.09约1e-4残余噪声和非线性累积这个结果很直观单纯靠光纤本征色散限制80km不补偿已经很难稳定工作而加上数字色散补偿后系统性能几乎回到背靠背水平只有一点点OSNR代价。这个“色散代价”来自数字补偿对信号和噪声同时处理窄带噪声会被补偿算法的响应展宽。4.3 入纤功率与非线性折扣接着固定链路为80km并做理想色散补偿但把发射功率从-6dBm逐步调到6dBm看非线性效应如何影响系统。这里我保持ASE噪声固定实际系统中功率变大后OSNR会变好但高功率SPM会吃掉增益。入纤功率补偿后BER主要限制因素-6 dBm约5e-3OSNR太低ASE主导-3 dBm约2e-4OSNR仍偏低0 dBm约4e-5线性与非线性平衡较好3 dBm约7e-5SPM开始显现6 dBm约5e-3非线性相位严重星座图旋转所以实际光纤系统中存在一个最优入纤功率不是越高越好。这个最优值取决于光纤长度、信道数、符号率和调制格式。在单信道QPSK里0dBm附近表现不错如果换成WDM系统XPM和FWM会更早出现最优功率还会下降。4.4 眼图和星座图的直观对比仿真中最打动人心的不是BER数字而是那张星座图。无损伤时星座点是四个小点只有ASE时点云围绕理想位置成高斯扩散只有色散时点云会散成椭圆边缘向相邻星座拉伸SPM严重时整个点云像一个“旋转的风车”四个象限边界模糊。眼图方面色散补偿前眼图中央区域变得浑浊眼高下降眼宽变窄。补偿后眼图重新出现清晰的“眼睛”。用以下命令可以直接画scatterplot(rx_sym_phase); eyediagram(rx_filtered(start_idx:sps:end), 2*sps);建议你在每次改参数后都保留一张星座图汇报项目时放三张对比图背靠背、无补偿、补偿后。这是最有说服力的实验结果。5. 常见问题与排查技巧实录5.1 仿真跑得太慢或直接内存不足这个项目最容易出的问题是内存占用和计算耗时。100万bit在80GHz采样率下就是3200万个复采样点每个复数是16字节光一个信号数组就占500MB以上加上滤波器、fft临时数组很容易超内存。我的建议是先用20万bit跑通全流程确认星座图正常后再加bit数。如果需要统计10的负5次方量级的BER20万bit可能采不到错误这时候可以改用半解析法先估计噪声方差再用误差函数计算理论BER。不要盲目堆蒙特卡洛次数否则跑一次要等很久。如果一定要蒙特卡洛可以分块处理把数据切成10块每块独立过信道最终把误码数累加。这样单块内存占用只有原来的十分之一而且可以用parfor并行。5.2 星座图整体旋转、BER极高最常见的现象是星座点分布正确但整体旋转了一个角度导致判决完全错误。这通常不是光纤问题而是载波相位估计有相位模糊。四次方估计出的theta可能有四个解因为你无法区分真实相位和加了π/2、π、3π/2后的相位。解决思路有两个一是用已知导频符号确定正确角度二是用差分QPSK把信息编码在相邻符号相位差里接收端不需要绝对相位。课程设计推荐导频方案因为物理意义更清晰且便于和真实相干接收机对应。5.3 眼图空洞、采样点抓到过渡带如果BER一开始就卡在0.4左右先看发射端和接收端的群延迟是否对齐。根升余弦滤波器长度是Lg个符号经过conv后信号有Lgsps/2个采样点的延迟。我用start_idx Lgsps/2 1就是处理这个。如果忘了这个偏移采样点正好落在符号过渡区星座图会形成一圈环状BER必然糟糕。另一个常见问题是匹配滤波器和成形滤波器不匹配。发射端用根升余弦接收端匹配滤波也应该用根升余弦两个级联后才等效为升余弦。如果接收端误用普通矩形窗信噪比会损失好几个dB。记住“根号根号等于升余弦”这个口诀。5.4 OSNR和EbN0搞混导致结果对不上很多朋友拿仿真BER去对照理论曲线发现怎么都对不上仔细看发现他把OSNR当EbN0用了。OSNR定义在0.1nm参考带宽而EbN0是用比特持续时间折算的每比特能量和噪声功率谱密度之比。两者换算还牵扯到符号率和参考带宽不是差一个固定3dB。我的办法是Simulink或Matlab里不做复杂的OSNR换算而是在接收端先把信号和噪声分离直接用噪声方差计算EbN0。具体做法是发射端信号能量已知接收端加ASE时记录下噪声功率判决前信噪比就能精确算出来。这样可以绕开GB单位和Hz单位之间的换算坑。6. 再聊几句实操体会这个仿真工程改到最后我对“真实光纤损伤”这几个字有了更深的理解。很多人一听到“真实”就以为要把所有物理效应全部放进仿真实际不是真实建模的要点是抓住当前系统性能的主要矛盾。在单信道10G QPSK里主要矛盾就是色散和ASE其次是SPM。PMD、偏振损耗、IQ失衡这些不是不重要而是它们的作用在10G速率下还没到决定性的程度。我个人的习惯是先跑一条没有任何噪声、只有色散的链路看眼图在哪个距离开始关死。这一步能帮你快速建立“指标直觉”80km不补偿会造成严重的符号间干扰但20km还能撑住。然后再把ASE加进来看OSNR和BER之间有没有理论曲线那么陡。最后才加非线性扫描入纤功率找到最优工作点。如果你想把工程往更高阶扩展下一步可以改成偏振复用QPSK也就是等效20Gbps并加入CMA盲均衡。也可以把光纤信道换成多跨段EDFA级联比较不同放大器噪声指数下的OSNR积累。这个项目里我已经把发射、信道、接收和统计模块都拆开了扩展这些功能基本就是加函数、加参数而不用把主线推倒重来。