☰
2FSK调制解调课程设计:建模、解调与误码率曲线对齐
2026/10/31 16:29:43 网站建设 项目流程

简介:面向通信原理课程设计与MATLAB仿真实操场景,这份文档围绕2FSK数字调制解调系统展开,适合需要完成课程设计、理解频移键控链路或复现仿真结果的学生参考。压缩包共1个doc文件,约579KB,内容以课程设计报告形式组织,涵盖设计任务与要求、开关法产生2FSK信号、加性高斯白噪声信道、相干解调方案以及MATLAB7.0源程序与仿真波形。读者可从中获取从信源产生、调制、信道传输到解调判决的完整设计思路,并参考误码率与信噪比曲线、眼图等性能分析方式,用于搭建自己的仿真模型、撰写报告或排查参数设置问题。目前已有855人学习,适合作为2FSK调制解调课程设计的对照材料。

1. 2FSK 调制与解调课程设计到底交付什么:波形好看但误码率对不上,问题出在哪

实验室里最常见的场面是:2FSK 调制与解调的时域波形已经画得很漂亮,两段频率清清楚楚,结果一加噪声、一统计误码率,曲线要么和理论差一个数量级,要么在高信噪比下平躺不动。通信系统原理课程设计真正要交付的从来不是一张波形图,而是三件事同时成立:调制器每个参数都能说出为什么这么取,解调器每个判决点都能在代码里指出来,仿真出来的误码率曲线能和理论值咬合。这篇按“建模—解调—整链跑通—排错—对曲线”的顺序走一遍,适合正在做 2FSK 仿真课设的人,也适合想在做硬件之前先用仿真摸清链路边界的人。

2. 2FSK 调制端怎么建模:两种生成方式决定了后面的频谱和带宽

调制端看着简单——比特 1 送一个频率,比特 0 送另一个频率——但这一步选错模型,后面频谱、带宽、滤波器参数全跟着错。我见过太多人上来就写cos(2*pi*f*t),跑出来的频谱旁瓣压不下去,然后花两天怀疑是自己的 FFT 写错了。所以这一章先把“2FSK 到底有几种”讲清楚,再落到参数和代码。

2.1 相位连续与相位不连续:同一个名字下的两套系统

从硬件实现看,2FSK 有两条完全不同的路。第一条是两路独立振荡器加一个模拟开关(或者数字 IO 切换),比特为 1 时接通 f1 那一路,为 0 时接通 f2 那一路。这种做法的优点是直观、器件少,代价是切换瞬间两路振荡器的相位毫无关系,波形在比特边界会出现台阶式跳变,这就是相位不连续 2FSK。第二条路是只用一个压控振荡器,用比特流去改分频比或者控制电压,频率切换时相位是接着走的,也就是连续相位 FSK,MSK 就是它的一个特例(调制指数 0.5)。

两者的差别最直接地体现在频谱上。相位不连续等价于给一段正弦乘了个矩形包络再切换,比特边沿的能量是宽带的,旁瓣滚降慢,工程上说大约按 1/f² 衰减;连续相位的边沿被“抹平”了,旁瓣掉得快得多。课程设计里如果题目要求画频谱图并解释占用带宽,你选的模型直接决定结论对不对——用不连续模型算出来的带宽,拿去设计带通滤波器,通常会把滤波器带宽开得过宽,噪声进来得多,误码率曲线整体左移,看起来“性能变好了”,其实是自欺欺人。

在仿真里区分这两种模型只需要一行代码的差别:不连续用全局时间轴cos(2*pi*freq*t),连续用相位累加cos(2*pi*cumsum(freq)/fs)。后面给的调制函数会把两条路都实现出来,方便你直接对比。

2.2 f1、f2 和 Rb 怎么定:调制指数 h 与正交条件

比特周期 Tb = 1/Rb,频偏定义为 Δf = |f1 − f2| / 2,调制指数 h = 2Δf·Tb = |f1 − f2| / Rb。这个 h 是整篇文章里最重要的一个数,它同时决定了频谱宽度和两路信号能不能“分开”。

参数符号课程设计常用取值定它的依据
比特率Rb1000 bit/s决定 Tb,也决定积分清零窗口长度
载波频率 1f11000 Hz取 Rb 的整数倍,且 f1·Tb 为整数
载波频率 2f22000 Hz同上,且与 f1 的间隔满足正交条件
调制指数h1.0h 为整数时相干与非相干都正交
采样率fs100 kHz至少 2×(f2 + Rb),工程上取 20~50 倍 Rb
每比特点数spbfs/Rb = 100必须是整数,否则判决窗口会逐比特漂移

