Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions REPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# 无线通信技术实验报告:信道编码与信道均衡

## 1. 实验目的

掌握 Hamming(7,4) 信道编码与单比特纠错的基本方法;理解多径信道导致的 ISI 以及 ZF/LMS 均衡器的设计与效果评估;熟悉使用 GitHub 自动评分与本地脚本进行实验结果验证,并能结合结果图进行解释与分析。

## 2. 实验原理

### 2.1 信道编码

Hamming(7,4) 是一种线性分组码,将 4 比特信息映射为 7 比特码字,通过冗余校验实现纠错能力。伴随式由接收码字与校验矩阵 $H$ 相乘得到,非零伴随式对应某一列,指示错误位置,从而实现单比特纠错。编码增益指在相同误码率下,编码系统相对未编码系统所需信噪比降低的程度。由于引入冗余,码率从 $4/7$ 降低,但可靠性提高,适合噪声较强的传输场景。

### 2.2 信道均衡

多径信道的冲激响应会在符号间产生叠加,导致符号间干扰(ISI)。ZF 均衡器通过设计 FIR 抽头使等效信道尽量逼近冲激响应,从而抵消 ISI,但在信道频谱深衰落处会放大噪声。LMS 自适应均衡器利用训练序列迭代更新抽头,使均衡输出逐步逼近期望符号,实现对未知或时变信道的跟踪。LMS 的收敛速度与稳态误差受步长影响,需要在稳定性与收敛速度之间权衡。

## 3. 实验环境

- Python 版本:3.x(Windows)
- 主要依赖:NumPy、Matplotlib、pytest
- AI 助手使用情况:使用 GitHub Copilot(GPT-5.2-Codex)协助理解实验要求与补全代码,但对关键算法与结果进行了自行验证与解释

## 4. 实验方法与步骤

### 4.1 Part 1:信道编码

1. 生成随机比特序列,并按 4 比特分组。
2. 使用 Hamming(7,4) 生成矩阵完成编码得到 7 比特码字。
3. 通过二元对称信道以不同翻转概率传输,得到带噪码字。
4. 计算伴随式并进行单比特纠错译码,取系统码前 4 位恢复信息比特。
5. 统计未编码与编码后的 BER,并绘制对比曲线,用于观察编码增益。

### 4.2 Part 2:信道均衡

1. 生成 BPSK 符号序列并通过多径 FIR 信道,加入噪声得到接收序列。
2. 使用最小二乘法估计 ZF 均衡器抽头作为参考。
3. 采用 LMS 算法,利用训练序列迭代更新均衡器抽头。
4. 通过均衡输出进行硬判决,计算均衡前后 BER,并绘制均衡波形与 LMS 误差曲线,观察 ISI 抑制效果与训练收敛过程。

## 5. 实验结果

插入结果图:

![编码BER曲线](./results/coding_ber_curve.png)
![均衡眼图对比](./results/equalization_eye_comparison.png)
![LMS误差曲线](./results/equalization_mse_curve.png)
Comment on lines +42 to +46

从 BER 曲线可见,Hamming(7,4) 编码在中低误码概率区域具有明显性能优势。均衡波形对比显示多径引起的畸变被 LMS 均衡器有效抑制,LMS 误差曲线随迭代下降,体现出自适应学习过程。

## 6. 结果分析与讨论

1. Hamming(7,4) 为什么能纠正单比特错误?
答:Hamming(7,4) 码的校验矩阵 $H$ 的每一列唯一对应一个比特位置,单比特错误会产生非零伴随式,且其值与某一列一致,因此可以定位并翻转该位完成纠错。

2. 为什么信道编码会引入冗余并降低码率?
答:编码通过添加校验位提升纠错能力,等价于用更多的比特表达相同信息量,因此码率降低为信息比特数与码长之比。

3. ZF 均衡为什么可能放大噪声?
答:ZF 追求消除 ISI,会在频域对信道的深衰落频点进行强增益补偿,从而同时放大噪声,导致噪声增强。

4. LMS 的步长过大或过小会出现什么问题?
答:步长过大会导致算法不稳定甚至发散;步长过小会收敛缓慢,训练时间变长,难以及时跟踪信道变化。

5. 均衡前后 ISI 有什么变化?
答:均衡后等效信道响应趋近冲激,符号间叠加减弱,ISI 明显减小,眼图开口增大,误码率下降。

## 7. 实验心得

通过本实验理解了分组码的纠错机理与多径信道均衡的核心思路。编码能够在噪声环境下提升可靠性,而均衡器可显著减轻 ISI 的影响。结合自动评分与结果可视化,能够快速定位问题并验证实现是否正确。AI 编程辅助主要用于梳理实现步骤与检查细节,提高了效率,但关键算法仍需自行理解与验证,确保结果可信。

## 8. 参考资料

- 课程课件:第6章 信道编码
- 课程课件:第7章 均衡
40 changes: 27 additions & 13 deletions REPORT_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,71 @@

## 1. 实验目的

