diff --git a/.gitignore b/.gitignore index f9d594a..47b2461 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ venv/ .pytest_cache/ .pylint.d/ .DS_Store -results/*.png +# results/*.png results/*.csv results/*.json !results/.gitkeep diff --git a/REPORT.md b/REPORT.md new file mode 100644 index 0000000..0dcf048 --- /dev/null +++ b/REPORT.md @@ -0,0 +1,78 @@ +# 无线通信技术实验报告:信道编码与信道均衡 + +## 1. 实验目的 + +本实验旨在掌握信道编码与信道均衡的基本原理与实现方法,包括: + +- 理解 Hamming(7,4) 线性分组码的编码规则与单比特纠错能力。 +- 实现伴随式计算并完成 Hamming 译码。 +- 理解多径信道导致的符号间干扰(ISI)。 +- 实现迫零(ZF)均衡器和 LMS 自适应均衡器,并观察均衡效果。 +- 练习在 GitHub 环境中运行实验脚本、生成结果图像和通过自动测试。 + +## 2. 实验原理 + +### 2.1 信道编码 + +Hamming(7,4) 是一种线性分组码,将 4 个信息比特映射为 7 个码字比特。编码过程使用生成矩阵 $G$,在 GF(2) 上进行矩阵乘法,生成 3 个校验位。接收端通过伴随式 $s = r H^T$ 计算校验结果,其中 $H$ 为校验矩阵。若伴随式为零,则码字无错误;若非零,则伴随式等于某一列,表示错误位位置,从而可纠正单比特错误。 + +### 2.2 信道均衡 + +在多径传输中,接收信号是发送符号与信道冲激响应的卷积,产生前向和后向 ISI。ZF 均衡器通过求解线性方程,使复合脉冲响应在目标采样点处对齐为单位冲激,从而抑制 ISI。LMS 自适应均衡器通过训练序列迭代更新系数,最小化瞬时误差,实现在未知或变化信道下的自适应补偿。 + +## 3. 实验环境 + +- Python 版本:3.12.12 +- 主要依赖:NumPy、Matplotlib、pytest +- 运行工具:Visual Studio Code、Python 解释器 +- AI 助手使用情况:本实验报告、核心算法实现与调试过程中参考了 AI 助手提供的代码结构建议,并在生成后确认逻辑正确,保证所有关键函数已测试通过。 + +## 4. 实验方法与步骤 + +### 4.1 Part 1:信道编码 + +1. 使用 `hamming74_encode(bits)` 实现 Hamming(7,4) 编码,将 4 比特数据块映射为 7 比特码字。 +2. 使用 `hamming74_syndrome(codewords)` 计算伴随式,验证正确码字的伴随式为零。 +3. 使用 `hamming74_decode(received)` 对接收码字进行单比特纠错:若伴随式非零,则与 $H$ 的列匹配,定位错误位置并翻转错误比特。 +4. 运行 `src/part1_channel_coding.py`,在不同信道错误概率下比较未编码与 Hamming 编码的 BER,并生成 `results/coding_ber_curve.png`。 + +### 4.2 Part 2:信道均衡 + +1. 生成 BPSK 模拟符号序列,并通过多径信道 `multipath_channel` 产生接收信号。 +2. 实现 `estimate_zf_equalizer(channel, num_taps)`,构造卷积矩阵并使用最小二乘法求解 ZF 均衡器系数。 +3. 实现 `apply_fir_filter(signal, taps)`,对接收信号进行 FIR 滤波。 +4. 实现 `lms_equalizer(rx_train, tx_train, num_taps, step_size)`,使用训练序列迭代更新均衡器参数,记录每次误差。 +5. 运行 `src/part2_equalization.py`,生成 `results/equalization_eye_comparison.png` 和 `results/equalization_mse_curve.png`,并比较均衡前后 BER。 + +## 5. 实验结果 + +### 图像结果 + +![编码BER曲线](results/coding_ber_curve.png) + +![均衡眼图对比](results/equalization_eye_comparison.png) + +![LMS误差曲线](results/equalization_mse_curve.png) + +### 运行结果 + +- Part 1: 成功生成 BER 曲线。 +- Part 2: 均衡前 BER = 0.0010,LMS 均衡后 BER = 0.0000。 +- 自动测试结果:`16 passed`。 + +## 6. 结果分析与讨论 + +1. Hamming(7,4) 能纠正单比特错误,因为每个单比特错误导致的伴随式不同,对应于校验矩阵 $H$ 的唯一列,从而可定位单比特错误位置。 +2. 信道编码引入冗余比特(从 4 比特增加到 7 比特),降低了有效码率,但通过增加冗余提高了对错误的检测与纠正能力,提升了可靠性。 +3. ZF 均衡器在抑制 ISI 时会放大某些频率成分,因此在存在噪声时可能引入噪声增强现象,尤其是在信道频率响应接近零点附近。 +4. LMS 步长过大会导致收敛不稳定、误差波动较大;步长过小则收敛速度慢,训练时间变长。本实验中选择 `step_size=0.01` 达到较稳定的收敛效果。 +5. 均衡前后 ISI 明显减弱。原始多径接收信号存在严重符号混叠,经过 LMS 均衡后输出波形更接近原始 BPSK 符号,误差曲线逐步下降。 + +## 7. 实验心得 + +本次实验加深了对 Hamming 码与均衡器原理的理解。实际编程过程中,注意矩阵运算的 GF(2) 处理与 FIR 卷积边界对齐非常关键。AI 助手在实现思路和调试上提供了参考,但最终代码已根据测试结果进行了验证,确保可运行且符合实验要求。 + +## 8. 参考资料 + +- 课程课件:第6章 信道编码 +- 课程课件:第7章 均衡 diff --git a/results/coding_ber_curve.png b/results/coding_ber_curve.png new file mode 100644 index 0000000..99ccf2e Binary files /dev/null and b/results/coding_ber_curve.png differ diff --git a/results/equalization_eye_comparison.png b/results/equalization_eye_comparison.png new file mode 100644 index 0000000..b6f72b8 Binary files /dev/null and b/results/equalization_eye_comparison.png differ diff --git a/results/equalization_mse_curve.png b/results/equalization_mse_curve.png new file mode 100644 index 0000000..57bcc8d Binary files /dev/null and b/results/equalization_mse_curve.png differ diff --git a/src/part1_channel_coding.py b/src/part1_channel_coding.py index 1ecf55e..32160d4 100644 --- a/src/part1_channel_coding.py +++ b/src/part1_channel_coding.py @@ -48,8 +48,9 @@ def hamming74_encode(bits): if not np.all((bits == 0) | (bits == 1)): raise ValueError('bits 只能包含 0 或 1') - # TODO: 将 bits reshape 为 (-1, 4),再与 HAMMING_G 相乘并对 2 取模。 - raise NotImplementedError('请实现 Hamming(7,4) 编码') + blocks = bits.reshape(-1, 4) + encoded = np.mod(blocks.dot(HAMMING_G), 2).astype(int) + return encoded.reshape(-1) def hamming74_syndrome(codewords): @@ -70,8 +71,8 @@ def hamming74_syndrome(codewords): if codewords.shape[1] != 7: raise ValueError('每个 Hamming(7,4) 码字长度必须为 7') - # TODO: 计算 s = r H^T mod 2。 - raise NotImplementedError('请实现伴随式计算') + syndromes = np.mod(codewords.dot(HAMMING_H.T), 2).astype(int) + return syndromes def hamming74_decode(received): @@ -94,8 +95,17 @@ def hamming74_decode(received): if received.ndim != 1 or len(received) % 7 != 0: raise ValueError('received 必须是一维数组,长度为 7 的倍数') - # TODO: 使用 hamming74_syndrome 完成单比特纠错,并返回前 4 个信息位。 - raise NotImplementedError('请实现 Hamming(7,4) 译码') + codewords = received.reshape(-1, 7) + corrected = codewords.copy() + syndromes = hamming74_syndrome(codewords) + h_columns = HAMMING_H.T + for idx, syndrome in enumerate(syndromes): + if np.any(syndrome): + matches = np.all(h_columns == syndrome, axis=1) + if np.any(matches): + error_pos = int(np.argmax(matches)) + corrected[idx, error_pos] ^= 1 + return corrected[:, :4].reshape(-1) def convolutional_encode(bits): diff --git a/src/part2_equalization.py b/src/part2_equalization.py index 8cbf1d8..1d923a6 100644 --- a/src/part2_equalization.py +++ b/src/part2_equalization.py @@ -38,8 +38,19 @@ def estimate_zf_equalizer(channel, num_taps): if num_taps < 1: raise ValueError('num_taps 必须为正整数') - # TODO: 构造卷积矩阵并求解 ZF 均衡器抽头。 - raise NotImplementedError('请实现 ZF 均衡器估计') + channel_len = len(channel) + num_rows = channel_len + num_taps - 1 + matrix = np.zeros((num_rows, num_taps), dtype=float) + for row in range(num_rows): + for col in range(num_taps): + idx = row - col + if 0 <= idx < channel_len: + matrix[row, col] = channel[idx] + desired = np.zeros(num_rows, dtype=float) + center = num_taps // 2 + desired[center] = 1.0 + taps, *_ = np.linalg.lstsq(matrix, desired, rcond=None) + return taps def apply_fir_filter(signal, taps): @@ -58,8 +69,8 @@ def apply_fir_filter(signal, taps): if signal.ndim != 1 or taps.ndim != 1: raise ValueError('signal 和 taps 必须是一维数组') - # TODO: 使用 np.convolve,并截取与 signal 等长的输出。 - raise NotImplementedError('请实现 FIR 滤波') + filtered = np.convolve(signal, taps, mode='full')[: len(signal)] + return filtered def lms_equalizer(rx_train, tx_train, num_taps, step_size=0.01): @@ -89,8 +100,21 @@ def lms_equalizer(rx_train, tx_train, num_taps, step_size=0.01): if num_taps < 1: raise ValueError('num_taps 必须为正整数') - # TODO: 实现 LMS 自适应均衡训练。 - raise NotImplementedError('请实现 LMS 均衡器') + rx_train = np.asarray(rx_train, dtype=float) + tx_train = np.asarray(tx_train, dtype=float) + num_samples = len(rx_train) + padded_rx = np.concatenate([np.zeros(num_taps - 1, dtype=float), rx_train]) + taps = np.zeros(num_taps, dtype=float) + center = num_taps // 2 + taps[center] = 1.0 + errors = [] + for n in range(num_samples): + window = padded_rx[n:n + num_taps][::-1] + y = np.dot(taps, window) + e = tx_train[n] - y + errors.append(e) + taps = taps + step_size * e * window + return taps, np.asarray(errors, dtype=float) def run_equalization_demo():