正交条件要分两种解调方式看。相干解调要求 |f1 − f2| = n/(2Tb),也就是 h = n/2;非相干(包络检波)要求 |f1 − f2| = n/Tb,也就是 h = n(n 为正整数)。取 h = 1 两边都满足,是最省事也最稳妥的选法,上面表里 f1 = 1000、f2 = 2000、Rb = 1000 就是 h = 1。取 h = 0.5 那一组(比如 f1 = 1000、f2 = 1500)只有相干解调成立,非相干解调会因为两路包络互相串扰而白白损失性能,这个坑在第 5 章还会再提一次。

带宽估算用卡森公式:B ≈ 2(Δf + Rb) = |f1 − f2| + 2Rb。按表里的取值就是 1000 + 2000 = 3000 Hz。这个数直接拿去做接收端带通滤波器的通带宽度,比凭感觉取“大概 5 kHz”靠谱得多。

还有一条容易被忽略的:f1·Tb 和 f2·Tb 最好是整数。原因在解调端——积分清零是在一个比特周期上做平均,如果载波在 Tb 内不是整数个周期,cos²的平均值就不严格等于 0.5,两路积分输出会带一个固定偏置,判决门限跟着偏,高信噪比下误码率就下不去。这个现象很隐蔽,因为波形看上去完全正常。

2.3 调制端的 Python 最小实现

import numpy as np def fsk_mod(bits, Rb, f1, f2, fs, phase_cont=False, A=1.0): """ 2FSK 调制 bits : 0/1 序列,np.ndarray Rb : 比特率 bit/s f1, f2 : 比特 1 / 比特 0 对应的载波频率 fs : 采样率 phase_cont : False=相位不连续(独立振荡器切换), True=连续相位(CPFSK) """ bits = np.asarray(bits) spb = int(round(fs / Rb)) # 每比特采样点数,必须整除 assert abs(spb - fs / Rb) < 1e-9, "fs 必须能被 Rb 整除" n = len(bits) * spb t = np.arange(n) / fs # 全局时间轴 # 每个采样点对应的瞬时频率:比特为 1 取 f1,为 0 取 f2 freq = np.repeat(np.where(bits == 1, f1, f2), spb) if phase_cont: # 相位累加:频率变化时相位接着走 phase = 2 * np.pi * np.cumsum(freq) / fs s = A * np.cos(phase) else: # 直接用瞬时频率乘全局时间:比特边界处相位跳变 s = A * np.cos(2 * np.pi * freq * t) return s

这段代码的核心是freq这个数组——它先把比特序列“拉长”成逐采样点的频率序列,再交给两种相位模型之一。np.where(bits == 1, f1, f2)完成比特到频率的映射,np.repeat(..., spb)把每个比特的频率复制 spb 份,这样频率和采样点就是一一对应的。

参数上有三个点必须卡死。第一,spb一定要断言整除,fs=100000、Rb=1000时 spb = 100 没问题,但如果有人把 Rb 改成 3000,spb = 33.33,取整成 33 之后每比特实际时长变成 0.33 ms,几十个比特之后判决窗口就漂到隔壁去了,这是后面第 5 章“高信噪比下误码率平躺”的头号原因。第二,A默认 1,因为后面噪声功率是按 A² 推的,改了 A 不改噪声公式,Eb/N0 就整体错。第三,phase_cont的选择要和你的报告口径一致——如果正文写“模拟两路独立振荡器”,仿真里就必须用 False,别一边说独立振荡器一边拿连续相位的漂亮频谱去交差。

2.4 用频谱和瞬时频率回头验证参数

调制完别急着往下走,先看一眼频谱。判断标准很简单:应该在 f1 和 f2 附近各看到一个主瓣,主瓣宽度量级是 2Rb,两个主瓣之间不重叠(h ≥ 1 时)。

import numpy as np def check_spectrum(s, fs, f_lo=0, f_hi=5000): """加汉宁窗看频谱,避免矩形窗的泄漏把旁瓣盖住""" N = len(s) win = np.hanning(N) S = np.abs(np.fft.rfft(s * win)) / (N * 0.5) # 归一化到单频幅度 f = np.fft.rfftfreq(N, 1 / fs) mask = (f >= f_lo) & (f <= f_hi) return f[mask], 20 * np.log10(S[mask] + 1e-12) # 用法 # f, mag_db = check_spectrum(s, fs) # 在两个主瓣峰值处应读到约 -6 dB(汉宁窗的相干增益损失)

