资讯详情

资讯详情

开源VNA2:1MHz-6GHz矢量网络分析仪的射频设计与C++校准实现

简介这套资源是1MHz-6GHz USB矢量网络分析仪VNA2的第二版设计包适合射频硬件工程师、嵌入式开发者以及仪器DIY爱好者学习参考。新版将RF芯片替换为性价比更高的型号供电改为5V以支持USB直接供电并利用微控制器配置FPGA免去JTAG编程器同时调整RF布局增强了端口间隔离。资源包共704个文件约10.67MB主要包含C/C固件源码.h、.cpp、FPGA的VHDL设计文件.vhd、PCB版图.brd/.sch、Qt界面文件.ui以及PDF说明、S2P测试数据等完整覆盖从硬件底层到PC端软件的各个环节。通过源码和文档可以了解FPGA与MCU的联合配置方式、USB供电下的射频电源方案以及上位机处理四个S参数的测量逻辑。已有1151人学习对想要自行搭建宽带网络分析仪或研究射频测量系统的开发者来说是一份具有较强工程参考价值的资料。1. 拆解 VNA21MHz 到 6GHz 的 USB 矢量网络分析仪到底强在哪VNA、RF、C 这三个词凑在一起基本就说明这份资源不是玩具级的 Arduino 测距仪而是一台真正能进实验室的矢量网络分析仪。VNA2 是它的第二版频率范围从 1MHz 拉到 6GHz上位机用 C 写硬件通过 USB 和电脑通信。对做天线、滤波器或者射频匹配电路的人来说这意味着不用再排队蹭实验室的台式 VNA也能在桌面上快速看 S11、S21 的扫频曲线。它的价值不在“能测”而在“这个频段范围下校准和误差修正怎么做才不至于白测”。新手拿到手先跑通软硬件链路熟手则可以直接参考它的射频前端架构和 C 处理管线省掉自己从零趟坑的时间。2. 射频前端设计从 1MHz 到 6GHz 的信号链路与四个容易失衡的环节2.1 反射桥与定向耦合器的分工为什么低频段和高频段要分开处理矢量网络分析仪的核心任务是分离入射波和反射波然后比较两者的幅度和相位。在 1MHz 到 6GHz 这个跨度里没有任何一种单一的无源结构能在全部频段都保持良好的方向性。传统微波定向耦合器基于四分之一波长耦合线在 6GHz 时四分之一波长只有几毫米做在 PCB 上没问题但到了 1MHz四分之一波长接近几十米根本不可能集成。所以第二版最常见的处理方式是分两段低频段用阻性反射桥高频段用定向耦合器或者继续用反射桥的改进型。阻性反射桥本质上是一个惠斯通电桥把三个电阻和一个被测阻抗接成桥路从电桥的不平衡端提取反射信号。它的优点是带宽极宽从直流到 6GHz 都能工作缺点是方向性通常只能做到 15 到 20dB插损约 6dB。这意味着小反射测量时会受到方向性误差的干扰但可以通过后续的校准算法补偿。很多开源 VNA 项目干脆全频段都用反射桥VNA2 这类方案在第二版里改进的是桥臂电阻的寄生参数贴片电阻在高频会有引线电感和并联电容导致桥在 4GHz 以上失衡。常见做法是用 0402 封装电阻对称布局并且在桥的两个输出端加等长微带线补偿相位。如果选择了高频段用定向耦合器耦合器的方向性和平坦度会更好但工作带宽受到耦合线结构的限制。常见做法是采用宽边耦合或多节耦合器比如三节渐变耦合器可以覆盖 2GHz 到 6GHz低频段再用反射桥接续。这样设计的好处是高频段测量精度更高坏处是两条路径之间的转换点需要额外的校准和切换开关。我更推荐直接全频段反射桥加高精度校准因为 VNA2 的使用场景大多是调试天线和匹配网络方向性误差只要校准做得扎实实际测量结果完全够用。2.2 频率合成与混频方案低中频单段扫频的取舍VNA 的接收机结构决定了动态范围和测量速度。常见的低成本方案是把反射桥输出的射频信号直接送到宽带检波器只测幅度不测相位但那不叫矢量网络分析仪。要得到相位信息必须把射频信号下变频到一个固定的中频然后通过 ADC 采集 IQ 数据。VNA2 这类 1MHz 到 6GHz 的仪器激励源和本振通常都由锁相环频率合成器产生。频率规划的关键是让本振频率始终比射频信号高或者低一个固定中频。比如中频选 10.7MHz扫频到 2GHz 时本振就设 2.0107GHz扫频到 6GHz 时本振就设 6.0107GHz。整个扫频本振必须跟着射频源同步变化这要求固件里有一个同步状态机而不是两个独立模块各扫各的。第二版常见改进是加入一个额外的低频频段直通路径1MHz 到 30MHz 直接通过运放缓冲进 ADC不再经过混频器因为混频器在极低本振频率下往往没有足够的驱动功率导致转换增益骤降。另一个取舍是镜像抑制。低中频方案镜像频率距离射频信号只有两倍中频比如射频 2GHz、中频 10.7MHz镜像就在 2.0214GHz。如果前端没有滤波镜像信号和真实信号会同时下变频并叠加。常见做法是在反射桥输出后加一个低通滤波器截止频率设在 6.5GHz 左右这样大部分频段内镜像落在滤除范围外。但低频段镜像落在通带内所以要依赖混频器本身的镜像抑制度。我在复现时习惯用双平衡混频器并仔细匹配中频端口的平衡阻抗这比额外加一个跟踪滤波器简单得多。2.3 双通道接收参考信号和反射信号同时数字化的时机矢量测量需要两个通道一路采样激励源的参考信号一路采样从被测件反射回来的信号。VNA2 这类 USB 设计通常把 ADC 放在设备端也就是混频后的两路中频信号分别经过可调增益放大器然后送给同一颗双通道 ADC。这样做的好处是两路信号共享同一个采样时钟和混频本振相位漂移被完全抵消只剩下被测件本身带来的相位差。实际操作时第一版常见的坑是两路中频放大器的增益不匹配。虽然校准可以归一化幅度但如果放大器在不同增益档位下相位延迟不一致扫频曲线就会出现不连续的台阶。VNA2 第二版通常会减少增益档位数量或者用高分辨率数字电位器做连续增益控制确保在校准频段内相位响应单调。ADC 的采样率不用太高中频如果选 10.7MHz采样率 20MSPS 甚至 25MSPS 就能满足后续在 C 软件里做数字下变频和抽取滤波。如果你打开 VNA2 的原理图会发现信号链路里还有一个容易被忽略的细节参考通道的中频信号幅度往往比反射通道大很多因为参考信号直接从激励源耦合几乎不经过衰减而反射信号经过反射桥后本身就有 6dB 以上损耗遇到低反射器件时还会更弱。所以两个通道的增益必须独立调节软件里要有自动增益控制的逻辑。常见做法是在每次扫频开始前做一次功率探测把两路中频信号的幅度都调整到 ADC 满量程的 70% 左右这样能最大程度利用 ADC 的动态范围。3. C 控制与处理软件把 IQ 采样变成 S 参数的核心路径3.1 USB 传输结构命令口与数据口分离的两种预案VNA2 通过 USB 和上位机通信传输结构直接影响扫频速度和数据完整性。最稳妥的做法是设备端实现一个复合 USB 设备一个 HID 接口用来传输命令一个批量传输接口用来传输采样数据。HID 接口的优点是操作系统自带驱动不需要额外安装而且数据包有握手命令不容易丢失缺点是传输速率低不适合大数据量。批量传输接口则相反速度可以到 40MB/s 级别但驱动安装稍微麻烦一点。我在实际使用这类资源时发现很多第二版工程会把命令和数据都塞进批量传输端点省掉 HID理由是 Windows 和 Linux 下用 libusb 操作批量端点非常直接。这种做法的问题是设备枚举时如果被系统识别为未知设备需要手动安装驱动。如果资源包里提供了驱动签名的 INF 文件那问题不大如果没有调试时就会卡在这一步很久。我的建议是保留 HID 做低速命令通道比如设置频率、设置功率、触发单次扫描同时用批量端点传输 IQ 数据。C 代码里用两个线程分别处理能避免扫频过程中命令排队拥堵。代码骨架可以这样组织// usb_link.h class UsbVna { public: bool init(); // 枚举设备打开接口 bool send_cmd(uint8_t cmd, uint32_t arg); bool read_iq_block(std::complexfloat* buf, size_t count); private: void* hid_handle; // 命令通道 void* bulk_handle; // 数据通道 };这个类把 USB 通信抽象成两个独立的通道。init 函数里会分别打开 HID 和批量接口如果批量接口打开失败就回退到纯 HID 模式只是扫频速度会慢。send_cmd 函数用来下发设置命令比如设置起始频率、终止频率、扫描点数以及驻留时间命令数据包只有 64 字节走 HID 完全够用。read_iq_block 则从批量端点读取一块复数 IQ 数据。第二版工程资源里经常看到类似的抽象层这比把 USB 底层操作散落在业务代码里要清晰得多。3.2 扫频状态机频率驻留、中频滤波与平均次数的配平扫频逻辑并不是简单地逐个设置频率然后立即读数据。射频前端和混频器在频率跳变之后需要一段建立时间中频滤波器也需要积累足够的周期才能给出稳定输出。如果驻留时间太短测得的数据点会叠加瞬态响应S11 曲线上出现毛刺如果驻留时间太长扫一次全频段的耗时无法接受尤其是设置了 4001 个扫描点的时候。一个常用的经验公式是驻留时间取中频滤波器带宽倒数的五到十倍。比如中频带宽设为 1kHz那么对应的建立时间至少是 5ms。VNA2 的软件里通常会把中频带宽的档位和驻留时间绑定而不是让用户单独设置。实际扫频状态机可以拆成几个阶段跳频、功率稳定、中频滤波器充电、采样、切换下一个频点。C 里可以用一个有限状态机来实现也可以用最简单的 for 循环加 sleep。// 扫频参数表 struct SweepConfig { double start_hz; double stop_hz; int points; int if_bw_hz; // 中频带宽 int averages; // 重复平均次数 }; void run_sweep(UsbVna* vna, const SweepConfig cfg, std::vectorstd::complexfloat s11_vec) { double step (cfg.stop_hz - cfg.start_hz) / (cfg.points - 1); uint32_t dwell_us 1000000 / cfg.if_bw_hz * 10; // 10倍时间常数 for (int i 0; i cfg.points; i) { double f cfg.start_hz step * i; vna-send_cmd(CMD_SET_FREQ, (uint32_t)f); std::this_thread::sleep_for(std::chrono::microseconds(dwell_us)); std::complexfloat ref, meas; vna-read_iq_block(ref, 1); vna-read_iq_block(meas, 1); s11_vec[i] meas / ref; // 原始未校准反射系数 } }这段代码有三个值得注意的地方。C 标准库里的std::complexfloat可以直接存储 IQ 数据复数除法的结果就是反射系数的幅相表示。驻留时间用微秒级整数计算避免浮点数换算的精度问题。扫频步进用points - 1做分母保证起始频率和终止频率都能落到扫描点上很多第一版资源在这里写错导致最后一点频率比设定值低一个步长。3.3 从时域 IQ 到频域 S11标准复数运算的落地写法ADC 采样到的中频信号在时域上是正弦波幅度和相位信息分别落在 I 和 Q 两个分量上。经过数字下变频之后IQ 数据就变成了一个复数序列实部是 I 路虚部是 Q 路。要得到单个频点的反射系数需要对这个复数序列做平均或者低通滤波提取直流分量。VNA2 上位机里最常见的做法是对采样序列做相干累加也就是把 N 个复数直接相加再除以 N这样可以有效抑制随机噪声。平均次数的选择要看中频带宽和期望的动态范围。中频带宽越窄滤波器抑制噪声的能力越强但扫描时间越长平均次数越多随机噪声降低越明显但同样耗时。常见做法是把中频带宽设为几个档位100Hz、1kHz、10kHz、100kHz平均次数在 1 到 16 次之间可调。对于 6GHz 频段的天线调试1kHz 带宽加 4 次平均已经能稳定测出 -40dB 左右的反射系数。std::complexfloat extract_center_iq(const std::complexfloat* buf, size_t n) { std::complexdouble acc(0, 0); for (size_t i 0; i n; i) { acc buf[i]; } return static_caststd::complexfloat(acc / static_castdouble(n)); }这段代码的逻辑是纯矢量运算没有任何窗函数处理因为在扫频式 VNA 里每个频点是稳态信号不需要像 FFT 分析仪那样做加窗抑制泄漏。实际使用中如果发现噪声偏大先不要急着增加平均次数而是检查中频带宽是否设置得太宽。我遇到过一个典型情况USB 供电的 VNA 在 5GHz 以上噪声明显变大换了个独立供电的 hub 就好了这说明电源纹波对 ADC 的影响远大于软件平均次数的提升效果。4. 校准SOLT 误差模型为什么会在 6GHz 附近“失真”4.1 误差盒模型与三个待解误差项所有 VNA 测量到的原始 S11 都不是真实值而是经过系统误差污染之后的值。单端口反射测量可以抽象成一个误差盒模型包含三个误差项方向性误差 E00、源匹配误差 E11、反射跟踪误差 E10E01。方向性误差来自反射桥本身有限的方向性即使接一个完美的 50Ω 负载反射通道也会测到一点残余信号。源匹配误差来自激励源输出阻抗不完全匹配导致被测件的反射信号在源端被二次反射。反射跟踪误差则是参考通道和反射通道增益不一致造成的幅度和相位偏差。这三个误差项和真实反射系数之间的数学关系是S11m E00 (E10E01 × S11a) / (1 - E11 × S11a)其中 S11m 是测量值S11a 是真实值。只要在测量前分别接上三个已知反射系数的标准件测出对应的 S11m就能反解出 E00、E11 和 E10E01。这就是 SOLT 校准里 S短路、O开路、L负载三个标准件的用途。校准之后任意被测件的真实 S11a 可以通过求解上式得到。高频段的麻烦在于标准件的反射系数不再等于理想值。理想开路是反射系数 1但实际开路标准件在 6GHz 时存在边缘电容反射系数会偏离 1短路标准件有残余串联电感同样偏离 -1。如果校准软件不修正这些寄生参数校准后测一个标准的 50Ω 负载会看到曲线上下漂移。第二版工程的校准代码里必须有每个标准件的频率响应模型通常是用电容或电感量级结合电长度来定义的。4.2 标准件定义开短路模型的寄生参数修正实际校准件定义包含两个部分参考面偏移和寄生参量。参考面偏移指的是短路和开路标准件作为物理器件它的实际短路点或开路点与连接器参考面之间存在一小段传输线这可以用一个电长度来表示。寄生参量则是开路端的边缘电容和短路端的残余电感。在低频段这些参量影响很小可以忽略但到了 6GHz一个 0.3pF 的边缘电容就会让开路标准的反射系数从 1 变成约 0.87 的相位偏移。C 里实现校准件模型时通常用一个类封装三个标准的表现struct CalStandard { double offset_length_m; // 参考面偏移长度 double offset_loss; // 偏移损耗单位奈培/米 std::complexdouble gamma_open(double f) { double cap 0.15e-12; // 开路边缘电容典型值 std::complexdouble z(0, -1.0 / (2 * M_PI * f * cap)); std::complexdouble z0(50, 0); return (z - z0) / (z z0); } std::complexdouble gamma_short(double f) { double ind 0.05e-9; // 短路残余电感典型值 std::complexdouble z(0, 2 * M_PI * f * ind); std::complexdouble z0(50, 0); return (z - z0) / (z z0); } };这里的关键参数是开路边缘电容和短路残余电感它们的数值必须和实际校准件的物理结构匹配。不同品牌的开路头边缘电容差异可以很大从 0.1pF 到 0.5pF 都有。如果你在 5GHz 以上校准后测开路出现负反射系数多半是电容模型设置得过小或过大。高频校准失败时先不要怀疑硬件把标准件模型换一组参数再试一次。4.3 C 误差修正实现从校准件测量到待测件还原校准流程在代码里分两步第一步采集三个标准件的原始 S11m第二步代入公式求解误差项。为了数值稳定性通常不直接解那个有理式而是构造线性方程组。定义中间变量后三个标准件分别提供一组方程用高斯消元或矩阵求逆求解struct SinglePortCal { std::complexdouble e00, e11, e10e01; void compute(const std::vectordouble freq, const std::vectorstd::complexdouble m_open, const std::vectorstd::complexdouble m_short, const std::vectorstd::complexdouble m_load) { // 理想负载时S11a0所以测量值 e00 m_load e00 m_load[0]; // 其余两个误差项由开路/短路测量值反解 std::complexdouble go gamma_open(freq[0]); std::complexdouble gs gamma_short(freq[0]); std::complexdouble denom m_open[0] - m_short[0]; e11 ( (m_open[0] - e00) / go - (m_short[0] - e00) / gs ) / ( (m_open[0] - e00) / go * gs - (m_short[0] - e00) / gs * go ); e10e01 (m_open[0] - e00) * (1 - e11 * go) / go; } std::complexdouble correct_s11(std::complexdouble m) { std::complexdouble diff m - e00; return diff / (e10e01 e11 * diff); } };这段代码展示了单点频率的校准计算实际工程需要在外层再套一层频率循环逐频点计算。e00 等于负载测量值是个很漂亮的简化因为负载标准真实反射系数接近 0所以在误差模型里可以直接读取。但要注意6GHz 时负载标准件的残余反射不再是零用理想 0 会造成方向性校准不精确。VNA2 这类工程里通常会额外提供一个可选的滑动负载校准或者用多次连接负载取平均来减少随机误差。还有一个实用技巧校准之后立即用开路标准回测一次观察测量值与理想模型之间的偏差。这个偏差直接反映校准质量。在 C 里可以写一个verify_calibration函数计算回测开路和短路的复误差。如果偏差超过一定阈值自动提示重新校准。从实际经验看6GHz 频段下校准质量受连接器重复性影响很大每次重新连接标准件都会引入微小的相位变化所以校准标准件需要使用高质量的连接器并且尽量使用扭矩扳手。5. 避坑笔记6GHz 测量中反复翻车的 5 个排错记录5.1 现象→原因→解决五个真实高发坑坑一1MHz 附近 S11 测出正增益现象接一个 50Ω 负载低频段 S11 显示 -2dB 甚至 -1dB比 0dB 还高看起来像有源器件。原因反射桥的低频响应受隔直电容影响。为了给后级放大器提供直流偏置很多设计会在信号路径上串隔直电容电容和桥臂电阻形成高通滤波导致 1MHz 附近反射通道幅度失真。解决把隔直电容容值加大到微法级别或者改用无源偏置结构。我在复现时换了 1uF 的陶瓷电容并联 0.1uF 高频电容低频段响应立刻恢复正常。软件层面不要试图通过校准修正这种频率相关的幅度塌陷很难用误差模型精确补偿。坑二4GHz 以上 S11 曲线出现周期性波纹现象扫频曲线在 4GHz 以上叠加了周期约 300MHz 的波纹幅度 0.3dB 左右。原因反射桥到 ADC 混频器之间的信号走线太长且没有做阻抗匹配形成了驻波。波纹周期对应走线电长度的两倍300MHz 周期意味着电长度约 0.5m这在 PCB 上显然不可能很可能是连接线缆造成的。解决把反射桥输出到混频器输入的线缆换成半刚性同轴尽量缩短长度。如果走线在 PCB 上需要保证特性阻抗 50Ω 并有足够的过孔接地。坑三校准后测短路出现螺旋状偏差现象校准完成回测短路标准反射系数幅度接近 1但相位在 5GHz 以上偏离理想 -1 超过 20 度。原因校准件模型里的参考面偏移长度设错了。相位偏差随频率线性增长典型就是电长度误差。解决在主控软件里加一个端口延时的微调参数把回测相位偏差拟合出一条直线斜率就是延时误差。用这个误差值修正校准件模型再重新校准通常一步就能修平。坑四中频带宽从 10kHz 改成 100Hz 后扫描曲线整体偏移现象窄带模式下测量的 S11 幅度和宽带模式下差 0.5dB。原因中频滤波器带宽改变会引入不同的群延迟如果增益放大器的相位响应不是完全平坦带宽变化就导致相位偏移。虽然校准能够部分修正但校准件套件里若只保存单一带宽下的误差项切换带宽后修正就会失效。解决每个中频带宽档位单独做一次校准或者在软件里对相位做群延迟补偿。VNA2 上位机参数里通常有一项 IF Bandwidth切换时必须确认当前校准数据对应的带宽值。坑五USB 连接下扫到一半曲线出现断点现象扫频曲线大部分正常但随机几个频点数据为 0 或者明显跳变。原因上位机读取数据超时。USB 批量传输在高负载和系统休眠策略下可能出现瞬间断流而固件侧没有重新发送机制。解决在批量读取代码里加超时重试逻辑超时后重新发起一次读请求同时把每批读取的复数个数控制在 256 个以内减少单次传输的数据量。我习惯用 128 或 256 作为块大小扫频结束后再拼接完整曲线。6. 验证与进阶把校准结果做成可信的测量数据6.1 三个低成本验证动作拿到校准后的 VNA 数据时第一件事不是急着测待测件而是验证系统本身的可信度。手里不需要高级验证件用校准套件里的短路和开路标准就能做基础验证。回测开短路时记录每个频点的反射系数和理想模型之间的误差。如果幅度偏差在 0.1dB 以内、相位偏差在 3 度以内这套校准可以进入正式使用。如果偏差较大回到第五章节的避坑记录里依次排查。第二个验证动作是用一段已知长度的同轴线缆加一个精密负载测这段线缆的输入阻抗。初学者常忽略的是线缆本身的损耗和相位延迟会随频率变化直接用理想公式去反推长度往往对不上。更好的做法是测量 S11 后做 IFFT 变换到时域观察反射脉冲的位置。在 C 里可以借助 FFT 库把频域复数数据补零到 4096 点再反变换得到的时域波形里能看到线缆末端负载的反射峰。通过反射峰的时延换算线缆长度和物理实际长度对比偏差应该在几个百分点以内。第三个动作是测一个已知衰减器的 S21。虽然抛出的标题是 S11 为主但 VNA2 这类设备通常会提供传输测量功能。软件里加一个直通校准后测一个 10dB 衰减器观察 1MHz 到 6GHz 的插损波动范围。合格的衰减器在工作频带内插损波动应该在 0.5dB 以内。如果波动明显可能是端口之间的串扰或校准件直通延迟没有完全消除。这些验证动作不需要额外购买昂贵仪表完全用配套软件和校准件就能完成。从那以后我每次拿到这类 VNA 资源都会强制在正式测试前走一遍开短路回测、时域反射定位和衰减器插损这三个验证动作确认校准质量过关才敢把数据写进报告。希望这些经验能帮你把 VNA2 的每一分硬件性能都榨出来。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →