1. 程式人生 > >LimeSDR 中文教程 (八)

LimeSDR 中文教程 (八)

這是第八篇教程。我們在第一篇就說過,我們會從SDR新手開始一步步教你,直到你學會呼叫API程式設計。前幾篇說完後,現在是時候開始寫程式了。如果你已經讀完了前面的文章,會用GNU Octave,也熟悉PothosGNU Radio了,那麼直接呼叫API只需要再前進一小步。

Linux下開發(Ubuntu系統)

首先我們需要一個編譯器,我們使用gcc,它是Linux的主要部件,所以你很可能已經有了。如果你沒有,可以執行sudo apt-get install gcc來安裝最新版本。

搞定後,我們來試試第一個Hello World程式。

建立一個hello_world.c,用你最喜歡的編輯器開啟檔案,加入下面的程式碼:

#include // so we can use printfint main(){printf(“\nhello world\n”); // “\n” means new line}

儲存並關閉。接下來我們需要使用gcc編譯它,我們執行這個命令: gcc -std=c99 hello_world.c -o helloworld.bin 

這個命令會生成helloworld.bin,它是一個可執行程式,如果你沒有寫-o,那麼會自動生成a.out-std=c99表示使用的c語言的標準。我們接下去使用的soapy例子就是c99標準。

執行helloworld.bin會得到如下輸出:

SoapySDR介面(API)

方便起見,我們可以把SopaySDR API看作一個黑盒。就好像printf一樣,我們只需會用,當我們填入正確的資料後,它會按照我們的要求來工作。這個APIc++python版本,你如果不熟悉c語言也可以使用另兩個版本。

API文件在Device.h這個標頭檔案裡。

SoapySDR的網站上也有例子,C語言的例子在這裡。

要使用這個API,必須安裝SoapySDR。推薦你用Linux,但是Windows也是支援的,只不過設定編譯器會更復雜。如果你之前也看了我的教程,那麼你已經裝過了,因為我們之前已經用了很多次SoapySDR了。如果沒有安裝,可以點這裡。

你可以下載C語言的例子,並且儲存為example.c

檔案。

然後就想編譯helloworld.c一樣,只不過這次gcc命令要這樣寫:

$ gcc -std=c99 example.c -lSoapySDR -lm -o example.bin

這次我們需要加入連結命令 -lm,這是因為要用到一些數學上的函式,我們最好每次都加上這個,這樣不容易出錯。

然後執行一下,會報錯: SoapySDRDevice_make fail: RTL-SDR device not found.

但是這個例子並不要用RTL-SDR

不要害怕,只需要做些修改,這個程式碼就適合LimeSDR了,把如下文字改一下:

“SoapySDRKwargs_set(&args,"driver", "rtlsdr");”

改為 :

