[原创] SDR# (SDRSharp)代码讲解
4840 查看
19 回复
 楼主 | 发布于 2017-12-21 | 只看楼主
分享到:

SDR#(也称SDRSharp)与Linux平台下常用的GQRX类似,是目前Windows平台上最常用的频谱观察,音频解调软件,支持AM、FM、SSB等多种调制方式。以SDRSharp为基础又派生出了其它一些软件比如TVSharp以及ADSBSharp等。


接下来本帖会讲解SDRSharp的核心代码,借此抛砖引玉,希望读者也能自此获得灵感,写出自己的作品。


我们的讲解会以最后一个开源版本的SDRSharp为基础。主要好处有两方面,一个是早期版本各类算法比较基础,方便理解,另一个是此版本代码经过一些简单修改后(加入HackRF等硬件的dll)即可在Visual Studio里编译运行,方便应用到自己的项目中。


下载地址:https://github.com/cgommel/sdrsharp


现计划把代码讲解分为几个部分,首先是解调算法的讲解,如AM、FM算法,会在书中找出代码所对应的公式,另外还会讲到用于频谱显示的FFT算法。接下来还会讲硬件接口部分的代码,包括如何与HackRF、RTL-SDR等硬件驱动交互的部分。


如有其它相关需求,也请读者提出,如果需求比较大也会对应讲解,另外如果有讲解的不对的地方也请读者不吝赐教,共同提高。


(版权说明:本帖及回复内容的版权分别属于各自的发帖者所有,转载请注明出处,谢谢。)

(1 ) (0 )
回复 举报

回复于 2017-12-21 沙发

(0 )
评论 (0) 举报

回复于 2017-12-21 2#

很好!

(0 )
评论 (0) 举报

回复于 2017-12-22 3#

感谢分享
(0 )
评论 (0) 举报

回复于 2017-12-22 4#

小板凳前排坐好,楼主可以开始了
(0 )
评论 (0) 举报

回复于 2017-12-22 5#

谢谢分享 认真记笔记...
(0 )
评论 (0) 举报

回复于 2017-12-27 6#

期待老铁大作。
(0 )
评论 (0) 举报

楼主 | 回复于 2017-12-31 7#

程序结构

进入SDRSharp主文件夹,可以发现下面有很多目录,这些目录主要分为3大类。


第一类是只与界面相关的代码,如:FrequencyEdit、FrequencyManager(频率管理界面)、CollapsiblePanel(左侧可收起的界面)、SDRSharp(第二层的SDRSharp子文件夹,里面是主界面)、PanView(主界面里的频谱图和瀑布图)


这一类文件夹的特征是,有些.cs文件一般都会有同名的.Designer.cs文件,这个文件就是C#界面编辑器根据拖动的控件自动生成的界面代码,界面上的事件对应的函数在前者的.cs了实现。一般界面代码里都不会有太复杂的逻辑或者算法,因此不必太仔细看,可能只有PanView值得研究下,因为里面涉及到了FFT算法的调用。


第二类是与SDR设备有关的代码,如:FUNcube、FUNcubeProPlus、HackRF、RTL283X、RTLSDR、RTLTCP、SDRIQ、SoftRock。可以很容易发现这些文件夹里的程序对应的就是你要使用到的SDR设备,它们也会有简单的硬件配置界面的代码,但是更重要的是,它们的内部都分别有NativeMethods.cs这个文件代码开头都会DllImport,这是用来读入设备对应的.dll文件的(相当于linux中的.so文件,如果是hackrf就相当于在读libhackrf.so),这样就可以在c#里直接调用设备驱动提供的函数接口了。我们会重点讲一下RTLSDR和HackRF文件夹,因为这两类硬件在国内比较常用,如果有时间,也可能会讲一下RTLTCP,通过TCP网络来获取远程RTL-SDR的数据。


RTLSDR文件夹中调用的层级结构是:RTLControllerDialog.cs(界面代码)->RTLSDRIO.cs->RtlDevice.cs->NativeMethods.cs->rtlsdr的驱动dll(这个dll是librtlsdr.c及其配套程序在Windows下编译出来的)。


