数字调制的本质是用数字比特流控制载波的某个参数(幅度、频率、相位),将基带信息"搬运"到适合信道传输的频段。三种最基本的数字调制方式:
| 调制方式 | 控制参数 | 数学表达式 | 特点 |
|---|---|---|---|
| ASK | 幅度 | s(t) = A_m · cos(2πf_ct) | 简单但抗噪声差 |
| FSK | 频率 | s(t) = cos(2π(f_c±Δf)t) | 抗噪声好,恒包络 |
| PSK | 相位 | s(t) = cos(2πf_ct + φ_m) | 频谱效率最高 |
ASK通过改变载波幅度来传递信息。最简单的OOK(On-Off Keying)就是2-ASK:
OOK信号的功率谱密度为:
第一项是载波分量(浪费功率),第二项是信息边带。带宽 B ≈ 2/T_s = 2R_s。
ASK的缺点:对幅度衰落敏感,不适合衰落信道。实际应用中较少单独使用。
FSK用不同频率代表不同符号。2-FSK(BFSK):
其中 Δf = |f_1 - f_0|/2,T_s 为符号周期。
PSK通过改变载波相位传递信息。BPSK(Binary PSK):
接收端将接收信号与本地载波相乘:
判决规则:LPF输出 > 0 → '0',< 0 → '1'
BPSK的BER(AWGN信道):
BPSK是最鲁棒的二进制调制,比OOK有3dB优势(无载波功率浪费)。
// modulator_ask_fsk_psk.v - 三种基本调制器
// 第03课:ASK/FSK/PSK调制
// ============================================================
// NCO(数控振荡器)- 调制器核心
// ============================================================
module nco #(
parameter PHASE_W = 16, // 相位累加器位宽
parameter OUT_W = 12 // 输出位宽
)(
input wire clk,
input wire rst_n,
input wire [PHASE_W-1:0] freq_word, // 频率控制字
input wire [PHASE_W-1:0] phase_offset, // 相位偏移
output wire [OUT_W-1:0] sin_out, // 正弦输出
output wire [OUT_W-1:0] cos_out // 余弦输出
);
reg [PHASE_W-1:0] phase_acc;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
phase_acc <= 0;
else
phase_acc <= phase_acc + freq_word;
end
wire [PHASE_W-1:0] phase_sin = phase_acc + phase_offset;
wire [PHASE_W-1:0] phase_cos = phase_acc + phase_offset + (1 << (PHASE_W-2)); // +90°
// 正弦查找表 (1/4周期对称)
reg [OUT_W-1:0] sin_table [0:2**PHASE_W-1];
reg [OUT_W-1:0] cos_table [0:2**PHASE_W-1];
initial begin
integer i;
for (i = 0; i < 2**PHASE_W; i = i + 1) begin
sin_table[i] = $rtoi((2**(OUT_W-1) - 1) *
$sin(2.0 * 3.14159265 * i / 2**PHASE_W));
cos_table[i] = $rtoi((2**(OUT_W-1) - 1) *
$cos(2.0 * 3.14159265 * i / 2**PHASE_W));
end
end
assign sin_out = sin_table[phase_sin];
assign cos_out = cos_table[phase_cos];
endmodule
// ============================================================
// OOK/ASK 调制器
// ============================================================
module ask_modulator #(
parameter DATA_W = 1,
parameter PHASE_W = 16,
parameter OUT_W = 12,
parameter CARRIER_FREQ_WORD = 16'h1000 // 载波频率控制字
)(
input wire clk,
input wire rst_n,
input wire [DATA_W-1:0] tx_bit, // 待调制比特
input wire tx_valid, // 数据有效
output wire [OUT_W-1:0] mod_out // 调制输出
);
wire [OUT_W-1:0] carrier;
nco #(
.PHASE_W(PHASE_W),
.OUT_W (OUT_W)
) u_carrier (
.clk (clk),
.rst_n (rst_n),
.freq_word (CARRIER_FREQ_WORD),
.phase_offset (16'd0),
.sin_out (carrier),
.cos_out ()
);
// ASK: 比特'1'→有载波,比特'0'→无载波
reg [OUT_W-1:0] amplitude;
always @(*) begin
case (tx_bit)
1'b0: amplitude = {OUT_W{1'b0}}; // 关闭
1'b1: amplitude = {1'b0, {(OUT_W-1){1'b1}}}; // 满幅度
default: amplitude = {OUT_W{1'b0}};
endcase
end
assign mod_out = (tx_valid) ? (carrier & amplitude[OUT_W-1:0]) : {OUT_W{1'b0}};
endmodule
// ============================================================
// BFSK 调制器
// ============================================================
module bfsk_modulator #(
parameter PHASE_W = 16,
parameter OUT_W = 12,
parameter FREQ_WORD_0 = 16'h0800, // '0'对应频率
parameter FREQ_WORD_1 = 16'h1000 // '1'对应频率
)(
input wire clk,
input wire rst_n,
input wire tx_bit, // 待调制比特
input wire tx_valid,
output wire [OUT_W-1:0] mod_out
);
// 根据比特选择频率控制字
reg [PHASE_W-1:0] freq_select;
always @(*) begin
freq_select = tx_bit ? FREQ_WORD_1 : FREQ_WORD_0;
end
nco #(
.PHASE_W(PHASE_W),
.OUT_W (OUT_W)
) u_fsk_nco (
.clk (clk),
.rst_n (rst_n),
.freq_word (freq_select),
.phase_offset (16'd0),
.sin_out (mod_out),
.cos_out ()
);
endmodule
// ============================================================
// BPSK 调制器
// ============================================================
module bpsk_modulator #(
parameter PHASE_W = 16,
parameter OUT_W = 12,
parameter CARRIER_FREQ_WORD = 16'h1000
)(
input wire clk,
input wire rst_n,
input wire tx_bit, // '0'→相位0, '1'→相位π
input wire tx_valid,
output wire [OUT_W-1:0] mod_out
);
// BPSK: 0→相位0, 1→相位π (等价于乘以±1)
wire [PHASE_W-1:0] phase_mod;
assign phase_mod = tx_bit ? {1'b1, {(PHASE_W-1){1'b0}}} : {PHASE_W{1'b0}};
nco #(
.PHASE_W(PHASE_W),
.OUT_W (OUT_W)
) u_bpsk_nco (
.clk (clk),
.rst_n (rst_n),
.freq_word (CARRIER_FREQ_WORD),
.phase_offset (phase_mod),
.sin_out (mod_out),
.cos_out ()
);
endmodule
// ============================================================
// BPSK 相干解调器
// ============================================================
module bpsk_demodulator #(
parameter SAMPLE_W = 12,
parameter PHASE_W = 16,
parameter OUT_W = 1
)(
input wire clk,
input wire rst_n,
input wire [SAMPLE_W-1:0] rx_sample, // 接收采样
input wire rx_valid,
output wire rx_bit, // 解调比特
output wire rx_valid_out,
// 本地载波NCO
input wire [PHASE_W-1:0] carrier_freq_word,
input wire carrier_lock // 载波锁定指示
);
// 本地载波
wire [SAMPLE_W-1:0] local_carrier;
nco #(
.PHASE_W(PHASE_W),
.OUT_W (SAMPLE_W)
) u_local_osc (
.clk (clk),
.rst_n (rst_n),
.freq_word (carrier_freq_word),
.phase_offset (16'd0),
.sin_out (local_carrier),
.cos_out ()
);
// 乘法器 (下变频)
reg signed [2*SAMPLE_W:0] mult_result;
always @(posedge clk) begin
if (rx_valid)
mult_result <= $signed(rx_sample) * $signed({1'b0, local_carrier});
end
// 积分-转储滤波器 (匹配滤波)
localparam INTEGRATE_LEN = 16;
reg signed [2*SAMPLE_W+$clog2(INTEGRATE_LEN):0] integrate_acc;
reg [$clog2(INTEGRATE_LEN)-1:0] sample_cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
integrate_acc <= 0;
sample_cnt <= 0;
end else if (rx_valid) begin
if (sample_cnt == 0)
integrate_acc <= mult_result >>> (SAMPLE_W);
else
integrate_acc <= integrate_acc + (mult_result >>> SAMPLE_W);
sample_cnt <= sample_cnt + 1'b1;
end
end
// 判决
assign rx_bit = integrate_acc[2*SAMPLE_W+$clog2(INTEGRATE_LEN)] ? 1'b1 : 1'b0;
// 符号结束时刻输出有效
assign rx_valid_out = (rx_valid && sample_cnt == INTEGRATE_LEN - 1) && carrier_lock;
endmodule
#!/usr/bin/env python3
"""ask_fsk_psk_simulation.py - ASK/FSK/PSK调制仿真对比
第03课:ASK/FSK/PSK调制
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import erfc
def generate_ask(bits, fs, fc, symbol_period):
"""OOK/ASK调制"""
samples_per_symbol = int(fs * symbol_period)
t = np.arange(len(bits) * samples_per_symbol) / fs
signal = np.zeros_like(t)
for i, bit in enumerate(bits):
start = i * samples_per_symbol
end = start + samples_per_symbol
if bit == 1:
signal[start:end] = np.cos(2 * np.pi * fc * t[start:end])
return t, signal
def generate_fsk(bits, fs, f0, f1, symbol_period):
"""BFSK调制(连续相位)"""
samples_per_symbol = int(fs * symbol_period)
t = np.arange(len(bits) * samples_per_symbol) / fs
signal = np.zeros_like(t)
phase = 0
for i, bit in enumerate(bits):
freq = f1 if bit == 1 else f0
start = i * samples_per_symbol
end = start + samples_per_symbol
t_local = t[start:end] - t[start]
signal[start:end] = np.cos(2 * np.pi * freq * t_local + phase)
phase = 2 * np.pi * freq * symbol_period + phase
phase = phase % (2 * np.pi)
return t, signal
def generate_bpsk(bits, fs, fc, symbol_period):
"""BPSK调制"""
samples_per_symbol = int(fs * symbol_period)
t = np.arange(len(bits) * samples_per_symbol) / fs
signal = np.zeros_like(t)
for i, bit in enumerate(bits):
start = i * samples_per_symbol
end = start + samples_per_symbol
phase = np.pi if bit == 1 else 0
signal[start:end] = np.cos(2 * np.pi * fc * t[start:end] + phase)
return t, signal
def simulate_ber_awgn(mod_type='bpsk', snr_db_range=np.arange(-5, 16)):
"""AWGN信道下BER仿真"""
np.random.seed(42)
num_bits = 100000
ber_results = []
for snr_db in snr_db_range:
snr_lin = 10 ** (snr_db / 10)
bits = np.random.randint(0, 2, num_bits)
noise_std = 1.0 / np.sqrt(2 * snr_lin)
if mod_type == 'ask':
symbols = bits.astype(float) # 0或1
elif mod_type == 'fsk':
symbols = 2 * bits - 1 # 非相干FSK
elif mod_type == 'bpsk':
symbols = 1 - 2 * bits # +1或-1
noise = noise_std * np.random.randn(num_bits)
rx = symbols + noise
if mod_type == 'ask':
rx_bits = (rx > 0.5).astype(int)
elif mod_type == 'fsk':
# 非相干检测(近似)
rx_bits = (rx > 0).astype(int)
rx_bits = 1 - rx_bits # 反转匹配
elif mod_type == 'bpsk':
rx_bits = (rx < 0).astype(int)
ber = np.sum(bits != rx_bits) / num_bits
ber_results.append(max(ber, 1e-7))
return ber_results
def plot_all_modulations():
"""绘制三种调制方式的波形和BER对比"""
fs = 1000
fc = 50
f0, f1 = 40, 60
symbol_period = 0.05
test_bits = [1, 0, 1, 1, 0, 0, 1, 0]
fig, axes = plt.subplots(3, 1, figsize=(14, 10))
# ASK
t_ask, s_ask = generate_ask(test_bits, fs, fc, symbol_period)
axes[0].plot(t_ask * 1000, s_ask, 'c-', linewidth=0.8)
for i, b in enumerate(test_bits):
axes[0].text(i * symbol_period * 1000 + symbol_period * 500, 1.3,
str(b), ha='center', fontsize=12, color='#f59e0b', fontweight='bold')
axes[0].set_title('ASK (OOK) 调制波形', fontsize=13)
axes[0].set_ylabel('幅度')
axes[0].grid(True, alpha=0.3)
# FSK
t_fsk, s_fsk = generate_fsk(test_bits, fs, f0, f1, symbol_period)
axes[1].plot(t_fsk * 1000, s_fsk, '#10b981', linewidth=0.8)
for i, b in enumerate(test_bits):
axes[1].text(i * symbol_period * 1000 + symbol_period * 500, 1.3,
str(b), ha='center', fontsize=12, color='#f59e0b', fontweight='bold')
axes[1].set_title(f'FSK 调制波形 (f0={f0}Hz, f1={f1}Hz)', fontsize=13)
axes[1].set_ylabel('幅度')
axes[1].grid(True, alpha=0.3)
# BPSK
t_bpsk, s_bpsk = generate_bpsk(test_bits, fs, fc, symbol_period)
axes[2].plot(t_bpsk * 1000, s_bpsk, '#8b5cf6', linewidth=0.8)
for i, b in enumerate(test_bits):
axes[2].text(i * symbol_period * 1000 + symbol_period * 500, 1.3,
str(b), ha='center', fontsize=12, color='#f59e0b', fontweight='bold')
axes[2].set_title('BPSK 调制波形', fontsize=13)
axes[2].set_xlabel('时间 (ms)')
axes[2].set_ylabel('幅度')
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('/var/www/ttl/digital-comm/ask_fsk_psk_waveforms.png', dpi=100,
facecolor='#0f172a', edgecolor='none')
print("调制波形图已保存")
# BER对比图
snr_range = np.arange(-2, 14)
ber_ask = simulate_ber_awgn('ask', snr_range)
ber_fsk = simulate_ber_awgn('fsk', snr_range)
ber_bpsk = simulate_ber_awgm('bpsk', snr_range) if False else simulate_ber_awgn('bpsk', snr_range)
# 理论值
ber_bpsk_th = 0.5 * erfc(np.sqrt(10**(snr_range/10)))
ber_ask_th = 0.5 * erfc(np.sqrt(10**(snr_range/10) / 4)) # OOK比BPSK差3dB
plt.figure(figsize=(10, 7))
plt.semilogy(snr_range, ber_ask, 'rs-', markersize=4, label='ASK仿真')
plt.semilogy(snr_range, ber_fsk, 'g^-', markersize=4, label='FSK仿真')
plt.semilogy(snr_range, ber_bpsk, 'bo-', markersize=4, label='BPSK仿真')
plt.semilogy(snr_range, ber_bpsk_th, 'c--', label='BPSK理论')
plt.semilogy(snr_range, ber_ask_th, 'r--', label='ASK理论')
plt.xlabel('Eb/N0 (dB)', fontsize=12)
plt.ylabel('误码率 (BER)', fontsize=12)
plt.title('ASK/FSK/BPSK 在AWGN信道下的BER对比', fontsize=14)
plt.legend(fontsize=10)
plt.grid(True, alpha=0.3, which='both')
plt.ylim(1e-6, 1)
plt.savefig('/var/www/ttl/digital-comm/ask_fsk_psk_ber.png', dpi=100,
facecolor='#0f172a', edgecolor='none')
print("BER对比图已保存")
def simulate_constellation():
"""绘制BPSK星座图"""
np.random.seed(42)
num_bits = 2000
snr_db = 8
bits = np.random.randint(0, 2, num_bits)
symbols = 1 - 2 * bits # BPSK: 0→+1, 1→-1
snr_lin = 10 ** (snr_db / 10)
noise_std = 1.0 / np.sqrt(2 * snr_lin)
noise_i = noise_std * np.random.randn(num_bits)
noise_q = noise_std * np.random.randn(num_bits)
rx_i = symbols + noise_i
rx_q = noise_q # BPSK只在I路有信号
plt.figure(figsize=(8, 8))
plt.scatter(rx_i, rx_q, c=['cyan' if b==0 else '#f59e0b' for b in bits],
s=3, alpha=0.5)
plt.axhline(0, color='white', alpha=0.3)
plt.axvline(0, color='white', alpha=0.3)
plt.plot(1, 0, 'c+', markersize=20, markeredgewidth=2, label='0: +1')
plt.plot(-1, 0, '#f59e0b+', markersize=20, markeredgewidth=2, label='1: -1')
plt.xlabel('I路', fontsize=12)
plt.ylabel('Q路', fontsize=12)
plt.title(f'BPSK星座图 (SNR={snr_db}dB)', fontsize=14)
plt.legend()
plt.grid(True, alpha=0.3)
plt.axis('equal')
plt.savefig('/var/www/ttl/digital-comm/bpsk_constellation.png', dpi=100,
facecolor='#0f172a', edgecolor='none')
print("BPSK星座图已保存")
if __name__ == '__main__':
print("=" * 60)
print("ASK/FSK/PSK 调制仿真")
print("=" * 60)
plot_all_modulations()
simulate_constellation()
print("\n✅ 所有仿真完成!")
| 指标 | OOK/ASK | BFSK | BPSK |
|---|---|---|---|
| 频谱效率 | 0.5 bps/Hz | 0.5 bps/Hz | 0.5 bps/Hz |
| BER性能 | 最差 | 中等 | 最好 |
| 包络 | 非恒定 | 恒定 | 恒定 |
| 功放要求 | 线性功放 | 非线性即可 | 非线性即可 |
| 解调复杂度 | 低(包络检测) | 中(鉴频器) | 高(相干解调) |
| 载波恢复 | 不需要 | 不需要 | 需要 |
| 典型应用 | 光通信OOK | 蓝牙BLE | 深空通信 |
练习1:修改ASK调制器,实现M-ASK(4电平幅度调制),分析其频谱效率提升。
练习2:设计一个FSK非相干解调器(包络检测法),与相干解调比较BER。
练习3:实现连续相位FSK(CPFSK),观察相位轨迹为何没有突变。
练习4:在BPSK解调器中,如果本地载波有10°相位误差,BER恶化多少?
练习5:用Python仿真:比较BPSK和DPSK(差分PSK)在AWGN下的BER差异。
你掌握了数字调制的三大基本方式!从OOK到BPSK,从NCO到相干解调,你已经能设计基本的数字调制解调器了。
下一课预告:第04课进入多进制调制——QPSK和QAM,用同样的带宽传输更多比特!