这里有两个细节值得说明。加窗是必须的,矩形窗的 sinc 泄漏会把 -40 dB 以下的旁瓣完全糊住,你就分不清“旁瓣高”是信号本身的问题还是窗的问题。归一化除以N*0.5是因为实信号 FFT 的单边谱幅度要乘 2,而汉宁窗的相干增益是 0.5,两者抵消后单频正弦的峰值应该落在 0 dB 附近,实测读到 -6 dB 左右是正常的(窗函数能量损失)。

再补一个更直观的检查:把freq数组直接画出来,看它是不是干净的方波状跳变,跳变点是不是严格落在 spb 的整数倍上。这一步比看频谱还快,能立刻抓出比特对齐错位的问题。

3. 解调端两条路线怎么选:相干、非相干和判决门限

调制端决定了信号长什么样,解调端决定了你能不能把它拿回来。课程设计里通常要求两种都做一遍做对比,但如果时间只够做一种,得先知道自己在放弃什么。这一章把相干和非相干的实现、代价、以及判决门限这个最容易翻车的地方讲清楚。

3.1 相干解调:两路乘法器加积分清零,为什么能省一半功率

相干解调的结构是:接收信号分别乘以本地恢复的 cos(2πf1t) 和 cos(2πf2t),两路各自过一个低通(或者直接在比特周期上积分),然后比较两路输出的相对大小做判决。数学上,乘以本振之后,与本地频率相同的那一路会产生直流分量 A/2·cos(Δθ) 和一个二倍频分量,二倍频被积分器吃掉,剩下直流;与本地频率不同的那一路,因为正交条件,一个比特周期上的积分近似为零。

所以判决规则就是“哪一路积分输出大就判哪个”。理想相干解调的理论误码率是 Pb = Q(√(Eb/N0)),非相干是 Pb = (1/2)·exp(−Eb/(2N0))。同样的 Eb/N0 下,相干大约比非相干好 1~3 dB(Eb/N0 较大时接近 3 dB,低信噪比时差距缩小)。这 3 dB 就是“本地载波必须严格同频同相”换来的——代价是接收端要做载波恢复,相位误差直接变成性能损失。

仿真里如果发射端和接收端用的是同一个np.cos(2*pi*f1*t),那等于相位误差恒为零,这是“理想相干”,报告里必须写清楚。想更贴近实际,可以给本地载波加一个随机初相 θ,看看 θ 从 0 扫到 π 时误码率怎么变——这是很好的加分项。

3.2 非相干解调:包络检波和过零检测各自适用什么场景

非相干解调不需要恢复载波相位,常见实现有两种。第一种是包络检波:先用两个带通滤波器分别滤出 f1 和 f2 附近的成分,各自整流加低通取出包络,再比较两个包络的大小。第二种是过零检测:把信号限幅成方波,数单位时间内的过零点数,频率高的一路过零密集,据此判决。

课程设计里我更推荐用正交下变频的方式实现“包络检波”,因为它和相干解调的代码结构几乎一样,好对比也好解释:把接收信号分别乘以 cos 和 −sin 得到 I、Q 两路,各自积分,然后取hypot(I, Q)。这个模值对载波初相不敏感,这就是“非相干”三个字的实际含义。过零检测虽然电路上简单,但在仿真里对噪声极其敏感——噪声一大,过零点就被打乱,低信噪比下性能会比理论差很多,不推荐作为主方案。

非相干的最大价值是工程可实现性:接收端不需要载波恢复环路,成本低、抗相位抖动。在短包、低成本场景里它是默认选择。做课设时如果题目只说“设计 2FSK 解调器”没说必须相干,两种都实现然后画一张对比曲线,是最完整的交付。

3.3 判决门限:固定门限什么时候会翻车

相干解调的判决是“比大小”,天生不需要门限,这是它的一大优势。非相干如果比较的是两路包络,同样是比较大小。真正需要门限的是“单路检测”结构——只用一路滤波器,输出大于门限判 1,小于判 0。这种结构省一半硬件,但门限怎么定是个问题。

固定门限在理想 AWGN 信道、信号幅度恒定的情况下可以用,理论上最优门限在两路包络均值的中点。但只要出现下面任意一种情况,固定门限就开始翻车:信号经过衰落信道幅度起伏、收发端有 AGC 误差、滤波器有直流偏置、或者前后比特的拖尾没清干净。这时门限应该改成自适应——常见做法是用一个长的滑动平均估计“1”和“0”的包络均值,取中点;或者用判决反馈,把已经判过的比特拿回来更新门限。