说明本实验希望掌握的信道编码、信道均衡和 GitHub 自动评分技能
掌握 Hamming(7,4) 信道编码与单比特纠错的基本方法;理解多径信道导致的 ISI 以及 ZF/LMS 均衡器的设计与效果评估;熟悉使用 GitHub 自动评分与本地脚本进行实验结果验证

Comment on lines 3 to 6
## 2. 实验原理

### 2.1 信道编码

说明 Hamming(7,4)、伴随式、单比特纠错、编码增益等概念
Hamming(7,4) 是一种线性分组码,将 4 比特信息映射为 7 比特码字,通过冗余校验实现纠错能力。伴随式由接收码字与校验矩阵 H 相乘得到,非零伴随式对应某一列,指示错误位置,从而实现单比特纠错。编码增益指在相同误码率下,编码系统相对未编码系统所需信噪比降低的程度

### 2.2 信道均衡

说明 ISI、多径信道、ZF、LMS 自适应均衡等概念
多径信道的冲激响应会在符号间产生叠加,导致符号间干扰(ISI)。ZF 均衡器通过设计 FIR 抽头使等效信道尽量逼近冲激,从而消除 ISI,但可能放大噪声。LMS 自适应均衡器利用训练序列迭代更新抽头,使均衡输出逐步逼近期望符号,实现对未知或时变信道的跟踪

## 3. 实验环境

- Python 版本:
- Python 版本:3.x(Windows)
- 主要依赖:NumPy、Matplotlib、pytest
- AI 助手使用情况:
- AI 助手使用情况:使用 GitHub Copilot(GPT-5.2-Codex)协助理解实验要求与补全代码

## 4. 实验方法与步骤

### 4.1 Part 1:信道编码

描述编码、加噪/翻转、译码、BER 对比流程。
1. 生成随机比特序列,并按 4 比特分组。
2. 使用 Hamming(7,4) 生成矩阵完成编码得到 7 比特码字。
3. 通过二元对称信道以不同翻转概率传输,得到带噪码字。
4. 计算伴随式并进行单比特纠错译码,取系统码前 4 位恢复信息比特。
5. 统计未编码与编码后的 BER,并绘制对比曲线。

### 4.2 Part 2:信道均衡

描述多径信道、ZF/LMS 均衡器设计、均衡效果评估流程。
1. 生成 BPSK 符号序列并通过多径 FIR 信道,加入噪声得到接收序列。
2. 使用最小二乘法估计 ZF 均衡器抽头作为参考。
3. 采用 LMS 算法,利用训练序列迭代更新均衡器抽头。
4. 通过均衡输出进行硬判决,计算均衡前后 BER,并绘制均衡波形与 LMS 误差曲线。

## 5. 实验结果

插入结果图:

```markdown
![编码BER曲线](results/coding_ber_curve.png)
![均衡眼图对比](results/equalization_eye_comparison.png)
![LMS误差曲线](results/equalization_mse_curve.png)
```
![编码BER曲线](./results/coding_ber_curve.png)
![均衡眼图对比](./results/equalization_eye_comparison.png)
![LMS误差曲线](./results/equalization_mse_curve.png)

## 6. 结果分析与讨论

回答:

1. Hamming(7,4) 为什么能纠正单比特错误?
答:Hamming(7,4) 码的校验矩阵 H 的每一列唯一对应一个比特位置,单比特错误会产生非零伴随式,且其值与某一列一致,因此可以定位并翻转该位完成纠错。

2. 为什么信道编码会引入冗余并降低码率?
答:编码通过添加校验位提升纠错能力,等价于用更多的比特表达相同信息量,因此码率降低为信息比特数与码长之比。

3. ZF 均衡为什么可能放大噪声?
答:ZF 追求消除 ISI,会在频域对信道的深衰落频点进行强增益补偿,从而同时放大噪声,导致噪声增强。

4. LMS 的步长过大或过小会出现什么问题?
答:步长过大会导致算法不稳定甚至发散;步长过小会收敛缓慢,训练时间变长,难以及时跟踪信道变化。

5. 均衡前后 ISI 有什么变化?
答:均衡后等效信道响应趋近冲激,符号间叠加减弱,ISI 明显减小,星座/眼图开口增大,误码率下降。

## 7. 实验心得

说明你对信道编码、信道均衡、自动评分和 AI 编程辅助的理解
通过本实验理解了分组码的纠错机理与多径信道均衡的核心思路。编码能够在噪声环境下提升可靠性,而均衡器可显著减轻 ISI 的影响。结合自动评分与结果可视化,能够快速定位问题并验证实现是否正确。AI 编程辅助主要用于梳理实现步骤与检查细节,提高了效率,但关键算法仍需自行理解与验证

## 8. 参考资料

Expand Down
Binary file added results/coding_ber_curve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added results/equalization_eye_comparison.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added results/equalization_mse_curve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 82 additions & 10 deletions src/part1_channel_coding.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (blocks @ HAMMING_G) % 2
return encoded.reshape(-1)


def hamming74_syndrome(codewords):
Expand All @@ -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 = (codewords @ HAMMING_H.T) % 2
return syndromes