其实,如果是初学者自己写程序的话,完全可以把中间的RTLSDRIO.cs、RtlDevice.cs、NativeMethods合并到界面代码中,直接在界面代码里调硬件驱动的API函数也是可以的。


第三类是纯算法的文件夹,如DNR(语音降噪)、Radio(各类解调算法)。这一类算法代码是SDR的精华,我们会重点讲解。


除这三类之外,还有一些文件夹也挺有意思,比如WavRecorder,主要用于实现录音功能,包括界面和对磁盘的写入代码。Common文件夹实现了一些接口,用于给其它新加入的模块集成,这样程序更加模块化,这样要对这个SDRSharp增加删除一些自定义模块时会更加灵活一些。初学者不需要太关心这个文件夹。


(0 )
评论 (0) 举报

楼主 | 回复于 2017-12-31 8#

我认为前面讲的这几种类型的代码里,最重要的有两方面:一个是与硬件驱动交互的部分(这部分上一篇已经大致讲了,另外也可以参考我的另一个HackRF代码讲解系列,会找到很多类似的东西,只不过那里调用的libhackrf.so这里变成了libhackrf.dll),另一个是数字信号处理的代码(包括AM、FM解调,FFT算法,语音降噪算法),我们会先讲这部分代码。


对于SDR领域,最有意义的就是这部分信号处理代码。在过去这类代码往往先在纸面上公式推导,然后在Matlab里做仿真验证。验证完了以后就把这些代码转换为DSP中用的C语言或者改写为VHDL语言在FPGA之类的硬件上实现。


SDR的魅力在于,你在完成初步的公式推导、理解概念后,可以直接把Matlab验证和硬件实现这两步合并起来。把最接近于通信原理中学的那些公式的代码直接生成波形然后用SDR硬件发射出去,或者用最接近原始通信原理公式的代码来解调SDR硬件接收到的波形。这样可以大大节省开发时间,提高灵活性。要知道Matlab代码要改写为C语言已经不怎么方便,如果要写入DSP芯片里可能还要交叉编译,调试起来也不如在电脑上直接运行的代码方便。如果Matlab代码要转为VHDL代码更麻烦。因为VHDL代码描述的是一个数字电路的硬件结构,可能实现个简单的除法在FPGA里就要写一个很复杂的逻辑。前面说的这两种硬件还算是比较容易改写的,如果是ASIC芯片,改写一次还要重新流片,开销巨大。所以对于批量不大,单价价格不敏感,并且灵活性要求高的项目来说,SDR实现可以说是一个非常有前途的方案。


我们先会看一下AmDetector.cs和FmDetector.cs这两个文件(以及它们所调用的其它辅助性.cs文件),他们分别对应于AM解调算法和FM解调算法,这两种算法可以说是很多模拟、数字调制方式的基础。


首先点开AmDetector.cs,可以发现代码很简单。代码中只有一个Demodulate方法,表示解调,它把收到的iq数据(这个iq数据就是从SDR硬件中获取到的原始数据流,至于如何从硬件获得后又调用到这个方法的以后会详细讲)做了取模的运算,然后作为sample,最后sample又送入了audio数组,用于音频输出。


这种AM解调算法是比较基础的算法,是非相干调幅解调算法的一种,我翻译的RTL-SDR Matlab书里也讲到过多次,见原书295页,或者原书232页6.8.2~6.8.3。书里的Magnitude和代码里的Modulus其实是同一个意思,都是取模(或者说取大小),用这种方式达到了低通滤波的效果。


所以这个解调算法的核心就是var sample = iq[i].Modulus()这一行代码。之后就把sample送入audio数组用于收听了。


说句题外话,早期的矿石收音机也是类似原理,只不过用的是硬件元器件处理波形,收到含信息的电磁波后经过二极管检波,其实就是把包络线找到了(相比取绝对值回丢失一些信息,但是做了低通滤波后也差不太多),然后就把这个包络线送入滤波电容,对包络线做进一步平滑处理,有些矿石收音机更简单,连后面的那个滤波电容都不用,因为人耳本身就有低通滤波的功能。