课设里有个很容易踩的坑:用整段信号的平均值当门限。这相当于提前看了答案,仿真出来的误码率会明显优于理论值,答辩时被问一句“实际接收机怎么知道这个均值”就答不上来。正确做法是门限只由当前比特之前的样点估计,或者干脆用比较两路的结构绕开这个问题。

3.4 解调端的 Python 实现与逐比特判决

import numpy as np def fsk_demod_coherent(r, Rb, f1, f2, fs): """相干解调:两路正交下变频 + 比特周期积分 + 比较判决""" spb = int(round(fs / Rb)) nb = len(r) // spb t = np.arange(len(r)) / fs # 本地载波(理想同相,实际应带相位恢复误差) lo1 = np.cos(2 * np.pi * f1 * t) lo2 = np.cos(2 * np.pi * f2 * t) # 积分清零:按比特reshape后求均值,比卷积更干净也不会引入延迟 y1 = (r * lo1)[:nb * spb].reshape(nb, spb).mean(axis=1) y2 = (r * lo2)[:nb * spb].reshape(nb, spb).mean(axis=1) return (y1 > y2).astype(int) # y1 大判 1,否则判 0 def fsk_demod_noncoh(r, Rb, f1, f2, fs): """非相干解调:正交下变频取模 + 比较两路包络""" spb = int(round(fs / Rb)) nb = len(r) // spb t = np.arange(len(r)) / fs def envelope(fc): i = (r * np.cos(2 * np.pi * fc * t))[:nb * spb].reshape(nb, spb).mean(axis=1) q = (-r * np.sin(2 * np.pi * fc * t))[:nb * spb].reshape(nb, spb).mean(axis=1) return np.hypot(i, q) # 模值,与载波初相无关 return (envelope(f1) > envelope(f2)).astype(int)

两个函数的结构完全对称,方便逐行对照。reshape(nb, spb).mean(axis=1)这一句同时完成了低通滤波和抽样两件事——在矩形脉冲、AWGN 的假设下,积分清零就是匹配滤波器,比“先设计一个巴特沃斯低通再抽样”更优也更省事。这也是为什么第 4 章会建议仿真阶段先用积分清零,把滤波器留到需要验证群时延的时候再加。

参数说明几个关键点。spb用round而不是int,配合断言可以尽早暴露 fs 不能整除 Rb 的问题;nb = len(r) // spb做了向下取整,末尾不足一比特的样点直接丢弃,避免 reshape 报错;非相干里 Q 路取负号是因为cos(2πfct)和sin(2πfct)构成正交基时,下变频到基带后 Q 路对应的是−sin,符号写错的话hypot结果不变(因为取模),但如果你中间要单独看 I、Q 波形就会觉得“怎么反了”。

判决输出是 0/1 整数数组,和原始比特序列等长(去掉尾部不足一比特的部分),直接相减取非零个数就是错误比特数。

4. 把整条链路跑起来:采样率、噪声功率、滤波器三件事

到这一步,调制器和解调器都有了,中间差一条信道和一套统计。这一章把链路串起来,重点是两个最容易算错的地方:采样率和噪声功率的对应关系。

4.1 采样率 fs 与每比特点数 spb:混叠和分辨率是一对矛盾

fs 的下限由采样定理给出:要无混叠地表示中心频率 f2、带宽约 2Rb 的带通信号,采样率至少要 2×(f2 + Rb)。按 f2 = 2000、Rb = 1000 算,下限是 6000 Hz。但实际仿真里没人取 6000,通常取 20~50 倍 Rb,也就是 20~50 kHz,我一般直接取 100 kHz。

为什么取这么高?因为积分清零的精度和 spb 有关。spb = 10 的时候,一个比特只有 10 个样点,积分出来的直流分量抖动很大,等效于引入了一份额外的量化噪声,误码率曲线在高信噪比段会翘起来。spb = 100 时这个影响基本可以忽略。但 fs 也不能无限加大——1e6 个比特、spb = 1000,就是 1e9 个采样点,内存直接爆。经验值是 spb 取 64~256 之间,既平滑又不至于跑不动。

还有一个隐性约束:fs 必须是 Rb 的整数倍。不整除的话 spb 取整后每比特实际时长和 1/Rb 有偏差,误差会累积,几千个比特后判决窗口就偏了一整个比特,误码率直接跳到 0.5。

