开发人员 Jan Dvořák 设计了最便宜的软件定义无线电 (SDR) 之一:1只需要1块 Raspberry Pi Pico 开发板、1个电阻器、1个电容器和1个合适的天线。
“我以前从未制作过无线电,”Jan Dvořák在谈到该项目的起源时写道。“作为一名软件开发人员,我曾参与过网络应用程序的开发。甚至使用 Wi-Fi 或蜂窝网络的应用程序也是如此。但我从未真正理解过这些无线电在软件与电磁场相遇时的工作原理。在发射方面,它最终变得非常简单。只需在正确的频率上切换 GPIO 引脚,将一段电线作为天线连接起来,就可以开始广播。但接收器很难。”
只需两个无源元件、一根天线和一个 Raspberry Pi Pico 就足以构建一个软件定义的无线电接收器
Dvořák 首先研究了其他人做过的事情,并找到了使用FPGA和少量无源元件的项目。“唯一的问题是他们的FPGA总是有一个高速比较器,用作一位ADC”,Dvořák 指出。“而我目前喜欢摆弄的芯片没有这个,那么如何使用 RP2040 构建数字无线电接收器呢?”
RP2040是一款售价1美元的微控制器,是售价4美元的Raspberry Pi Pico的核心,它有一个板载模拟数字转换器,但 500kHz 的采样率不足以用于无线电工作。该芯片还具有可编程输入/输出 (PIO) 块,能够创建可控制输入/输出引脚的状态机 — 正是这一点提供了项目所需的性能,借助充当简单低通滤波器的电容器,它发挥了一点作用。
通过使用 RP2040的PIO块(而不是 ADC),Dvořák 能够创建一个直接采样接收器 - 这意味着混合和过滤由软件而不是硬件处理,运行在RP2040的Arm Cortex-M0+ 微控制器内核上。“原始IQ样本通过USB CDC发送到我的 PC,”这位创客解释说,“由一个小型 Python 脚本拾取,转换为TCP流并使用GNU Radio进行处理,它提供了一个名为 GNU Radio Companion的出色图形应用程序,可用于构建信号处理管道并将音频输出到系统扬声器。”
完整的项目记录可在 Dvořák 的博客上找到,源代码可在 git 存储库中找到,并遵循完全开放的公共领域许可证。“是的,它还是有很多噪声,”开发人员承认,“但你能用 1 美元的通用微处理器、两个无源元件和一个天线做到这一点,这难道不很酷吗?”
原文: Pico SDR
我之前从来没有制作过收音机。
作为一名软件开发人员,我曾开发过网络应用程序。甚至使用 WiFi 或蜂窝网络的应用程序。但我从未真正理解过这些无线电在软件与电磁场相遇时的工作原理。
我研究过一些频率较低的 DIY 业余产品。似乎每个人都在制作“曼哈顿风格”的模拟电路板。这让我很失望,因为我的手很笨拙,没有耐心仔细地“彻底”焊接零件,最重要的是,我觉得没有可编程控制器的电路不够有吸引力。
在传输方面,它最终变得非常简单。只需在正确的频率上切换 GPIO 引脚,将一段电线作为天线连接起来,然后开始广播。除了所需的信号外,您还会用大量不需要的谐波和混频产物污染频谱,因此请添加滤波器或使用更多引脚来调整信号,使其更接近正弦波。还有很多事情要做,但大部分复杂性在于接收端。
接收器很难
如何构建数字接收器?我说的不是购买现成的模块并将其连接到 Arduino。如何从头开始使用通用微控制器构建接收器?
令人惊讶的是,之前已经多次做过并记录过 。一些超级聪明的人实际上已经使用 FPGA 和一些无源器件制作了无线电接收器,为我们留下了线索。
唯一的问题是他们的 FPGA 总是有一个高速比较器用作 1 位 ADC。而我目前喜欢摆弄的芯片没有这个。那么如何使用RP2040(Raspberry Pi Pico 的核心)构建数字无线电接收器呢?
射频ADC
使用比较器作为 ADC 的一部分非常常见。将它与 DAC 结合以生成电压进行比较,您可以使用二进制搜索来测量输入电压,就像 RP2040 使用其内置 ADC 一样。或者在比较器切换时生成快速、众所周知的斜率和时间。或者向输入注入特定噪声并平均一堆样本。
遗憾的是,我们不能使用内置 ADC,因为它的采样率只有 500 kHz,超频后性能会迅速下降。它的输入带宽也很差,任何高频信号最终都会衰减,我们根本无法检测到它们。
除了 ADC,RP2040 还具有数字 GPIO 引脚,可配置为输入或输出。输入具有超高阻抗,远高于 1MΩ。这意味着它们对于任何传入信号基本上都像开路一样。它们的输入寄生电容很小,因此不会衰减太多高频信号。有可以启用或禁用的施密特触发器,这意味着引脚要么干净地切换,至少有 100 mV 的滞后,要么有点不可预测地切换。
配置为输出的 GPIO 引脚具有可选的输出阻抗。选择约为 100Ω、72Ω、50Ω 和 36Ω。我说大约,因为我主要根据这里的数据表图表,并且保持高电平的引脚的阻抗与保持低电平的引脚的阻抗相差几欧姆。有两种斜率模式:快速和慢速。斜率表示引脚电压变化的速度,但我不知道在这种情况下它们实际上有多大差异。
还有上拉/下拉选项。根据数据表,上拉电阻约为 65kΩ。当两者同时启用时,芯片不会将引脚偏置到一半,而是切换到总线保持模式,其中上拉方向遵循最后一个输入值。
所有 GPIO 引脚都可以指定为由 CPU 内核或各种外设控制。最有趣的是,有 2 个可编程 I/O 单元(PIO,每个单元有 4 个状态机)可以以全系统时钟速度为引脚提供服务。不过,输入和 PIO 之间有 2 个周期延迟,以防止亚稳态问题。状态机可以读取、写入、更改引脚方向、翻转位、操作输入和输出队列、在中断时同步,有 2 个可以与零进行比较的暂存寄存器、递减,还有更多我现在不打算介绍的功能。
为了根据上述内容构建 1 位 ADC,必须禁用接收器 GPIO 引脚的输入滞后,以便它在阈值附近的微小变化时频繁切换,信号直流电平必须尽可能接近 GPIO 引脚在 0 和 1 之间翻转的阈值,并且必须存在足够量的不相关噪声,以便通过平均可以确定感兴趣信号的幅度。
想象一下感兴趣的信号骑在其他信号上;它在阈值处花费的时间和它引起的交叉次数与其幅度成正比:
至于将信号偏置到阈值附近,例如使用电阻分压器,这是不可能的。不仅电阻器不那么精确,而且阈值会随着电源电压和温度而变化。需要一个负反馈回路,通过轻轻地将引脚推向所需的方向来偏置引脚。
构建此类循环最有效的方法可能是利用 PIO。从接收器引脚读取,取反该值并将其输出到另一个引脚。然后用高值电阻连接这两个引脚。我使用的是 1MΩ。
不过,这种安排有几个问题。首先,偏置引脚输出即使在 1MΩ 电阻后面也太强了。添加一个 100nF 电容器以形成一个具有 100Ω 输出阻抗的基本低通滤波器似乎有帮助,但我仍然认为这个问题没有真正得到解决。
另一个问题是,对于非常弱的信号,偏置仍然太强,并会导致各种问题,从将信号带入和带出范围的不必要的振荡到增加大量高频噪声。可以通过关闭一段时间的偏置来微调平均偏置阻抗并限制噪声。例如,可以输出 1 个周期的偏置,然后将其关闭 31 个周期。PIO 支持额外的延迟和设置引脚方向。对于较强的信号,加大偏置力度,对于较弱的信号,轻轻地增加偏置,应该可以解决问题。
接收
我们正在构建的接收器类型称为直接采样接收器。请点击链接,PySDR网站是数字无线电的一个非常好的入门网站。如果没有它,我不可能走到今天。
与我们的接收器架构相关的最重要的一点是,所有混频和滤波都将在软件中执行。首先,需要两个相隔 90° 的正弦波。必要时可以使用方波。一个波用于同相分量,另一个波用于正交分量。我们称之为本地振荡器,尽管它是一个虚拟振荡器。
来自接收 PIO 的输入位与来自本地振荡器的位进行异或运算。为什么要进行异或运算?碰巧的是,如果您将 1/0 解释为 1/-1,然后将两个这样的方波相乘,结果最终与异或运算的结果相同。将输入信号与本地振荡器混合后,产生的复杂波形包含最初在本地振荡器频率附近向下偏移到 0 Hz 左右的信号,然后包含大量其他更高的混合产物。
由于我们在这里处理的是复杂信号,我建议您查看Mikael Q Kuisma 对复杂无线电信号背后的直觉的描述。这对我帮助很大。无论如何,看似0Hz的频率实际上是我们调谐到的频率,而更高的频率则是它周围的频率。
遗憾的是,RP2040的速度不足以进行任何复杂的过滤。但它至少可以通过逐位求和来抽取波形。至于以这样的速度添加位,通常会使用popcount
(人口计数)指令。由于 Cortex-M0+ 内核上没有该指令,因此必须以其他方式进行模拟。CPU 的速度太慢,无法以上述速度执行此操作,因此要么必须牺牲一些样本以获得更高的本底噪声,要么使用其他方法。
可以使用 PIO 添加位。可以使用 TX FIFO 输入位,并利用暂存寄存器和减量指令以全速求和。为了在每个周期或更短的时间内执行一次加法,必须一次处理两个位。我使用了以下 PIO 程序:
0: jmp y--, 1 // 00 => -1, Y--, goto 1
1: out pc, 2 // 01 => 0, next 2 bits
2: out pc, 2 // 10 => 0, next 2 bits
3: jmp x--, 2 // 11 => +1, X--, goto 2
4: out pc, 2 // avoid goto 0 on first X--
它基本上是一个查找表。每个out pc, 2
都消耗来自输入的两个位并跳转到相应的指令。机器将无限期地不断减少X
和Y
。如果某些输入位最终为01
或 10
,它将消耗位的速度比读取的速度快。最坏的情况下,它将以相同的速度消耗它们。
为了获得需要减去以读取总振幅的那些X
寄存器Y
,需要注入额外的指令以定期将临时寄存器复制到 RX FIFO。这些额外的指令将代替保存的程序执行,保存的程序随后将恢复。我一直在使用简单的方法:
in x, 32 // output X
in y, 32 // output Y
set x, 0 // zero X
set y, 0 // zero Y
最终我们应该得到大致如下的结果:
嗯,这其实是谎言。很多没有正确过滤掉的高频内容会作为我们感兴趣的信号的一部分出现,以不想要的方式扭曲它。遗憾的是,凭借 RP2040 的处理能力,我们对此无能为力。
从信号处理、模拟角度来看,系统如下所示。您可能会注意到300 Mbps。我已经对 Pico 进行了超频以获得更好的效果。我建议将系统时钟保持在您希望接收的频率的 2.5 倍以上。在图中,本地振荡器调至 88.2 MHz,这是较强的本地 FM 广播电台之一。
至于混合实现,RP2040 允许使用更高的地址写入某些寄存器0x1000
。在这种情况下,这些位实际上是与当前寄存器位进行异或运算的。通过广泛使用 DMA,可以自动化整个混合和求和过程,并让内核自由进行进一步处理,例如过滤和二次抽取。
DMA 从接收器 PIO 获取数据,制作两份副本,将它们与本地振荡器位进行异或运算,然后将其发送到夏季 PIO。为了以可预测的速率获得夏季输出,可以在节奏定时器的帮助下使用 DMA 发送额外指令。
可以使用循环缓冲区在软件中执行进一步的处理,例如额外的过滤。为了获得最佳性能,我不得不使用几乎所有的 DMA 通道。我希望 RP2040 的后续产品能够填充所有 16 个通道,而不仅仅是 12 个。
使用一根电线做天线是可行的,但性能不是很好。由于我的可伸缩偶极天线配备了 SMA 连接器,因此,为了快速而简单地集成 100nF 电容器和 1MΩ 电阻器,我拼凑了以下解决方案:
目前,我不太想自己编码处理最终的192kHz流以解调宽带FM来产生音频。我可能必须学习CORDIC才能以 atan2
足够快的速度解调 FM,实现足够快的去加重滤波器,最后将波形转换为差分PDM信号,通过全H桥电路驱动扬声器。
因此,我不用做这些,只需将原始 IQ 样本通过 USB CDC 发送到我的电脑,由一个小的 Python 脚本接收,转换成 TCP 流并用GNU Radio处理,它提供了一个名为GNU Radio Companion的图形应用程序,可用于构建信号处理管道并将音频输出到系统扬声器。
结论
是的,它很吵,但用 1 美元的通用微处理器、两个无源元件和一个天线就能做到这一点,难道不是很酷吗?
查看源代码以了解所有细节。
后记
说实话,有更好更简单的方法。正交检测器(也称为 Tayloe 检测器)目前是现代 DIY 低于100MHz收发器的最佳选择。通过在 MCU 外部进行混合,它们将密集的滤波工作留给少数模拟部件,并为您提供基带信号,以便使用慢速高分辨率 ADC 读取。并且该ADC的每一位都会为您提供3dBm的SNR。这几乎是内置Pico ADC的30dBm,如果您使用外部音频 ADC,则为60dBm。并且您可以在基带频率上添加一个超级简单的模拟放大器,以获得额外的增益。
这种解决方案几乎无法接收来自同一城市的超大 FM 电台。不过,它可以重新用作遥控玩具的接收器。通过频移或相移调制,使用简单的GPIO天线发射器,可以从最远约40米外接收超过1kbps 的命令。
另外附上一个在树莓派上进行RF发射的程序资源链接:
rpitx
相应讨论区:https://groups.io/g/rpitx