首页/文章/ 详情

射频人学基带(12)--矩形函数

9小时前浏览2
(1)
昨天开始看杨博课程的第6讲。
第6讲的程序,就如老师在课程中所讲,是前面的程序的延伸,还是能看懂的。
但是不知怎地,感觉脑子还是处在混沌状态,对程序涉及到的时序还是懵懵懂懂,不过,应该在完成程序运行和结果分析之后,会变得清晰一点。
(2)
昨天看到扩频时钟,让我想起来矩形周期函数。
用deepseek画了一下,不同duty cycle,不同slew rate下面,矩形函数的时域和频域图形。
具体代码见文末。
程序是通过计算傅里叶系数的方式,来求频谱的。
(3)
对于硬件设计人员,可能接触的最多的,就是矩形函数带来的EMI的问题。
从频谱图中可以看到,周期矩形函数带来的频谱分量,是绵延不绝的。所以,即使是很低频的时钟信号,可能还是会对系统带来干扰,比如造成接收机灵敏度的降低,导致发射机的带外杂散超标。
保持周期不变,改变时钟的duty_cycle,会发现,各个谐波频率处的幅度会有一些变化,有的频率处会变大,有的频率处会变小。
保持周期和duty_cycle不变,改变时钟的slew rate时,可以看到当上升沿变缓的时候,高频的分量大幅度的衰减。






























































































































import numpy as npimport matplotlib.pyplot as pltfrom matplotlib.widgets import Slider
# 参数配置A = 1.0         # 幅度T = 1.0         # 周期initial_duty = 0.5  # 初始占空比initial_slew = 0.05 # 初始斜率(沿的时间占比)n_max = 80      # 最大谐波次数
# 创建双视图布局fig = plt.figure(figsize=(108))ax1 = fig.add_subplot(211)  # 频谱图ax2 = fig.add_subplot(212)  # 时域图plt.subplots_adjust(bottom=0.3, hspace=0.5)
# ====== 初始化频谱图(对数坐标) ======n = np.arange(-n_max, n_max+1)f = n / Tc_initial = A * initial_duty * np.sinc(n * initial_duty)
# 转换为分贝(添加小量避免log(0))spect_container = ax1.stem(f, 20*np.log10(np.abs(c_initial) + 1e-12),                           linefmt='b-', markerfmt='bo', basefmt=' ')
# 设置坐标轴范围和标签ax1.set(xlim=(-80,80), ylim=(-806),        xlabel='Frequency (Hz)', ylabel='Amplitude (dB)',        title=f'Spectrum with Duty={initial_duty:.2f}, Slew={initial_slew:.2f}')ax1.grid(linestyle=':', alpha=0.6)
# 提取stem组件(关键修复)stemlines = spect_container.stemlinesmarkers = spect_container.markerline
# ====== 初始化时域波形 ======t = np.linspace(-2*T, 2*T, 2000)
def generate_wave(t, duty, slew_rate):    """生成带可调斜边的梯形波"""    phase = (t % T)/T  # 归一化相位[0,1)    rise_time = slew_rate    fall_time = slew_rate    on_time = duty - (rise_time + fall_time)    on_time = max(on_time, 0)
    # 各阶段边界    rise_end = rise_time    plateau_end = rise_end + on_time    fall_end = plateau_end + fall_time
    wave = np.zeros_like(phase)    # 上升沿    mask = phase < rise_end    wave[mask] = phase[mask]/rise_time    # 平顶    mask = (phase >= rise_end) & (phase < plateau_end)    wave[mask] = 1.0    # 下降沿    mask = (phase >= plateau_end) & (phase < fall_end)    wave[mask] = 1.0 - (phase[mask] - plateau_end)/fall_time    return A * wave
wave_initial = generate_wave(t, initial_duty, initial_slew)wave_line, = ax2.plot(t, wave_initial, 'b-')ax2.set(xlim=(-2,2), ylim=(-0.1,1.1),        xlabel='Time (s)', ylabel='Amplitude',        title='Time Domain Waveform')ax2.grid(linestyle=':', alpha=0.6)
# ====== 添加双滑块 ======ax_duty = plt.axes([0.250.20.650.03])duty_slider = Slider(ax_duty, 'Duty Cycle'0.011.0, valinit=initial_duty)
ax_slew = plt.axes([0.250.150.650.03])slew_slider = Slider(ax_slew, 'Slew Rate'0.00.5, valinit=initial_slew)
# ====== 更新函数(支持对数显示) ======def update(val):    duty = duty_slider.val    slew = slew_slider.val
    # 更新时域波形    new_wave = generate_wave(t, duty, slew)    wave_line.set_ydata(new_wave)
    # 计算实际波形频谱(数值积分)    N = 1024  # 采样点数    t_fft = np.linspace(0, T, N, endpoint=False)    wave_fft = generate_wave(t_fft, duty, slew)
    # 计算傅里叶系数    c_new = []    for ni in n:        k = ni  # 谐波次数        integrand = wave_fft * np.exp(-1j*2*np.pi*k*t_fft/T)        cn = np.trapz(integrand, t_fft) / T        c_new.append(cn)    c_new = np.array(c_new)
    # 转换为分贝(添加小量避免log(0))    y_dB = 20*np.log10(np.abs(c_new) + 1e-12)
    # 更新stem图    new_segments = []    for xi, yi in zip(f, y_dB):        new_segments.append([(xi, -80), (xi, yi)])  # 基线设为-80dB    stemlines.set_segments(new_segments)
    # 更新标记点    markers.set_ydata(y_dB)
    # 保持坐标轴范围固定    ax1.set_ylim(-806)    ax1.set_title(f'Spectrum with Duty={duty:.2f}, Slew={slew:.2f}')    ax2.set_title(f'Time Domain (Duty={duty:.2f}, Slew={slew:.2f})')
    fig.canvas.draw_idle()
# 绑定事件duty_slider.on_changed(update)slew_slider.on_changed(update)
plt.show()

来源:加油射频工程师
芯片UM
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2025-05-09
最近编辑:9小时前
加油射频工程师
分享所学知识
获赞 266粉丝 98文章 622课程 1
点赞
收藏
作者推荐
未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习计划 福利任务
下载APP
联系我们
帮助与反馈