4.2 噪声功率怎么加才和 Eb/N0 对得上

这是整个仿真里最容易错、错了还最难发现的一步。仿真里加的噪声是实带通白噪声,双边功率谱密度 N0/2,采样率 fs 下的噪声方差 σ² = (N0/2)·fs,所以 N0 = 2σ²/fs。信号功率是 A²/2,每比特能量 Eb = (A²/2)·Tb。于是:

Eb/N0 = (A²/2)·Tb / (2σ²/fs) = A²·Tb·fs / (4σ²)

反解出加噪需要的标准差:σ = A·√(Tb·fs / (4·(Eb/N0)))。这个式子里的 fs 和 Tb 都以“采样点”和“秒”为单位,量纲上是自洽的,可以直接用。

如果偷懒直接按 SNR 定义算 σ²,就会掉进第 5 章那个“曲线整体平移 3 dB 甚至 17 dB”的坑里——因为 SNR 里的噪声带宽是接收机带宽,而仿真里噪声铺满了整个 fs,两者差着10log10(fs/(2B))这个量级。

4.3 完整链路代码:调制 → AWGN → 解调 → 误码统计

import numpy as np from scipy.special import erfc def add_awgn(s, ebn0_db, Rb, fs, A=1.0, rng=None): """按 Eb/N0 加实带通高斯白噪声""" rng = rng or np.random.default_rng() ebn0 = 10 ** (ebn0_db / 10) Tb = 1 / Rb sigma2 = A ** 2 * Tb * fs / (4 * ebn0) # 由 Eb/N0 = A^2*Tb*fs/(4*sigma^2) 反解 return s + rng.normal(0, np.sqrt(sigma2), len(s)) def qfunc(x): return 0.5 * erfc(x / np.sqrt(2)) def ber_sim(ebn0_list, nbits, Rb=1000, f1=1000, f2=2000, fs=100000, coherent=True, phase_cont=False, seed=0): rng = np.random.default_rng(seed) bits = rng.integers(0, 2, nbits) # 一次生成,全 SNR 点复用,保证可比 s = fsk_mod(bits, Rb, f1, f2, fs, phase_cont=phase_cont) demod = fsk_demod_coherent if coherent else fsk_demod_noncoh ber = [] for db in ebn0_list: r = add_awgn(s, db, Rb, fs, rng=rng) est = demod(r, Rb, f1, f2, fs) ber.append(np.mean(est != bits[:len(est)])) return np.array(ber) def ber_theory(ebn0_list, coherent=True): ebn0 = 10 ** (np.array(ebn0_list) / 10) if coherent: return qfunc(np.sqrt(ebn0)) # 相干正交 2FSK return 0.5 * np.exp(-ebn0 / 2) # 非相干正交 2FSK

逻辑上有三处刻意的设计。bits在所有 SNR 点之间复用,而不是每个点重新生成——这样曲线上的点之间可比,也避免“某个点运气好”带来的抖动;如果你要评估统计波动,应该改 seed 多次跑取均值,而不是换比特序列。噪声用同一个rng连续生成,保证不同 SNR 点之间不是完全独立的随机流,但这不影响统计特性。est != bits[:len(est)]里的切片是为了兼容尾部丢弃,不加这行遇到长度不等会直接报错。

参数上,ebn0_list建议从 0 取到 12 dB,步进 2 dB,覆盖“完全不可用”到“接近无误码”的完整区间,画出来的曲线才有形状。nbits在 1e4 量级时能撑到 BER ≈ 1e-3,再往下就得加比特数,下一章会讲怎么判断够不够。

4.4 滤波器阶数与群时延:仿真里为什么常先用积分清零

真实接收机一定有带通滤波器和低通滤波器,仿真里要不要加?我的建议是分两步:先用积分清零跑通全链路、对齐理论曲线,确认调制解调逻辑没问题;再插入滤波器,看性能掉多少。反过来做的话,一旦曲线不对,你分不清是逻辑错还是滤波器错。

真要加的话,带通用巴特沃斯,通带按卡森公式取 |f1−f2| + 2Rb,阶数 4~6 阶够了。阶数太低阻带抑制不够,邻道噪声进来;阶数太高群时延变大,而且群时延在通带边缘剧烈变化,会让比特边沿的样点被“拖”过去,等效于定时偏移。4 阶巴特沃斯在通带边缘的群时延变化还算平缓,6 阶以上就得做时延补偿了。

