[实战]调频(FM)和调幅(AM)信号生成(完整C语言实现)

调频(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. 验证结果说明

  1. AM信号验证

    • 时域图:显示振幅包络按100Hz正弦变化
    • 频谱图:在1kHz载波两侧出现100Hz间隔的边带
    • 调制深度:通过边带与载波幅度比计算 μ \mu μ
  2. FM信号验证

    • 时域图:恒定振幅,波形疏密变化
    • 频谱图:能量分散在 f c ± Δ f f_c \pm \Delta f fc±Δf范围内
    • 瞬时频率:在 f c ± Δ f f_c \pm \Delta f fc±Δf之间波动(示例:1000-1400Hz)
  3. 数值验证

    复制代码
     AM验证: 设定μ=0.80, 实测μ=0.80
     FM验证: 设定Δf=200Hz, 实测Δf=200.3Hz

注:实际运行需安装Python科学计算库:pip install numpy matplotlib scipy


研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


相关推荐
Tiandaren1 小时前
Selenium 4 教程:自动化 WebDriver 管理与 Cookie 提取 || 用于解决chromedriver版本不匹配问题
selenium·测试工具·算法·自动化
岁忧2 小时前
(LeetCode 面试经典 150 题 ) 11. 盛最多水的容器 (贪心+双指针)
java·c++·算法·leetcode·面试·go
chao_7892 小时前
二分查找篇——搜索旋转排序数组【LeetCode】两次二分查找
开发语言·数据结构·python·算法·leetcode
秋说4 小时前
【PTA数据结构 | C语言版】一元多项式求导
c语言·数据结构·算法
Maybyy4 小时前
力扣61.旋转链表
算法·leetcode·链表
暮鹤筠5 小时前
[C语言初阶]操作符
c语言·开发语言
卡卡卡卡罗特6 小时前
每日mysql
数据结构·算法
chao_7897 小时前
二分查找篇——搜索旋转排序数组【LeetCode】一次二分查找
数据结构·python·算法·leetcode·二分查找
lifallen7 小时前
Paimon 原子提交实现
java·大数据·数据结构·数据库·后端·算法
lixzest7 小时前
C++ Lambda 表达式详解
服务器·开发语言·c++·算法