接下来再仔细看下代码,AMDetector.cs还有些其它内容,比如powerThreshold和squelchThreshold。它们两个其实是差不多的东西,可以从powerThreshold被赋值的那句代码看到,只是squelchThreshold的一个线性运算后的产物。它们的作用是,可以让使用者人为设定一个阈值,然后把解调出的sample值和这个阈值比较,如果sample足够大就作为音频数据输出,否则当作噪音去除掉。这个版本的代码里还把sample做了平滑处理(低通滤波,0.99f *avg+ 0.01f*power这句话实现)后再与那个阈值比较。


另外,还有一个小细节初学者可能不理解。因为我们普遍认为无线电信号和解调出的音频信号应该是连续的,实际确实也是连续的,但是电脑在处理时,无法处理一个无限长度的数据,只能对于一定长度的数据分段处理,所以有了length这个参数,并且做了一个循环,在没达到length前,不停地对iq数据做解调操作并不停地把解调出的数据送入audio数组。

(0 )
评论 (0) 举报

楼主 | 回复于 2017-12-31 9#

前面讲完了AMDetector.cs,可以说你已经入了SDR和DSP的门了,也具备一定的读代码能力了,接下去的无非是举一反三,按照之前说的那套格式实现其它类型的解调算法。比如我们接下来要讲的FMDetector.cs,总体结构和AMDetector.cs是很相似的,最重要的部分也是这个模块中对应的Demodulate方法。


这个Demodulate里的算法其实就是参照书中381页的9.7节的算法,只不过稍微有点区别,书里的算法是一路信号通过移相器取了共轭后,另一路信号做了延迟,然后把两路信号相乘后取角得到解调出的信号。


我们这里算法稍微有点不同,是把同一路的信号既做了延迟,又取了共轭。而与它相乘的另一路信号什么都没做。这两路信号相乘后,还是利用c#里的Argument函数实现了取角。


这两种方法实际上是同一种方法,最终实际解调出的波形只是符号反一反(有待确认?)。


所以Demodulate中最核心的其实只有3行代码。

var f=iq[i]*_iqState.Conjugate() 用于取共轭和乘法

var a=f.Argument() 用于取角

_iqState=iq[i] 用于实现延迟


这里实现延迟的方法比较巧妙,是利用了上一个循环中保留下来的iq数据存入_iqState中,这样它对于下一次循环就是经过延迟一个采样点的iq数据了,符合我们的预期,然后就可以取共轭,再相乘,最后取角了。


除了这些最核心的代码外,这个fm解调算法还有一些其他的内容,比如,如果那个乘法的结果过大,可以用一个缩放因子来减小它,然后再取角。


另外,FM信号还分窄带和宽带FM信号。如果是窄带FM信号,我们的解调算法还会调用ProcessSquelch方法对前面解调出的音频数据做进一步的处理。具体是用了hissFilter来对前面解调的audio数据做了滤波,这个hissFilter是用FirFilter这个类实现的。


然后在第95行把经过滤波的数据又去取了一下幅值,注意,别看这行里的变量很多,名字很长,其实_noiseAveragingRatio就是一个常量而已,这一行代码只是把前面的滤波结果再次做了一个低通滤波,然后把滤波出来的结果比较了一下预先设置的阈值,如果太响就静音。


个人认为这一段代码没必要细看,先不说这样的处理方式到底能提高多少效果,FM解调中窄带FM也只是一种特殊情况而已,这个FM解调算法只对窄带FM做了这种复杂的操作,而宽带FM根本不需要经过这些处理,照样能解调,那么这段代码不看也罢,照样可以认为看懂FM解调了,读者有兴趣也可以在前面Demodulate中注释掉这部分代码,包括后面对增益的修改,看看窄带FM的效果到底有多大区别。

(0 )
评论 (0) 举报

回复于 2018-01-01 10#

楼主讲的太好了

(0 )
评论 (0) 举报
发表回复
0/3000





举报

请选择举报类别

  • 广告垃圾
  • 违规内容
  • 恶意灌水
  • 重复发帖

全部板块

返回顶部