SoapySDRKwargs_set(&args,"driver", “lime");

編譯,再次執行,你就會看到如下顯示:

很簡單!

這樣我們的例子就可以工作了,它讀取了我們的LimeSDR的設定和序列號,另外還讀了10組資料,每組1024個取樣點。

API的文件說得很清楚了。比如如果我們想要更改增益,增益型別已經在程式碼裡顯示了,這是用如下語句實現的:

SoapySDRDevice_listGains(sdr,SOAPY_SDR_RX, 0, &length);

我們可以看一下API對這個函式的描述:

* List available amplification elements.* Elements should be in order RF to baseband.* \param device a pointer to a device instance* \param direction the channel direction RX or TX* \param channel an available channel* \param [out] length the number of gain names* \return a list of gain string names*/SOAPY_SDR_API char **SoapySDRDevice_listGains(const SoapySDRDevice *device, const int direction, const size_t channel, size_t *length);

要調整增益,我們只需要在這個文件裡搜尋關鍵字“_setGain”。我們把API中搜索到的內容貼在下面了,你可以看到它與listGain API很接近,那麼仿照前面的程式碼來設定增益應該很簡單。

* Set the overall amplification in a chain.*The gain will be distributed automatically across available element.*\param device a pointer to a device instance*\param direction the channel direction RX or TX*\param channel an available channel on the device*\param value the new amplification value in dB*\return an error code or 0 for success*/SOAPY_SDR_API int SoapySDRDevice_setGain(SoapySDRDevice *device, const int direction, const size_t channel, const double value);

通過上述資訊,我們可以這樣呼叫API來設定增益:

int success = SoapySDRDevice_setGain(sdr, SOAPY_SDR_RX, 0, 20)

這樣接收通道就會有20dB的增益。就是這麼簡單,整個API的文件都是這麼寫的,所以要加入其它函式也是小事一樁。

先整理一下

我們現在要實現一個新的工具。我們要從小程式起步,比如實現一個頻譜檢測程式。我們可以對於每個頻率讀取1024個取樣點,並計算RMS值。我們可以設定開始頻率和結束頻率,並且設定每次讀取間隔多少Hz

首先我們知道:程式程式碼對於硬體工程師來講並不熟悉,我們無法閱讀大量程式碼,所以最好把這個例子分解開來,這樣我們的主迴圈可以小一點,維護程式碼也方便。我們需要用到函式和指標,所以你最好先學習一點C語言基礎(我們自己也需要做點練習)。

我們會寫一些函式(這些可以看作是原型):

struct SoapySDRDevice *Setup(void);void DeviceInfo();void Read_1024_samples(struct SoapySDRDevice *sdr, double freq, complex float *buffer);void Close();

SoapySDRDevice

這個函式相對複雜,它會返回一個SoapySDRDevice結構體,並且把一個指標指向這個建立的SDR裝置。這個指標是其它所有函式要用的,它們通過這種方式與LimeSDR互動。

這個函式可以像這樣呼叫

struct SoapySDRDevice *sdr = Setup();

struct SoapySDRDevice *Setup(void){//enumerate devicesSoapySDRKwargs *results = SoapySDRDevice_enumerate(NULL, &length);for (size_t i = 0; i < length; i++){printf("Found device #%d: ", (int)i);for (size_t j = 0; j < results[i].size; j++){printf("%s=%s, ", results[i].keys[j], results[i].vals[j]);}printf("\n");}SoapySDRKwargsList_clear(results, length);//create device instance//args can be user defined or from the enumeration resultSoapySDRKwargs args = {};SoapySDRKwargs_set(&args,"driver", "lime");SoapySDRDevice *sdr = SoapySDRDevice_make(&args);SoapySDRKwargs_clear(&args);if(sdr == NULL){printf("SoapySDRDevice_make fail: %s\n", SoapySDRDevice_lastError());//return EXIT_FAILURE;}return sdr;}

DeviceInfo

這是一個簡單的函式,返回資訊資料,在呼叫時需要傳入SDR裝置:

DeviceInfo(sdr);

void DeviceInfo(struct SoapySDRDevice *sdr){//query device infochar** names = SoapySDRDevice_listAntennas(sdr, SOAPY_SDR_RX, 0, &length);printf("Rx antennas: ");for (size_t i = 0; i < length; i++) printf("%s, ", names[i]);printf("\n");SoapySDRStrings_clear(&names,length);names = SoapySDRDevice_listGains(sdr, SOAPY_SDR_RX, 0, &length);printf("Rx gains: ");for (size_t i = 0; i < length; i++) printf("%s, ", names[i]);printf("\n");SoapySDRStrings_clear(&names, length);SoapySDRRange *ranges = SoapySDRDevice_getFrequencyRange(sdr, SOAPY_SDR_RX, 0, &length);printf("Rx freq ranges: ");for(size_t i = 0; i < length; i++) printf("[%g Hz -> %g Hz], ", ranges[i].minimum, ranges[i].maximum);printf("\n");free(ranges);}

Read_1024_samples

一開始這個函式看上去有點可怕。你可以像這樣呼叫它:

Read_1024_samples(sdr, frequency, buffn);

sdrSetup()函式建立的,frequency是要調諧到的頻率值,最後的buffn是用來接收讀取的取樣點的buffer。這些值會儲存到buffn變數的指標中。

我們還在這個函式中加入了一些錯誤檢查機制。這樣可以避免調諧到支援的範圍外。除此之外,我們加入了增益,並且強制設定LNAL作為接收天線。

void Read_1024_samples(struct SoapySDRDevice *sdr, double freq, complex float *buffer){// check freq is within rangeif (freq <10e3){freq = 10e3;printf("Frequency out of range setting min: \n");}if (freq >3.8e9){freq = 3.8e9;printf("Frequency out of range setting max: \n");}//apply settingsif (SoapySDRDevice_setSampleRate(sdr, SOAPY_SDR_RX, 0, 1e6) != 0){printf("setSampleRate fail: %s\n", SoapySDRDevice_lastError());}if (SoapySDRDevice_setAntenna(sdr, SOAPY_SDR_RX, 0, "LNAL") != 0){printf("setAntenna fail: %s\n", SoapySDRDevice_lastError());}if (SoapySDRDevice_setGain(sdr, SOAPY_SDR_RX, 0, 20) != 0){printf("setAntenna fail: %s\n", SoapySDRDevice_lastError());}if (SoapySDRDevice_setFrequency(sdr, SOAPY_SDR_RX, 0, freq, NULL) != 0){printf("setFrequency fail: %s\n", SoapySDRDevice_lastError());}//setup a stream (complex floats)SoapySDRStream *rxStream;if (SoapySDRDevice_setupStream(sdr, &rxStream, SOAPY_SDR_RX, SOAPY_SDR_CF32, NULL, 0, NULL) != 0){printf("setupStream fail: %s\n", SoapySDRDevice_lastError());}SoapySDRDevice_activateStream(sdr, rxStream, 0, 0, 0); //start streamingvoid *buffs[] = {buffer}; //array of buffersint flags; //flags set by receive operationlong long timeNs; //timestamp for receive bufferint ret = SoapySDRDevice_readStream(sdr, rxStream, buffs, 1024, &flags, &timeNs, 100000);//shutdown the streamSoapySDRDevice_deactivateStream(sdr, rxStream, 0, 0); //stop streamingSoapySDRDevice_closeStream(sdr, rxStream);}

Close

最終我們需要關閉之前用Setup函式開啟的SDR裝置,我們只需要呼叫:

close(sdr)

void Close(struct SoapySDRDevice *sdr2){//cleanup device handleSoapySDRDevice_unmake(sdr2);printf("Done\n");//return EXIT_SUCCESS;}

最小的程式

我們的程式大概已經寫了90%了,我們現在寫一個最小型的例子。我們需要include下面這些檔案:

#include <SoapySDR/Device.h>#include <SoapySDR/Formats.h>#include <stdio.h> //printf#include <stdlib.h> //free#include <complex.h>

這是唯一的全域性變數

size_t length;

主函式只有這3

struct SoapySDRDevice *sdr = Setup();DeviceInfo(sdr);Close(sdr);

一個頻率監控工具

接下來只需要加入一小部分程式碼就可以開始建立很多應用程式了。這就是我們的頻率監控例子:

我們在命令列傳入3個引數: 開始頻率、結束頻率、間隔。這個程式碼就會設定好LimeSDR,計算需要做多少次採集。根據這個結果,程式會在每個頻率上讀取1024個取樣點,然後計算RMS值。 RMS均方根,就是把所有值取平方,再求平均,再求平方根。這個結果會列印到終端裡。注意sqrt命令要加入-lm才可以編譯正常,可能你的電腦不一定要加。

int main(int argc, char *argv[]){int argvcount=0;if(argc!=4){printf("%d",argc);printf("invalid input");exit(EXIT_FAILURE);}struct SoapySDRDevice *sdr = Setup();DeviceInfo(sdr);atexit(Cleanup);// HERE WE CAN DO THINGS// Lets do a site survey measuring every xxx Hz and displaying the powerdouble StartFreq = strtod(argv[1],NULL);double EndFreq = strtod(argv[2],NULL);double Interval =strtod(argv[3],NULL);double NumberOfScanPoints= (EndFreq-StartFreq)/Interval;printf("Starting RMS scan, Start is: %f, End is: %f, Interval is: %f\n",StartFreq,EndFreq,Interval);printf("This gives a total of: %f Sample points",NumberOfScanPoints);while (NumberOfScanPoints !=0){Get_Samples(sdr);//create a re-usable buffer for rx samplescomplex float buffn[1024];double frequency = StartFreq + (Interval * ((EndFreq/Interval)-NumberOfScanPoints));Read_1024_samples(sdr, frequency,buffn);//printf("ret=%d, flags=%d, timeNs=%lld\n", ret, flags, timeNs);float realavg = 0;for (int x = 0; x < 1024; x++){printf("%f + i%f\n", creal(buffn[x]), cimag(buffn[x]));realavg= realavg+ (creal(buffn[x])*creal(buffn[x]));}realavg = sqrt(realavg/1024);printf("Freq is: %f, RMS is: %f \n",frequency,realavg);NumberOfScanPoints--;}Close(sdr);}

編譯完成並執行程式,輸出如下:

在命令引數里加入LNA選擇和增益設定應該也很簡單。另外,把輸出儲存到檔案中,在Linux下也很好實現,只需要這樣執行:./soapy_api.bin 1e6 2e6 100e3 >output.txt

最後

C語言很有用,要實現更強大的程式也美問題。解碼、編碼、發射、接收都是可以實現的,就像上面這個例子一樣簡單。

我們希望從這個簡單的SoapySDR API裡會給予你一些啟發,使用這個方式,我們就不受限於PothosGNU Radio提供的模組了,我們可以建立全新的程式,你也可以選用熟悉的語言(CC++Python都可以)。