def hamming74_decode(received):
Expand All @@ -94,8 +95,20 @@ 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).copy()
syndromes = hamming74_syndrome(codewords)

# 预计算伴随式到错误位置的映射
syndrome_map = {tuple(HAMMING_H[:, i] % 2): i for i in range(7)}

for idx, syndrome in enumerate(syndromes):
if np.any(syndrome):
error_pos = syndrome_map.get(tuple(syndrome))
if error_pos is not None:
codewords[idx, error_pos] ^= 1

decoded_bits = codewords[:, :4].reshape(-1)
return decoded_bits


def convolutional_encode(bits):
Expand All @@ -108,8 +121,17 @@ def convolutional_encode(bits):
if not np.all((bits == 0) | (bits == 1)):
raise ValueError('bits 只能包含 0 或 1')

# TODO: 选做任务,可参考课件第6章卷积码部分。
raise NotImplementedError('选做:请实现卷积码编码')
tail = np.zeros(2, dtype=int)
bits = np.concatenate([bits, tail])
state = [0, 0] # s1, s2
outputs = []
for bit in bits:
s1, s2 = state
g1 = bit ^ s1 ^ s2
g2 = bit ^ s2
outputs.extend([g1, g2])
state = [bit, s1]
return np.array(outputs, dtype=int)


def viterbi_decode_hard(received_bits):
Expand All @@ -120,8 +142,58 @@ def viterbi_decode_hard(received_bits):
if len(received_bits) % 2 != 0:
raise ValueError('卷积码接收序列长度必须是 2 的倍数')

# TODO: 选做任务,可使用汉明距离作为路径度量。
raise NotImplementedError('选做:请实现 Viterbi 硬判决译码')
num_symbols = len(received_bits) // 2
received_pairs = received_bits.reshape(-1, 2)

num_states = 4 # memory=2
inf = 1e9
path_metrics = np.full(num_states, inf, dtype=float)
path_metrics[0] = 0.0
prev_state = np.full((num_symbols, num_states), -1, dtype=int)
prev_bit = np.full((num_symbols, num_states), -1, dtype=int)

# State representation: (s1, s2) -> s1*2 + s2
for t in range(num_symbols):
rx = received_pairs[t]
next_metrics = np.full(num_states, inf, dtype=float)
next_prev_state = np.full(num_states, -1, dtype=int)
next_prev_bit = np.full(num_states, -1, dtype=int)

for state in range(num_states):
if path_metrics[state] >= inf:
continue
s1 = (state >> 1) & 1
s2 = state & 1
for bit in (0, 1):
g1 = bit ^ s1 ^ s2
g2 = bit ^ s2
dist = (g1 != rx[0]) + (g2 != rx[1])
next_state = (bit << 1) | s1
metric = path_metrics[state] + dist
if metric < next_metrics[next_state]:
next_metrics[next_state] = metric
next_prev_state[next_state] = state
next_prev_bit[next_state] = bit

path_metrics = next_metrics
prev_state[t] = next_prev_state
prev_bit[t] = next_prev_bit

# Prefer terminating to all-zero state when tail bits are used
final_state = 0
if path_metrics[final_state] >= inf:
final_state = int(np.argmin(path_metrics))

decoded = np.zeros(num_symbols, dtype=int)
state = final_state
for t in range(num_symbols - 1, -1, -1):
bit = prev_bit[t, state]
decoded[t] = 0 if bit < 0 else bit
state = prev_state[t, state] if prev_state[t, state] >= 0 else 0

if num_symbols >= 2:
decoded = decoded[:-2]
return decoded


def run_coding_demo():
Expand Down
35 changes: 29 additions & 6 deletions src/part2_equalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,21 @@ def estimate_zf_equalizer(channel, num_taps):
if num_taps < 1:
raise ValueError('num_taps 必须为正整数')

# TODO: 构造卷积矩阵并求解 ZF 均衡器抽头。
raise NotImplementedError('请实现 ZF 均衡器估计')
channel_len = len(channel)
conv_len = channel_len + num_taps - 1
conv_matrix = np.zeros((conv_len, num_taps), dtype=float)
for row in range(conv_len):
for col in range(num_taps):
idx = row - col
if 0 <= idx < channel_len:
conv_matrix[row, col] = channel[idx]

desired = np.zeros(conv_len, dtype=float)
center = (channel_len - 1) // 2 + (num_taps - 1) // 2
desired[center] = 1.0

taps, *_ = np.linalg.lstsq(conv_matrix, desired, rcond=None)
return taps


def apply_fir_filter(signal, taps):
Expand All @@ -58,8 +71,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):
Expand Down Expand Up @@ -89,8 +102,18 @@ 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 均衡器')
taps = np.zeros(num_taps, dtype=float)
taps[(num_taps - 1) // 2] = 1.0
errors = []

for n in range(num_taps - 1, len(rx_train)):
x = rx_train[n - num_taps + 1: n + 1][::-1]
y = np.dot(taps, x)
error = tx_train[n] - y
taps = taps + step_size * error * x
errors.append(error)

return taps, np.asarray(errors, dtype=float)


def run_equalization_demo():
Expand Down
Loading