补偿的方法很简单:数字滤波器是因果的,输出相对输入有固定延迟,用scipy.signal.lfilter时可以查d = (N-1)//2(N 为阶数)估算群时延采样点数,判决前把积分窗口整体平移 d 个点即可。这个“平移多少”没有解析公式,实际都是扫描出来的,第 6 章会给扫描代码。

4.5 Python 链路与 Simulink 模型的对应关系

很多人课设要求用 Simulink 交模型,但调试时用 Python 更快。两者一一对应着搭,改一处对一处,效率最高。

链路环节Python 实现Simulink 对应模块参数对齐要点
信源rng.integers(0,2,nbits)Bernoulli Binary Generator采样时间设 1/Rb
2FSK 调制fsk_mod两个正弦源 + Switch,或 M-FSK Modulator Baseband频率间隔与 h 一致
信道add_awgnAWGN Channel模式选 Eb/N0 而非 SNR
相干解调fsk_demod_coherent乘法器 + Analog Filter Design + Zero-Order Hold积分时间 = Tb
非相干解调fsk_demod_noncoh带通 + 包络检波 + 比较器两路滤波器增益要一致
误码统计est != bitsError Rate Calculation收发端都要加延迟对齐

表里最后一列的“延迟对齐”是 Simulink 里翻车率最高的一项:调制、滤波、解调每一级都有延迟,Error Rate Calculation 模块的 Receive Delay 参数如果不填对,统计出来的误码率会是 0.5,看起来像“完全解调失败”,其实只是没对齐。用 Python 先跑通再照着搭,能省掉大量在示波器上找延迟的时间。

5. 2FSK 仿真避坑清单:五个让误码率曲线跑偏的典型问题

这一章全是血泪经验。下面五条按“现象 → 原因 → 解决”写,每条都对应一个能立刻验证的动作,不是泛泛而谈。

5.1 低信噪比段误码率反而比理论曲线还好

现象:Eb/N0 = 0 dB 附近算出来的 BER 比理论值低将近一个数量级,曲线在低端“翘起来”。

原因:最常见的是门限用了整段信号统计出来的均值,等于提前看了答案。其次是噪声按“每比特单独加一次”而不是“整段加一次”,导致相邻比特的噪声不独立,等效于降低了噪声的有效维度。还有一种隐蔽情况:add_awgn里的 A 和fsk_mod里的 A 不一致,信号功率被低估,实际 Eb/N0 比标称值高。

解决:门限只允许用当前比特及其之前的样点估计;噪声一次性加到整段信号上;A 定义成模块级常量,调制和加噪引用同一个变量。验证方法是把 nbits 从 1e3 加到 1e5,如果低 SNR 段的结果随比特数明显变化,说明统计还没收敛,别急着下结论。

5.2 频谱旁瓣压不下去,带外泄漏明显

现象:FFT 图上主瓣两侧 −20 dB 之后衰减很慢,包络接近 1/f 而不是 1/f²。

原因:用了相位不连续模型(cos(2πf·t)直接乘全局时间),比特边界处相位跳变,等价于给正弦乘了矩形包络,边沿产生宽带冲激。这不是代码错,是模型本身的性质。

解决:如果报告里写的是“模拟压控振荡器”,就换成cumsum的连续相位模型;如果写的是“两路独立振荡器切换”,就保留这个结果,但要在报告里明确指出旁瓣是由相位跳变引起的,并且不要把这条谱线拿去估算占用带宽。两种情况都别用矩形窗看频谱,换成汉宁窗再判断。

5.3 高信噪比时误码率卡在 1e-2 不再下降

现象:Eb/N0 超过 8 dB 后 BER 平躺在 1e-2 附近,继续加 SNR 完全不动。这是最典型的“仿真地板”。

原因:判决窗口和比特边界没对齐。可能来自三处——fs 不能整除 Rb 导致 spb 取整后逐比特漂移;加了滤波器但没做群时延补偿;接收信号做过截断或补零,起点偏移了几个样点。

解决:先打印fs/Rb看是不是整数;如果加了滤波器,用第 6 章的窗口扫描代码找最佳平移量;检查len(r) // spb之前有没有做过np.convolve(..., 'same')这类会引入中心偏移的操作。判断依据很直接:如果 BER 随 SNR 完全不降,一定是结构性问题,不是噪声问题,别再去调噪声功率了。

5.4 整条曲线整体平移 3 dB 以上

现象:曲线形状和理论完全一致,就是横坐标整体左移或右移。

原因:把 Eb/N0 和 SNR 混用了。仿真里噪声铺满整个采样带宽 fs,而 SNR

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询