资讯动态

从数字下变频到频谱分析:FPGA实现软件无线电接收链路

发布时间:2026/9/15 19:57:54 来源:尧图企业网站定制
简介面向软件无线电与FPGA课程设计的综合学习包适合通信工程、电子信息类学生或工程师从基础到实践逐步掌握SDR。内容覆盖软件无线电原理、射频前端、数字信号处理、FPGA实现及项目案例针对AM/FM接收、常见无线通信协议、USRP和LimeSDR平台开发等场景配有原理讲解与设计思路可帮助读者理解调制解调、滤波器、FFT/DFT等关键算法并上手FPGA编程。压缩包约22.06MB具体文件总数与类型明细暂未由上游提供下载后可按课程模块逐步查阅。目前已有409人学习下载适合作为课程设计、毕业设计或自学的参考资料既能补强理论知识也能借助其中案例提升Verilog/VHDL编码、Vivado/Quartus工具使用和Gnu Radio/MATLAB仿真等实操能力。1. 软件无线电课程设计与 FPGA 实现的切入点先看数字下变频软件无线电SDR课程设计里最容易卡住的地方不是不会配 GNU Radio 的 block而是不知道一个从天线进来的信号经过 ADC 之后在 FPGA 内部究竟以什么格式流动。很多教程把 FPGA 的 FFT 当作黑盒直接实例化然后读谱结果频率轴对不上、数据抖动、直流分量抬高等问题层出不穷。作为软件无线电课程的核心工程上的主线是射频前端的 I/Q 数据进 FPGA 后先用数字下变频DDC把采样率降到基带再用 FFT 做频谱分析最后通过 USRP 硬件验证。这篇文章按这个顺序展开给出可复现的 Verilog 代码和 GNU Radio 流程适合通信工程高年级做 FPGA 课程设计也适合从纯软件转向 SDR 硬件开发的工程师作为入门参考。2. SDR 架构与 FPGA 的角色从零中频到数字中频的 HDL 实现2.1 射频前端架构对比与选择SDR 接收链路中射频前端决定了送入 FPGA 的是什么形式的数据。传统超外差接收机会经过两次模拟混频镜像抑制靠声表面波或陶瓷滤波器体积和成本都不友好。零中频架构把本振频率直接设在射频频率上一次混频就得到 I/Q 基带信号没有镜像频率问题代价是直流偏移、本振泄漏以及 1/f 噪声抬升。低中频架构先把射频搬到几十 MHz 的中频再由 ADC 采样后续镜像抑制交给 FPGA 里的复数混频和数字滤波。USRP B210、LimeSDR 这类平台普遍采用零中频或低中频方案因此 FPGA 内部必须承担一部分数字信号处理而不能只做数据搬运。架构ADC 采样频率镜像抑制手段FPGA 主要任务常见平台零中频信号带宽量级如 220 MS/sI/Q 校准、模拟调零DC 偏置估计、I/Q 不平衡校正USRP B210, LimeSDR低中频中频的 24 倍如 80 MS/s第二次数字混频数字下变频、镜像抑制滤波窄带接收机、部分 SDR 前端数字中频中频的 4 倍以上数字滤波与抽取宽带数字混频、多通道抽取雷达接收、5G 基站课程设计里选平台时不要只看最大采样率还要看 ADC 量化位宽和接口瓶颈。B210 的 ADC 是 12 位实际有效位数可能不到 10 位所以你在 FPGA 里把数据位宽做到 24 位并不会提升动态范围反而白白消耗逻辑资源。设计前先确认前端输出位宽再决定后面各级的处理位宽这才是硬件工程师该有的习惯。2.2 DDC 模块划分NCO、混频器与抽取滤波数字下变频本质上是“乘法加滤波加抽取”。NCO数控振荡器产生正交本振信号与 ADC 输入相乘得到 I/Q 分量再经过抽取滤波器把采样率降到符号速率附近。FPGA 实现时NCO 可以直接用 Xilinx DDS IP 核或 Altera NCO IP 核抽取器最常用 CIC级联积分梳状滤波器因为它只用到加法器和寄存器几乎不消耗乘法器适合高倍抽取后续整形滤波再用 FIR Compiler IP 核补偿 CIC 的频谱滚降。这条链路是软件无线电课程设计的主干你的 Verilog 代码里最容易被忽略的是不同模块之间的位宽匹配。DDS 输出位宽、乘法器输出位宽、CIC 输出位宽每一级都要做截位或饱和处理否则量化误差会一路累积到最终的频谱里。CIC 抽取滤波器的输出位宽计算公式在 Xilinx 的 CIC Compiler IP 核配置界面里会自动给出但你要理解它为什么比输入位宽大很多。级联积分器会放大直流增益所以位宽必须预留增长空间否则滤波结果溢出后表现为波形削顶。拿到 IP 生成的输出位宽之后再根据后级 FFT 需要的位宽做算术右移这一步是很多人出错的地方。2.3 Verilog 实现数字下变频骨架下面是一段可综合的 DDC 骨架代码省略了复位时序和寄存器配置重点展示数据流方向。module ddc_top #( parameter DIN_WIDTH 16, parameter NCO_WIDTH 18 )( input wire clk, input wire rst_n, input wire signed [DIN_WIDTH-1:0] adc_data, output reg signed [23:0] i_out, output reg signed [23:0] q_out ); wire signed [NCO_WIDTH-1:0] sine; wire signed [NCO_WIDTH-1:0] cosine; wire signed [DIN_WIDTHNCO_WIDTH-1:0] mix_i; wire signed [DIN_WIDTHNCO_WIDTH-1:0] mix_q; wire signed [19:0] i_tmp; wire signed [19:0] q_tmp; dds_compiler_0 u_dds ( .aclk(clk), .s_axis_phase_tvalid(1b1), .s_axis_phase_tdata(20d0), // 频率控制字由寄存器写入 .m_axis_data_tdata({sine, cosine}) ); assign mix_i adc_data * sine; assign mix_q adc_data * cosine; cic_decimator_0 u_cic_i ( .aclk(clk), .s_axis_data_tvalid(1b1), .s_axis_data_tdata(mix_i), .m_axis_data_tdata(i_tmp) ); cic_decimator_0 u_cic_q ( .aclk(clk), .s_axis_data_tvalid(1b1), .s_axis_data_tdata(mix_q), .m_axis_data_tdata(q_tmp) ); always (posedge clk or negedge rst_n) begin if (!rst_n) begin i_out 24d0; q_out 24d0; end else begin i_out $signed(i_tmp) 4; // 右移 4 位等效除以 16 q_out $signed(q_tmp) 4; end end endmodule这里两个 IP 核没有写端口位宽细节实际例化时要以 Vivado 生成的配置为准。dds_compiler_0的m_axis_data_tdata在配置成 Sine and Cosine 输出时高位是正弦、低位是余弦拆出来之后和 ADC 数据做有符号乘法。s_axis_phase_tdata的位宽是相位累加器位宽0 相位表示本振频率为 0实际频率由上层写入的频率控制字决定。CIC 输出i_tmp的宽度是 20 位这是 CIC Compiler 根据输入位宽、抽取倍数和级数自动计算的。最后右移 4 位是为了把数据对齐到后续 FFT 的 16 位输入范围具体右移多少应通过仿真观察如果发现信号幅度太小就减少移位数发现溢出就增加移位数。3. 软件无线电课程设计中的 FFT IP 核参数选择与频谱分析3.1 点数、位宽和输出顺序的取舍FPGA 课程设计里做频谱分析第一反应是调用 Xilinx FFT IP 核但参数没选对会导致后面所有数据都是乱的。变换点数Transform Length直接影响频率分辨率假设 ADC 采样率 4 MS/s经过 DDC 抽取后降到 1 MS/s做 4096 点 FFT分辨带宽约为 244 Hz足够分辨 AM 广播载波。继续增大点数到 16384分辨率确实提高到 61 Hz但 BRAM 消耗成倍上升处理延迟也增加。数据位宽方面输入来自 DDC 输出通常保持 16 位或 24 位FFT IP 核的旋转因子位宽Phase Factor Width默认 16 位对大多数课程设计足够如果发现频谱本底噪声异常抬高优先检查这个参数而不是盲目增加采样点数。输出顺序推荐选择 Natural Order这样输出索引直接对应频率正序不需要额外倒序逻辑也方便在 ChipScope 里肉眼核对谱线位置。参数典型值性能影响容易踩的坑Transform Length1024 / 4096 / 16384频率分辨率 fs / NN 越大延迟越高Input Data Width16 / 24反映输入动态范围截位后频谱出现假峰Phase Factor Width16 / 24旋转因子精度过小导致本底噪声抬高Output OrderNatural Order输出索引对应频率顺序选错后频点全部乱序Cyclic Prefix Insertion关闭仅 OFDM 需要普通频谱分析必须关闭3.2 用 Vivado 配置 FFT IP 核并实例化在 Vivado 工程里点击 IP Catalog搜索 FFT选择 Fast Fourier Transform 7.2。Configuration 页面把 Transform Length 设为 4096Input Data Width 设为 16Phase Factor Width 保持 16Architecture 选择 R2^2 以节省逻辑资源。对于课程设计目标时钟 100 MHz、输入连续数据流可以选择 Pipelined Streaming 模式。生成后在顶层模块里这样例化wire [15:0] fft_s_axis_data_tdata; wire fft_s_axis_data_tvalid; wire fft_s_axis_data_tlast; wire [31:0] fft_m_axis_data_tdata; wire fft_m_axis_data_tvalid; fft_4096 u_fft ( .aclk(clk), .s_axis_config_tdata(16h0001), // 01 表示 FFT00 表示 IFFT .s_axis_config_tvalid(1b1), .s_axis_data_tdata(fft_s_axis_data_tdata), .s_axis_data_tvalid(fft_s_axis_data_tvalid), .s_axis_data_tlast(fft_s_axis_data_tlast), .m_axis_data_tdata(fft_m_axis_data_tdata), .m_axis_data_tvalid(fft_m_axis_data_tvalid), .event_frame_started(event_frame_started) );s_axis_data_tdata的格式是{虚部[15:0], 实部[15:0]}。如果只做实数信号的实数谱虚部可以填 0但要注意这不会节省硬件资源因为 FFT 核仍然按复信号处理。s_axis_config_tdata的低两位是模式控制01表示 FFT00表示 IFFT其他位用来配置缩放因子。缩放因子可以在 IP 配置里设为自动也可以手动指定课程设计推荐使用 Block Floating Point这种模式会根据每帧数据的最大值自动调整缩放避免溢出且精度稳定。tlast是很多人漏掉的信号它标记一帧数据的最后一个点Pipelined Streaming 架构必须正确拉高否则 IP 核永远不会输出结果。写一个计数器在有 4095 个有效输入数据时把tlast拉高一个周期这是最稳妥的做法。3.3 从频域数据到信号检测幅度谱计算与阈值判断FFT IP 核输出的是复数谱要得到幅度谱需要做sqrt(I^2Q^2)。在 FPGA 里常用 CORDIC IP 核的 Translate 模式直接输出幅度省掉乘法器和开方逻辑。如果只是判断信号有无可以用abs(I) abs(Q)近似误差约 0.7 dB对付课程设计足够了。检测逻辑可以这样组织用计数器产生帧同步tlast触发状态机读取m_axis_data_tdata。拆出 I、Q送入 CORDIC 得到幅度或者做绝对值和。把幅度与阈值比较超过阈值拉高detect_flag。记录峰值所在的 bin 索引换算频率f bin_index * fs / N。阈值不能设成固定值。接收机增益变化或者前端噪声波动都会改变底噪建议取前 100 帧的幅度谱做平均作为噪声基底然后取基底加 6 dB 作为检测门限。这个做法在答辩时是加分项它说明你理解了信号检测与虚警概率的关系而不是简单写死一个常数。4. 基于 USRP 与 GNU Radio 的 SDR 链路验证4.1 USRP 硬件平台与 FPGA 数据交互USRP B210 是学生项目里最常见的 SDR 平台内部有一片 Xilinx Spartan-6 FPGA负责射频收发、DDC/DUC 和 USB 3.0 接口。课程设计里你的 Verilog 代码最终要跑在硬件上而 GNU Radio 运行在宿主机 CPU 上二者通过 UHD 驱动通信。设计参数前先做数据率预算B210 的 ADC 最大采样率 61.44 MS/s但 USB 3.0 实际可用带宽大约 200 MB/s因此 16 位复数采样最多能跑到 6 MS/s 左右。如果把采样率设成 30 MS/s数据必然丢失现象是频谱出现周期性断裂。计算方法是复数 IQ 每个采样占 4 字节I、Q 各 16 位6 MS/s × 4 字节 24 MB/s这只是理论值实际还要留出协议开销。平台可调带宽最大采样率FPGA 型号接口USRP B20056 MHz61.44 MS/sSpartan-6 LX75USB 3.0USRP B21056 MHz61.44 MS/sSpartan-6 LX150USB 3.0LimeSDR61.44 MHz61.44 MS/sCyclone IV EUSB 3.0如果只是想验证 FPGA 的 DDC 和 FFT 链路不一定要直接跑满采样率。把 USRP 的采样率降到 1 MS/s中心频率设在 93.8 MHz 附近的 FM 电台就能获得稳定的窄带信号。这样 FPGA 里的数据率只有 1 MS/sFFT 的帧率约 244 fps在 ILA 里可以清楚看到频谱峰值。4.2 GNU Radio Companion 构建 FM 广播接收机用 GNU Radio 验证 SDR 链路时不要从零搭建 OFDM 系统最简单且能覆盖整个接收过程的例子是 FM 广播接收。在 GRC 里拖出 Osmocom Source、WBFM Receive、Audio Sink 和 Variable 即可。关键参数源频率设为真实电台频率采样率设为 2.048 MHzWBFM Receive 的 Quadrature Rate 等于源采样率Audio Decimation 设为 10输出音频率约 204.8 kHz。如果主机没有 USRP可以用 RTL-SDR 替代V3 版本采样率上限 2.4 MHz也能完成实验。下面是用 Python 的 GNU Radio 流图等价于 GRC 自动生成的逻辑from gnuradio import gr, analog, audio import osmosdr class fm_rx(gr.top_block): def __init__(self): gr.top_block.__init__(self, fm_rx) freq 93.8e6 samp_rate 2.048e6 self.rtl_source osmosdr.source(argsnumchan1) self.rtl_source.set_sample_rate(samp_rate) self.rtl_source.set_center_freq(freq, 0) self.rtl_source.set_gain(40, 0) self.wbfm_rx analog.wfm_rx( audio_rate32e3, quad_ratesamp_rate, tau75e-6, max_dev75e3) self.audio_sink audio.sink(32000, 0, False) self.connect(self.rtl_source, self.wbfm_rx, self.audio_sink) def main(): tb fm_rx() tb.start() input(Press Enter to stop) tb.stop() if __name__ __main__: main()这段代码里analog.wfm_rx的tau75e-6是去加重时间常数欧洲 FM 广播用 50 µs美国和中国大多用 75 µs用错听起来声音发闷。max_dev是最大频偏 75 kHz与 FM 广播标准一致。如果你希望把 FM 解调后的信号与 FPGA 的频谱检测结果对比可以在 GNU Radio 里加一个能量检测块输出到一个 ZMQ Push Sink然后由 Python 脚本读取。这样宿主机上的检测结果和 FPGA 上的检测结果互相印证整个课程设计就闭环了。4.3 MATLAB 与 FPGA 联合仿真的数据格式对齐很多软件无线电课程设计会用 MATLAB 生成测试文件然后交给 ModelSim 或 Vivado Simulator 仿真。这里有个稳定的做法先用 MATLAB 生成含调制信号的文本文件再用 Verilog testbench 的$readmemh读入。关键问题是你必须把 MATLAB 的 double 数据量化成定点数格式要和 FPGA 内部一致。例如 DDC 输入是 16 位有符号数量化公式是qi round(real_signal / max(abs(real_signal)) * 32767)然后写入文本时保留补码格式。不要直接把浮点数存成文本仿真器读进来位宽和符号位全不对波形看起来就是乱的。仿真结束后把 FPGA 的 FFT 输出或 DDC 输出写回文件用 MATLAB 的xcorr与理想结果做相关分析。相关系数大于 0.99 说明链路没有系统性错误如果相关度低优先查混频器频率字是否配错、截位方向是否正确、以及tlast计数是否偏移一个时钟周期。这步比对能帮你把算法错误和硬件实现错误区分开。5. 软件无线电与 FPGA 调试双音测试定位截位与同步错误5.1 用 ILA 抓取 FFT 输出的关键信号调试 FPGA 内的 SDR 链路我习惯先把 ILAIntegrated Logic Analyzer挂到 FFT 的输出总线上触发条件设为m_axis_data_tvalid拉高采样深度设为 4096 个点。这样能直接看到一帧 FFT 输出的实部序列如果发现 0Hz 附近有一个巨大值说明 DDC 没有做直流偏置消除如果谱峰出现在完全错误的 bin 上说明tlast计数器位宽和 FFT 点数不匹配或者数据不是按帧对齐的。ILA 的触发位置放在tvalid比放在tlast更容易观察连续输出因为tvalid拉高后数据就是连续有效的。5.2 双音测试的设计方法双音测试是验证截位是否正确的高效手段。向 DDC 输入两路频率接近的正弦波例如在 1 MS/s 采样率下输入 100 kHz 和 102 kHz 的双音信号。经过 4096 点 FFT频率分辨率为 244 Hz两谱线相隔 8 个 bin。正常情况下频谱上只有两根谱线如果看到两根谱线旁边出现明显的互调分量且分量位置在 98 kHz 和 104 kHz 处说明混频器或 FFT 输入存在非线性截位。把截位位数从 4 位调整到 6 位再仿真互调分量应该明显下降。在 ILA 里观察双音测试的输出时记录两个谱线的幅度比。如果幅度比与输入不一致可能是 CIC 滤波器通带在 100 kHz 附近有滚降也可能是右移位数造成小信号被截掉。此时先查 CIC 的频率响应再看截位。双音测试的优点是不需要真实天线信号源使用 FPGA 内部 DDS 即可整个测试在仿真阶段就能完成等仿真通过后再把同样的激励通过 USRP 的 DAC 发送做硬件闭环验证。这个习惯能让你在课程设计答辩现场快速证明系统究竟哪里出了问题。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价