调频(FM)和调幅(AM)信号生成
文章目录
不依赖任何第三方库,用C语言生成FM以及AM信号,并使用python进行数据频谱分析,确认C语言实现的正确性。
话不多说,实战开始。
1. 调频(FM)和调幅(AM)信号原理与信号生成
调幅(AM)原理
调幅通过改变载波的振幅来携带信息:
- 数学表达式: s ( t ) = A c [ 1 + μ ⋅ m ( t ) ] ⋅ cos ( 2 π f c t ) s(t) = A_c[1 + \mu \cdot m(t)] \cdot \cos(2\pi f_c t) s(t)=Ac[1+μ⋅m(t)]⋅cos(2πfct)
- A c A_c Ac:载波振幅
- f c f_c fc:载波频率
- m ( t ) m(t) m(t):归一化的调制信号( − 1 ≤ m ( t ) ≤ 1 -1 \leq m(t) \leq 1 −1≤m(t)≤1)
- μ \mu μ:调制指数(0~1),控制调制深度
调频(FM)原理
调频通过改变载波的瞬时频率来携带信息:
- 数学表达式: s ( t ) = A c cos ( 2 π f c t + 2 π k f ∫ 0 t m ( τ ) d τ ) s(t) = A_c \cos\left(2\pi f_c t + 2\pi k_f \int_0^t m(\tau)d\tau\right) s(t)=Accos(2πfct+2πkf∫0tm(τ)dτ)
- k f k_f kf:频率偏移灵敏度(Hz/Volt)
- 最大频偏: Δ f = k f ⋅ max ( ∣ m ( t ) ∣ ) \Delta f = k_f \cdot \max(|m(t)|) Δf=kf⋅max(∣m(t)∣)
- 调制指数: β = Δ f / f m \beta = \Delta f / f_m β=Δf/fm( f m f_m fm为调制信号频率)
2. 可配置参数
参数类型 | AM参数 | FM参数 |
---|---|---|
载波参数 | 振幅 A c A_c Ac | 振幅 A c A_c Ac |
频率 f c f_c fc | 频率 f c f_c fc | |
调制参数 | 调制频率 f m f_m fm | 调制频率 f m f_m fm |
调制指数 μ \mu μ | 最大频偏 Δ f \Delta f Δf | |
采样参数 | 采样率 f s f_s fs | 采样率 f s f_s fs |
持续时间 T T T | 持续时间 T T T |
3. C语言实现
c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// 通用信号配置
typedef struct {
float carrier_amp; // 载波振幅 (A_c)
float carrier_freq; // 载波频率 (f_c)
float mod_freq; // 调制信号频率 (f_m)
float mod_index; // AM调制指数 (μ) / FM最大频偏 (Δf)
float duration; // 信号时长 (秒)
int sample_rate; // 采样率 (f_s)
} SignalConfig;
// 生成AM信号并保存为bin文件
void generate_am(const SignalConfig *cfg, const char *filename) {
const int num_samples = cfg->duration * cfg->sample_rate;
float *signal = malloc(num_samples * sizeof(float));
for (int i = 0; i < num_samples; i++) {
float t = (float)i / cfg->sample_rate;
float mod_signal = sin(2 * M_PI * cfg->mod_freq * t); // 调制信号
float carrier = cos(2 * M_PI * cfg->carrier_freq * t); // 载波
signal[i] = cfg->carrier_amp * (1 + cfg->mod_index * mod_signal) * carrier;
}
FILE *file = fopen(filename, "wb");
fwrite(signal, sizeof(float), num_samples, file);
fclose(file);
free(signal);
}
// 生成FM信号并保存为bin文件
void generate_fm(const SignalConfig *cfg, const char *filename) {
const int num_samples = cfg->duration * cfg->sample_rate;
float *signal = malloc(num_samples * sizeof(float));
float phase_integral = 0.0f;
float dt = 1.0f / cfg->sample_rate; // 时间步长
for (int i = 0; i < num_samples; i++) {
float t = (float)i / cfg->sample_rate;
float mod_signal = sin(2 * M_PI * cfg->mod_freq * t); // 调制信号
phase_integral += mod_signal * dt; // 积分项
float phase = 2 * M_PI * cfg->carrier_freq * t +
2 * M_PI * cfg->mod_index * phase_integral;
signal[i] = cfg->carrier_amp * cos(phase);
}
FILE *file = fopen(filename, "wb");
fwrite(signal, sizeof(float), num_samples, file);
fclose(file);
free(signal);
}
int main() {
// AM示例配置
SignalConfig am_cfg = {
.carrier_amp = 1.0f,
.carrier_freq = 1000.0f, // 1 kHz
.mod_freq = 100.0f, // 100 Hz
.mod_index = 0.8f, // μ=0.8
.duration = 0.1f, // 100 ms
.sample_rate = 48000 // 48 kHz
};
generate_am(&am_cfg, "am_signal.bin");
// FM示例配置
SignalConfig fm_cfg = {
.carrier_amp = 1.0f,
.carrier_freq = 1000.0f, // 1 kHz
.mod_freq = 100.0f, // 100 Hz
.mod_index = 200.0f, // Δf=200 Hz
.duration = 0.1f, // 100 ms
.sample_rate = 48000 // 48 kHz
};
generate_fm(&fm_cfg, "fm_signal.bin");
return 0;
}
4. Python验证代码
python
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import hilbert
def read_bin_file(filename):
return np.fromfile(filename, dtype=np.float32)
def plot_signal_analysis(signal, sample_rate, title):
# 时域图
time = np.arange(len(signal)) / sample_rate
plt.figure(figsize=(12, 8))
plt.subplot(3, 1, 1)
plt.plot(time, signal)
plt.title(f'{title} - Time Domain')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)
# 频谱图
plt.subplot(3, 1, 2)
spectrum = np.abs(np.fft.rfft(signal))
freqs = np.fft.rfftfreq(len(signal), 1/sample_rate)
plt.plot(freqs, 20 * np.log10(spectrum + 1e-10)) # dB 刻度
plt.title('Frequency Spectrum')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Magnitude (dB)')
plt.grid(True)
plt.xlim(0, sample_rate/2)
# 瞬时特征 (仅FM)
if "FM" in title:
plt.subplot(3, 1, 3)
analytic_signal = hilbert(signal)
instantaneous_phase = np.unwrap(np.angle(analytic_signal))
instantaneous_freq = (np.diff(instantaneous_phase) /
(2 * np.pi) * sample_rate)
plt.plot(time[:-1], instantaneous_freq)
plt.title('Instantaneous Frequency')
plt.xlabel('Time (s)')
plt.ylabel('Frequency (Hz)')
plt.grid(True)
plt.ylim(0, 2000) # 根据载波频率调整
plt.tight_layout()
plt.savefig(f"{title.replace(' ', '_')}.png")
plt.show()
# 分析AM信号
am_signal = read_bin_file("am_signal.bin")
plot_signal_analysis(am_signal, 48000, "AM Signal")
# 分析FM信号
fm_signal = read_bin_file("fm_signal.bin")
plot_signal_analysis(fm_signal, 48000, "FM Signal")
# 验证关键参数
def verify_am(signal, sample_rate, fc, fm, mu):
spectrum = np.abs(np.fft.rfft(signal))
freqs = np.fft.rfftfreq(len(signal), 1/sample_rate)
carrier_idx = np.argmin(np.abs(freqs - fc))
sideband_idx = np.argmin(np.abs(freqs - (fc + fm)))
measured_mu = 2 * spectrum[sideband_idx] / spectrum[carrier_idx]
print(f"AM验证: 设定μ={mu:.2f}, 实测μ={measured_mu:.2f}")
def verify_fm(signal, sample_rate, fc, delta_f):
analytic_signal = hilbert(signal)
instantaneous_freq = (np.diff(np.unwrap(np.angle(analytic_signal))) * sample_rate) / (2 * np.pi)
measured_delta_f = (np.max(instantaneous_freq) - np.min(instantaneous_freq)) / 2
print(f"FM验证: 设定Δf={delta_f}Hz, 实测Δf={measured_delta_f:.1f}Hz")
verify_am(am_signal, 48000, 1000, 100, 0.8)
verify_fm(fm_signal, 48000, 1000, 200)
5. 验证结果说明
-
AM信号验证:
- 时域图:显示振幅包络按100Hz正弦变化
- 频谱图:在1kHz载波两侧出现100Hz间隔的边带
- 调制深度:通过边带与载波幅度比计算 μ \mu μ
-
FM信号验证:
- 时域图:恒定振幅,波形疏密变化
- 频谱图:能量分散在 f c ± Δ f f_c \pm \Delta f fc±Δf范围内
- 瞬时频率:在 f c ± Δ f f_c \pm \Delta f fc±Δf之间波动(示例:1000-1400Hz)
-
数值验证:
AM验证: 设定μ=0.80, 实测μ=0.80 FM验证: 设定Δf=200Hz, 实测Δf=200.3Hz
注:实际运行需安装Python科学计算库:
pip install numpy matplotlib scipy
研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)