No samples with example code

I’m trying to run one of the example programs on my MacBook, and I’m not getting any samples.
I installed LimeSuite with brew.

/**
    @file   singleRX.cpp
    @author Lime Microsystems (www.limemicro.com)
    @brief  RX example
 */
#include "lime/LimeSuite.h"
#include <iostream>
#include <chrono>
#ifdef USE_GNU_PLOT
#include "gnuPlotPipe.h"
#endif

using namespace std;

// Device structure, should be initialize to NULL
lms_device_t *device = NULL;

int error()
{
    if (device != NULL)
        LMS_Close(device);
    exit(-1);
}

int main(int argc, char **argv)
{
    // Find devices
    // First we find number of devices, then allocate large enough list,  and then populate the list
    int n;
    if ((n = LMS_GetDeviceList(NULL)) < 0) // Pass NULL to only obtain number of devices
        error();
    cout << "Devices found: " << n << endl;
    if (n < 1)
        return -1;

    lms_info_str_t *list = new lms_info_str_t[n]; // allocate device list

    if (LMS_GetDeviceList(list) < 0) // Populate device list
        error();

    for (int i = 0; i < n; i++) // print device list
        cout << i << ": " << list[i] << endl;
    cout << endl;

    // Open the first device
    if (LMS_Open(&device, list[0], NULL))
        error();

    delete[] list; // free device list

    // Initialize device with default configuration
    // Do not use if you want to keep existing configuration
    // Use LMS_LoadConfig(device, "/path/to/file.ini") to load config from INI
    if (LMS_Init(device) != 0)
        error();

    // Enable RX channel
    // Channels are numbered starting at 0
    if (LMS_EnableChannel(device, LMS_CH_RX, 0, true) != 0)
        error();

    // Set center frequency to 800 MHz
    if (LMS_SetLOFrequency(device, LMS_CH_RX, 0, 98500000) != 0)
        error();

    // print currently set center frequency
    float_type freq;
    if (LMS_GetLOFrequency(device, LMS_CH_RX, 0, &freq) != 0)
        error();
    cout << "\nCenter frequency: " << freq / 1e6 << " MHz\n";

    // select antenna port
    lms_name_t antenna_list[10]; // large enough list for antenna names.
                                 // Alternatively, NULL can be passed to LMS_GetAntennaList() to obtain number of antennae
    if ((n = LMS_GetAntennaList(device, LMS_CH_RX, 0, antenna_list)) < 0)
        error();

    cout << "Available antennae:\n"; // print available antennae names
    for (int i = 0; i < n; i++)
        cout << i << ": " << antenna_list[i] << endl;

    if ((n = LMS_GetAntenna(device, LMS_CH_RX, 0)) < 0) // get currently selected antenna index
        error();
    // print antenna index and name
    cout << "Automatically selected antenna: " << n << ": " << antenna_list[n] << endl;

    //    if (LMS_SetAntenna(device, LMS_CH_RX, 0, LMS_PATH_LNAW) != 0) // manually select antenna
    //        error();

    if ((n = LMS_GetAntenna(device, LMS_CH_RX, 0)) < 0) // get currently selected antenna index
        error();
    // print antenna index and name
    cout << "Manually selected antenna: " << n << ": " << antenna_list[n] << endl;

    // Set sample rate to 8 MHz, preferred oversampling in RF 8x
    // This set sampling rate for all channels
    if (LMS_SetSampleRate(device, 8e6, 8) != 0)
        error();
    // print resulting sampling rates (interface to host , and ADC)
    float_type rate, rf_rate;
    if (LMS_GetSampleRate(device, LMS_CH_RX, 0, &rate, &rf_rate) != 0) // NULL can be passed
        error();
    cout << "\nHost interface sample rate: " << rate / 1e6 << " MHz\nRF ADC sample rate: " << rf_rate / 1e6 << "MHz\n\n";

    // Example of getting allowed parameter value range
    // There are also functions to get other parameter ranges (check LimeSuite.h)

    // Get allowed LPF bandwidth range
    lms_range_t range;
    if (LMS_GetLPFBWRange(device, LMS_CH_RX, &range) != 0)
        error();

    cout << "RX LPF bandwitdh range: " << range.min / 1e6 << " - " << range.max / 1e6 << " MHz\n\n";

    // Configure LPF, bandwidth 8 MHz
    if (LMS_SetLPFBW(device, LMS_CH_RX, 0, 8e6) != 0)
        error();

    // Set RX gain
    if (LMS_SetNormalizedGain(device, LMS_CH_RX, 0, 0.7) != 0)
        error();
    // Print RX gain
    float_type gain; // normalized gain
    if (LMS_GetNormalizedGain(device, LMS_CH_RX, 0, &gain) != 0)
        error();
    cout << "Normalized RX Gain: " << gain << endl;

    unsigned int gaindB; // gain in dB
    if (LMS_GetGaindB(device, LMS_CH_RX, 0, &gaindB) != 0)
        error();
    cout << "RX Gain: " << gaindB << " dB" << endl;

    // Perform automatic calibration
    if (LMS_Calibrate(device, LMS_CH_RX, 0, 8e6, 0) != 0)
        error();

    // Enable test signal generation
    // To receive data from RF, remove this line or change signal to LMS_TESTSIG_NONE
    if (LMS_SetTestSignal(device, LMS_CH_RX, 0, LMS_TESTSIG_NCODIV8, 0, 0) != 0)
        error();

    // Streaming Setup

    // Initialize stream
    lms_stream_t streamId;
    streamId.channel = 0;                         // channel number
    streamId.fifoSize = 1024 * 1024;              // fifo size in samples
    streamId.throughputVsLatency = 1.0;           // optimize for max throughput
    streamId.isTx = false;                        // RX channel
    streamId.dataFmt = lms_stream_t::LMS_FMT_F32; // 32-bit floats
    if (LMS_SetupStream(device, &streamId) != 0)
        error();

    // Data buffers
    const int bufersize = 10000; // complex samples per buffer
    float buffer[bufersize * 2]; // must hold I+Q values of each sample
    // Start streaming
    LMS_StartStream(&streamId);

#ifdef USE_GNU_PLOT
    GNUPlotPipe gp;
    gp.write("set size square\n set xrange[-1:1]\n set yrange[-1:1]\n");
#endif
    auto t1 = chrono::high_resolution_clock::now();
    auto t2 = t1;

    while (chrono::high_resolution_clock::now() - t1 < chrono::seconds(100)) // run for 10 seconds
    {
        int samplesRead;

        lms_stream_status_t status;
        if (LMS_GetStreamStatus(&streamId, &status) != 0)
        {
            // Handle error
            cout << "Stream Status error" << endl;
        }

        // Receive samples
        samplesRead = LMS_RecvStream(&streamId, buffer, bufersize, NULL, 1000);
        if (samplesRead <= 0)
        {
            cout << "error: " << samplesRead << endl;
        }
        // I and Q samples are interleaved in buffer: IQIQIQ...
        /*
            INSERT CODE FOR PROCESSING RECEIVED SAMPLES
        */
        // Plot samples
#ifdef USE_GNU_PLOT
        gp.write("plot '-' with points\n");
        for (int j = 0; j < samplesRead; ++j)
            gp.writef("%f %f\n", buffer[2 * j], buffer[2 * j + 1]);
        gp.write("e\n");
        gp.flush();
#endif
        // Print stats (once per second)
        if (chrono::high_resolution_clock::now() - t2 > chrono::seconds(1))
        {
            t2 = chrono::high_resolution_clock::now();
            lms_stream_status_t status;
            // Get stream status
            LMS_GetStreamStatus(&streamId, &status);
            cout << "RX data rate: " << status.linkRate / 1e6 << " MB/s\n";                       // link data rate
            cout << "RX fifo: " << 100 * status.fifoFilledCount / status.fifoSize << "%" << endl; // percentage of FIFO filled
        }
    }

    // Stop streaming
    LMS_StopStream(&streamId);            // stream is stopped but can be started again with LMS_StartStream()
    LMS_DestroyStream(device, &streamId); // stream is deallocated and can no longer be used

    // Close device
    LMS_Close(device);

    return 0;
}

I get the following out:
Devices found: 1
0: LimeSDR Mini, media=USB 3.0, module=FT601, addr=24607:1027, serial=1D90EE13DEF75D

Reference clock 40.00 MHz

Center frequency: 98.5 MHz
Available antennae:
0: NONE
1: LNAH
2: LNAL
3: LNAW
4: LB1
5: LB2
Automatically selected antenna: 2: LNAL
Manually selected antenna: 2: LNAL

Host interface sample rate: 8 MHz
RF ADC sample rate: 64MHz

RX LPF bandwitdh range: 1.4001 - 130 MHz

RX LPF configured
Normalized RX Gain: 0.739726
RX Gain: 54 dB
Rx calibration finished
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
error: 0
RX data rate: 0 MB/s
RX fifo: 0%
Message from debugger: killed

Program ended with exit code: 9

What version of Lime Suite?

And from your post I can see it’s a LimeSDR Mini, but what board version?

And can you run LimeQuickTest and post the output.

I tried singleRX and basicRX on MacOS Monterey and they both fail using LimeSuite-23.10.0. basicRX worked with some older version. Several SoapySDR programs work Ok some of the time with the LimeMini 2.0 using LimeSuite-23.10.0 on MacOS Monterey and Ubuntu 23.10. After running singleRX, I found that SoapySDR programs often lost the signal.

The board says it’s a Rev2.2. I’m going to have to hunt down LimeQuickTest because the brew install mentions it but it’s not in the distro.

######################################################

LimeSuite information summary

######################################################

Version information:
Library version: v20.10.0-release
Build timestamp: 2020-10-29
Interface version: v2020.10.0
Binary interface: 20.10-1

System resources:
Installation root: /usr/local
User home directory: /Users/ek
App data directory: /Users/ek/.local/share/LimeSuite
Config directory: /Users/ek/.limesuite
Image search paths:
- /usr/local/share/LimeSuite/images

Supported connections:

  • FT601
  • FX3
  • PCIEXillybus

This predates LimeSDR Mini v2 support. Can you please build from the latest tag, v23.11.0. Once you have Lime Suite built and installed from this, please post LimeQuickTest results.

I just built limesuite with the Unix instructions and got this from LimeQuickCheck:
[ TESTING STARTED ]

->Start time: Fri Dec 29 04:47:01 2023

->LimeSuite version: 22.09.0-ge829d3ed

->Device: LimeSDR Mini, media=USB 3.0, module=FT601, addr=24607:1027, serial=1D90EE13DEF75D, HW=5, GW=2.1

Serial Number: 1D90EE13DEF75D

Chip temperature: 48 C

[ Clock Network Test ]

->REF clock test

Test results: 38714; 41496; 44231 - PASSED

->VCTCXO test

Results : 6710993 (min); 6711029 (max) - FAILED

FAILED

->Clock Network Test FAILED

[ FPGA EEPROM Test ]

->Read EEPROM

Operation failed: error code 5

->FPGA EEPROM Test FAILED

[ LMS7002M Test ]

->Perform Registers Test

->External Reset line test

Reg 0x20: Write value 0xFFFD, Read value 0xFFFD

Reg 0x20: value after reset 0x0FFFF

->LMS7002M Test PASSED

[ RF Loopback Test ]

->Configure LMS

->Run Tests (TX_2 → LNA_W):

CH0 (SXR=1000.0MHz, SXT=1005.0MHz): Result:(-28.3 dBFS, 5.00 MHz) - FAILED

->Run Tests (TX_1 → LNA_H):

CH0 (SXR=2100.0MHz, SXT=2105.0MHz): Result:(-23.7 dBFS, 5.00 MHz) - FAILED

->RF Loopback Test FAILED

=> Board tests FAILED <=

Elapsed time: 2.00 seconds

I didn’t see your detail about using the latest tag, and it’s bedtime, so I’ll give it a try later.

I build from tag v23.11.0 and they still fail on MacOS 12.6.1 - I added a print out of the library version.

[dir:~/Archive/NetsdrSend-0359/LimeSDR] dir% c++ -std=c++11 -o basicRX basicRX.cpp -lLimeSuite
[dir:~/Archive/NetsdrSend-0359/LimeSDR] dir% c++ -std=c++11 -o singleRX singleRX.cpp -lLimeSuite
[dir:~/Archive/NetsdrSend-0359/LimeSDR] dir% basicRX
Devices found: 1
Library Version: 23.11.0-gc2d9e877
Reference clock 40.00 MHz
Selected RX path: LNAW
Received 0 samples
Received 0 samples
libusb: warning [darwin_abort_transfers] aborting all transactions on interface 1 pipe 4
Received 0 samples
Received 0 samples
libusb: warning [darwin_abort_transfers] aborting all transactions on interface 1 pipe 4

[dir:~/Archive/NetsdrSend-0359/LimeSDR] dir% singleRX
Devices found: 1
Library Version: 23.11.0-gc2d9e877
0: LimeSDR Mini, media=USB 3.0, module=FT601, addr=24607:1027, serial=1DA1546B7D43FB

Reference clock 40.00 MHz
Selected RX path: LNAW

Center frequency: 800 MHz
Available antennae:
0: NONE
1: LNAH
2: LNAL_NC
3: LNAW
4: Auto
Automatically selected antenna: 3: LNAW
Manually selected antenna: 3: LNAW

Host interface sample rate: 8 MHz
RF ADC sample rate: 64MHz

RX LPF bandwitdh range: 1.4001 - 130 MHz

RX LPF configured
Normalized RX Gain: 0.739726
RX Gain: 54 dB
Rx calibration finished
RX data rate: 0 MB/s
RX fifo: 0%
RX data rate: 0 MB/s
RX fifo: 0%
RX data rate: 0 MB/s
RX fifo: 0%
RX data rate: 0 MB/s
RX fifo: 0%
^C
[dir:~/Archive/NetsdrSend-0359/LimeSDR] dir%

My limeSDR Mini 2.0 passes the LimeQuickTest -

FPGA EEPROM Test ]
->Read EEPROM
FPGA EEPROM not supported in v2
->FPGA EEPROM Test PASSED

[ LMS7002M Test ]
->Perform Registers Test
->External Reset line test
Reg 0x20: Write value 0xFFFD, Read value 0xFFFD
Reg 0x20: value after reset 0x0FFFF
->LMS7002M Test PASSED

[ RF Loopback Test ]
->Configure LMS
->Run Tests (TX_2 → LNA_W):
CH0 (SXR=1000.0MHz, SXT=1005.0MHz): Result:(-28.5 dBFS, 5.00 MHz) - PASSED
->Run Tests (TX_1 → LNA_H):
CH0 (SXR=2100.0MHz, SXT=2105.0MHz): Result:(-25.9 dBFS, 5.00 MHz) - PASSED
->RF Loopback Test PASSED

=> Board tests PASSED <=

Elapsed time: 1.82 seconds

They also fail on ubuntu 22.04 -

daleDefault-string:~/Documents/Archive/NetsdrSend-0360/LimeSDR> c++ -std=c++11 -o basicRX basicRX.cpp -lLimeSuite
daleDefault-string:~/Documents/Archive/NetsdrSend-0360/LimeSDR> c++ -std=c++11 -o singleRX singleRX.cpp -lLimeSuite
daleDefault-string:~/Documents/Archive/NetsdrSend-0360/LimeSDR> basicRX
Devices found: 1
Library Version: 23.11.0-gc2d9e877
Reference clock 40.00 MHz
Selected RX path: LNAW
Received 0 samples
Received 0 samples
Received 0 samples
Received 0 samples

daleDefault-string:~/Documents/Archive/NetsdrSend-0360/LimeSDR> singleRX
Devices found: 1
Library Version: 23.11.0-gc2d9e877
0: LimeSDR Mini, media=USB 3.0, module=FT601, addr=24607:1027, serial=1DA1546B7D43FB

Reference clock 40.00 MHz
Selected RX path: LNAW

Center frequency: 800 MHz
Available antennae:
0: NONE
1: LNAH
2: LNAL_NC
3: LNAW
4: Auto
Automatically selected antenna: 3: LNAW
Manually selected antenna: 3: LNAW

Host interface sample rate: 8 MHz
RF ADC sample rate: 64MHz

RX LPF bandwitdh range: 1.4001 - 130 MHz

RX LPF configured
Normalized RX Gain: 0.739726
RX Gain: 54 dB
Rx calibration finished
RX data rate: 0 MB/s
RX fifo: 0%
RX data rate: 0 MB/s
RX fifo: 0%
RX data rate: 0 MB/s
RX fifo: 0%
RX data rate: 0 MB/s
RX fifo: 0%
^C
daleDefault-string:~/Documents/Archive/NetsdrSend-0360/LimeSDR>

I’ve run into similar issue with mine LimeSDR Mini V2. Zack suggested that with the new LimeSDR both RX and TX channels are to enabled even only for RX-only applications.

If you add

    if (LMS_EnableChannel(device, LMS_CH_TX, 0, true)!=0)	// Fix for v2
        error();

before //Enable RX channel in singleRX.cpp and recompile you should see proper streaming going on.

I’ve submitted a PR to the LimeSuite now with this change. Hopefully it will can be landed soon, or some alternative/better fix will land, so that we all will have better out-of-box experience with the LimeSuite :slight_smile:

That seems to work around the problem - singleRX.cpp and basicRX.cpp appear to work with that change.

With the brew 20.10 distribution, that change gets me here:
Devices found: 1

0: LimeSDR Mini, media=USB 3.0, module=FT601, addr=24607:1027, serial=1D90EE13DEF75D

Reference clock 40.00 MHz

Center frequency: 98.5 MHz

Available antennae:

0: NONE

1: LNAH

2: LNAL

3: LNAW

4: LB1

5: LB2

Automatically selected antenna: 2: LNAL

Manually selected antenna: 2: LNAL

Host interface sample rate: 8 MHz

RF ADC sample rate: 64MHz

RX LPF bandwitdh range: 1.4001 - 130 MHz

RX LPF configured

Normalized RX Gain: 0.739726

RX Gain: 54 dB

Rx calibration finished

RX data rate: 0 MB/s

RX fifo: 0%

RX data rate: 32.1782 MB/s

RX fifo: 0%

RX data rate: 32.1454 MB/s

RX fifo: 0%

RX data rate: 32.1454 MB/s

RX fifo: 0%

RX data rate: 32.1454 MB/s

RX fifo: 0%

RX data rate: 32.1454 MB/s

RX fifo: 0%

RX data rate: 32.1454 MB/s

RX fifo: 0%

RX data rate: 32.1454 MB/s

RX fifo: 0%

RX data rate: 32.1454 MB/s

RX fifo: 0%

After some more experimentation I have this. It stores a list of samples from a local FM radio station.
Creating a graph in a spreadsheet, the I and Q samples seem to be 45° out of phase.
Processing the CSV in Python isn’t quite getting what I’d expect. Energies are at unexpected frequencies.
Wondering what I might be doing wrong.

/**
    @file   singleRX.cpp
    @author Lime Microsystems (www.limemicro.com)
    @brief  RX example
 */
// Try to get this to demodulate FM radio with the blog entry at:
// https://witestlab.poly.edu/blog/capture-and-decode-fm-radio/
// #define USE_GNU_PLOT
#include "lime/LimeSuite.h"
#include <iostream>
#include <chrono>
#include <fstream>
#ifdef USE_GNU_PLOT
#include "gnuPlotPipe.h"
#endif
#include <fftw3.h>

using namespace std;

// Device structure, should be initialize to NULL
lms_device_t *device = NULL;

int error()
{
    if (device != NULL)
        LMS_Close(device);
    exit(-1);
}

int main(int argc, char **argv)
{
    // Find devices
    // First we find number of devices, then allocate large enough list,  and then populate the list
    int n;
    ofstream samplecsv;
    samplecsv.open("samples.csv");

    if ((n = LMS_GetDeviceList(NULL)) < 0) // Pass NULL to only obtain number of devices
        error();
    cout << "Devices found: " << n << endl;
    if (n < 1)
        return -1;

    lms_info_str_t *list = new lms_info_str_t[n]; // allocate device list

    if (LMS_GetDeviceList(list) < 0) // Populate device list
        error();

    for (int i = 0; i < n; i++) // print device list
        cout << i << ": " << list[i] << endl;
    cout << endl;

    // Open the first device
    if (LMS_Open(&device, list[0], NULL))
        error();

    delete[] list; // free device list

    // Initialize device with default configuration
    // Do not use if you want to keep existing configuration
    // Use LMS_LoadConfig(device, "/path/to/file.ini") to load config from INI
    if (LMS_Init(device) != 0)
        error();

    // Enable RX channel
    // Channels are numbered starting at 0
    if (LMS_EnableChannel(device, LMS_CH_RX, 0, true) != 0)
        error();

    if (LMS_EnableChannel(device, LMS_CH_TX, 0, true) != 0) // Fix for v2
        error();

    // Set center frequency to 800 MHz
    if (LMS_SetLOFrequency(device, LMS_CH_RX, 0, 98500000) != 0)
        error();

    // print currently set center frequency
    float_type freq;
    if (LMS_GetLOFrequency(device, LMS_CH_RX, 0, &freq) != 0)
        error();
    cout << "\nCenter frequency: " << freq / 1e6 << " MHz\n";

    // select antenna port
    lms_name_t antenna_list[10]; // large enough list for antenna names.
                                 // Alternatively, NULL can be passed to LMS_GetAntennaList() to obtain number of antennae
    if ((n = LMS_GetAntennaList(device, LMS_CH_RX, 0, antenna_list)) < 0)
        error();

    cout << "Available antennae:\n"; // print available antennae names
    for (int i = 0; i < n; i++)
        cout << i << ": " << antenna_list[i] << endl;

    if ((n = LMS_GetAntenna(device, LMS_CH_RX, 0)) < 0) // get currently selected antenna index
        error();
    // print antenna index and name
    cout << "Automatically selected antenna: " << n << ": " << antenna_list[n] << endl;

    //    if (LMS_SetAntenna(device, LMS_CH_RX, 0, LMS_PATH_LNAW) != 0) // manually select antenna
    //        error();

    if ((n = LMS_GetAntenna(device, LMS_CH_RX, 0)) < 0) // get currently selected antenna index
        error();
    // print antenna index and name
    cout << "Manually selected antenna: " << n << ": " << antenna_list[n] << endl;

    // Set sample rate to 8 MHz, preferred oversampling in RF 8x
    // This set sampling rate for all channels
    if (LMS_SetSampleRate(device, 8e6, 8) != 0)
        error();
    // print resulting sampling rates (interface to host , and ADC)
    float_type rate, rf_rate;
    if (LMS_GetSampleRate(device, LMS_CH_RX, 0, &rate, &rf_rate) != 0) // NULL can be passed
        error();
    cout << "\nHost interface sample rate: " << rate / 1e6 << " MHz\nRF ADC sample rate: " << rf_rate / 1e6 << "MHz\n\n";

    // Example of getting allowed parameter value range
    // There are also functions to get other parameter ranges (check LimeSuite.h)

    // Get allowed LPF bandwidth range
    lms_range_t range;
    if (LMS_GetLPFBWRange(device, LMS_CH_RX, &range) != 0)
        error();

    cout << "RX LPF bandwidth range: " << range.min / 1e6 << " - " << range.max / 1e6 << " MHz\n\n";

    // Configure LPF, bandwidth 8 MHz
    if (LMS_SetLPFBW(device, LMS_CH_RX, 0, 8e6) != 0)
        error();

    // Set RX gain
    if (LMS_SetNormalizedGain(device, LMS_CH_RX, 0, 0.7) != 0)
        error();
    // Print RX gain
    float_type gain; // normalized gain
    if (LMS_GetNormalizedGain(device, LMS_CH_RX, 0, &gain) != 0)
        error();
    cout << "Normalized RX Gain: " << gain << endl;

    unsigned int gaindB; // gain in dB
    if (LMS_GetGaindB(device, LMS_CH_RX, 0, &gaindB) != 0)
        error();
    cout << "RX Gain: " << gaindB << " dB" << endl;

    // Perform automatic calibration
    if (LMS_Calibrate(device, LMS_CH_RX, 0, 8e6, 0) != 0)
        error();

    // Enable test signal generation
    // To receive data from RF, remove this line or change signal to LMS_TESTSIG_NONE
    if (LMS_SetTestSignal(device, LMS_CH_RX, 0, LMS_TESTSIG_NCODIV8, 0, 0) != 0)
        error();

    // Streaming Setup

    // Initialize stream
    lms_stream_t streamId;
    streamId.channel = 0;                         // channel number
    streamId.fifoSize = 1024 * 1024;              // fifo size in samples
    streamId.throughputVsLatency = 1.0;           // optimize for max throughput
    streamId.isTx = false;                        // RX channel
    streamId.dataFmt = lms_stream_t::LMS_FMT_F32; // 32-bit floats
    if (LMS_SetupStream(device, &streamId) != 0)
        error();

    // Data buffers
    const int bufersize = 10000; // complex samples per buffer
    float buffer[bufersize * 2]; // must hold I+Q values of each sample
    // Start streaming
    LMS_StartStream(&streamId);

#ifdef USE_GNU_PLOT
    GNUPlotPipe gp;
    gp.write("set size square\n set xrange[-1:1]\n set yrange[-1:1]\n");
#endif
    auto t1 = chrono::high_resolution_clock::now();
    auto t2 = t1;

    while (chrono::high_resolution_clock::now() - t1 < chrono::seconds(10)) // run for 10 seconds
    {
        int samplesRead;

        lms_stream_status_t status;
        if (LMS_GetStreamStatus(&streamId, &status) != 0)
        {
            // Handle error
            cout << "Stream Status error" << endl;
        }

        // Receive samples
        samplesRead = LMS_RecvStream(&streamId, buffer, bufersize, NULL, 1000);
        if (samplesRead <= 0)
        {
            cout << "error: " << samplesRead << endl;
        }
        //        cout << "Read " << samplesRead << " samples." << endl;

        for (int i = 0; i < samplesRead; i++)
        {
            samplecsv << buffer[i];
            if (i & 1)
            {
                samplecsv << endl;
            }
            else
            {
                samplecsv << ",";
            }
        }

        // I and Q samples are interleaved in buffer: IQIQIQ...
        /*
            INSERT CODE FOR PROCESSING RECEIVED SAMPLES
        */
        // Plot samples
#ifdef USE_GNU_PLOT
        gp.write("plot '-' with points\n");
        for (int j = 0; j < samplesRead; ++j)
            gp.writef("%f %f\n", buffer[2 * j], buffer[2 * j + 1]);
        gp.write("e\n");
        gp.flush();
#endif
        // Print stats (once per second)
        if (chrono::high_resolution_clock::now() - t2 > chrono::seconds(1))
        {
            t2 = chrono::high_resolution_clock::now();
            lms_stream_status_t status;
            // Get stream status
            LMS_GetStreamStatus(&streamId, &status);
            cout << "RX data rate: " << status.linkRate / 1e6 << " MB/s\n";                       // link data rate
            cout << "RX fifo: " << 100 * status.fifoFilledCount / status.fifoSize << "%" << endl; // percentage of FIFO filled
        }
    }
    samplecsv.close();

    // Stop streaming
    LMS_StopStream(&streamId);            // stream is stopped but can be started again with LMS_StartStream()
    LMS_DestroyStream(device, &streamId); // stream is deallocated and can no longer be used

    // Close device
    LMS_Close(device);

    return 0;
}

#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt

# Load the CSV file
data = np.loadtxt("samples.csv", delimiter=",")

# Convert the two columns to complex numbers
complex_data = data[:, 0] + 1j * data[:, 1]

# Define the sample rate and frequency
sample_rate = 64000000  # 1 MHz
frequency = 98500000  # 98.5 MHz

# Generate the time values
t = np.arange(len(complex_data)) / sample_rate

# Generate the complex exponential
exp = np.exp(-1j * 2 * np.pi * frequency * t)

# Downconvert the signal
downconverted_data = complex_data * exp
# Compute the DFT
dft = np.fft.fft(downconverted_data)


# Compute the magnitude spectrum
magnitude_spectrum = np.abs(dft)
# Define the frequency resolution
freq_res = sample_rate / len(magnitude_spectrum)

# Define the frequency array
freqs = np.arange(len(magnitude_spectrum)) * freq_res

# Define the threshold
threshold = 50  # replace with your actual threshold

# Find the indices where the magnitude spectrum is above the threshold
indices = np.where(magnitude_spectrum > threshold)[0]

# Print the frequencies that have energy
for index in indices:
    print(f"Frequency: {freqs[index]} Hz, Energy: {magnitude_spectrum[index]}")

# Plot the magnitude spectrum
plt.figure()
plt.plot(magnitude_spectrum)
plt.title("Magnitude Spectrum")
plt.xlabel("Frequency")
plt.ylabel("Magnitude")
# start = 2281350  # replace with your start frequency
# end = 2281450  # replace with your end frequency
# plt.xlim([start, end])

plt.show()

sample_rate = 64000000  # 1 MHz

This does not seem right. It’s the ADC sample rate, but it gets decimated and reduced to Host interface sample rate: 8 MHz, so actual sample rate is 8MHz.

I fixed the sample rate to 8MHz.
I’ve been trying to follow these directions but I get a practically empty RAW file at the end:
This station comes in fine with my Yaesu.

/**
    @file   singleRX.cpp
    @author Lime Microsystems (www.limemicro.com)
    @brief  RX example
 */
// Try to get this to demodulate FM radio with the blog entry at:
// https://witestlab.poly.edu/blog/capture-and-decode-fm-radio/
// #define USE_GNU_PLOT
#include "lime/LimeSuite.h"
#include <iostream>
#include <chrono>
#include <fstream>
#ifdef USE_GNU_PLOT
#include "gnuPlotPipe.h"
#endif
#include <fftw3.h>

using namespace std;

// Device structure, should be initialize to NULL
lms_device_t *device = NULL;

int error()
{
    if (device != NULL)
        LMS_Close(device);
    exit(-1);
}

int main(int argc, char **argv)
{
    // Find devices
    // First we find number of devices, then allocate large enough list,  and then populate the list
    int n;
    ofstream samplecsv;

    if ((n = LMS_GetDeviceList(NULL)) < 0) // Pass NULL to only obtain number of devices
        error();
    cout << "Devices found: " << n << endl;
    if (n < 1)
        return -1;

    lms_info_str_t *list = new lms_info_str_t[n]; // allocate device list

    if (LMS_GetDeviceList(list) < 0) // Populate device list
        error();

    for (int i = 0; i < n; i++) // print device list
        cout << i << ": " << list[i] << endl;
    cout << endl;

    // Open the first device
    if (LMS_Open(&device, list[0], NULL))
        error();

    delete[] list; // free device list

    // Initialize device with default configuration
    // Do not use if you want to keep existing configuration
    // Use LMS_LoadConfig(device, "/path/to/file.ini") to load config from INI
    if (LMS_Init(device) != 0)
        error();

    // Enable RX channel
    // Channels are numbered starting at 0
    if (LMS_EnableChannel(device, LMS_CH_RX, 0, true) != 0)
        error();

    if (LMS_EnableChannel(device, LMS_CH_TX, 0, true) != 0) // Fix for v2
        error();

    // Set center frequency to 800 MHz
    int F_offset = 250000;
    int desired_freq = 985 * 1e5-F_offset;
    if (LMS_SetLOFrequency(device, LMS_CH_RX, 0, desired_freq) != 0)
        error();

    // print currently set center frequency
    float_type freq;
    if (LMS_GetLOFrequency(device, LMS_CH_RX, 0, &freq) != 0)
        error();
    cout << "\nCenter frequency: " << freq / 1e6 << " MHz\n";

    // select antenna port
    lms_name_t antenna_list[10]; // large enough list for antenna names.
                                 // Alternatively, NULL can be passed to LMS_GetAntennaList() to obtain number of antennae
    if ((n = LMS_GetAntennaList(device, LMS_CH_RX, 0, antenna_list)) < 0)
        error();

    cout << "Available antennae:\n"; // print available antennae names
    for (int i = 0; i < n; i++)
        cout << i << ": " << antenna_list[i] << endl;

    if ((n = LMS_GetAntenna(device, LMS_CH_RX, 0)) < 0) // get currently selected antenna index
        error();
    // print antenna index and name
    cout << "Automatically selected antenna: " << n << ": " << antenna_list[n] << endl;

    //    if (LMS_SetAntenna(device, LMS_CH_RX, 0, LMS_PATH_LNAW) != 0) // manually select antenna
    //        error();

    if ((n = LMS_GetAntenna(device, LMS_CH_RX, 0)) < 0) // get currently selected antenna index
        error();
    // print antenna index and name
    cout << "Manually selected antenna: " << n << ": " << antenna_list[n] << endl;

    // Set sample rate to 8 MHz, preferred oversampling in RF 8x
    // This set sampling rate for all channels
    if (LMS_SetSampleRate(device, 8e6, 8) != 0)
        error();
    // print resulting sampling rates (interface to host , and ADC)
    float_type rate, rf_rate;
    if (LMS_GetSampleRate(device, LMS_CH_RX, 0, &rate, &rf_rate) != 0) // NULL can be passed
        error();
    cout << "\nHost interface sample rate: " << rate / 1e6 << " MHz\nRF ADC sample rate: " << rf_rate / 1e6 << "MHz\n\n";

    // Example of getting allowed parameter value range
    // There are also functions to get other parameter ranges (check LimeSuite.h)

    // Get allowed LPF bandwidth range
    lms_range_t range;
    if (LMS_GetLPFBWRange(device, LMS_CH_RX, &range) != 0)
        error();

    cout << "RX LPF bandwidth range: " << range.min / 1e6 << " - " << range.max / 1e6 << " MHz\n\n";

    // Configure LPF, bandwidth 8 MHz
    if (LMS_SetLPFBW(device, LMS_CH_RX, 0, 8e6) != 0)
        error();

    // Set RX gain
    if (LMS_SetNormalizedGain(device, LMS_CH_RX, 0, 0.7) != 0)
        error();
    // Print RX gain
    float_type gain; // normalized gain
    if (LMS_GetNormalizedGain(device, LMS_CH_RX, 0, &gain) != 0)
        error();
    cout << "Normalized RX Gain: " << gain << endl;

    unsigned int gaindB; // gain in dB
    if (LMS_GetGaindB(device, LMS_CH_RX, 0, &gaindB) != 0)
        error();
    cout << "RX Gain: " << gaindB << " dB" << endl;

    // Perform automatic calibration
    if (LMS_Calibrate(device, LMS_CH_RX, 0, 8e6, 0) != 0)
        error();

    // Enable test signal generation
    // To receive data from RF, remove this line or change signal to LMS_TESTSIG_NONE
    if (LMS_SetTestSignal(device, LMS_CH_RX, 0, LMS_TESTSIG_NCODIV8, 0, 0) != 0)
        error();

    // Streaming Setup

    // Initialize stream
    lms_stream_t streamId;
    streamId.channel = 0;                         // channel number
    streamId.fifoSize = 1024 * 1024;              // fifo size in samples
    streamId.throughputVsLatency = 1.0;           // optimize for max throughput
    streamId.isTx = false;                        // RX channel
    streamId.dataFmt = lms_stream_t::LMS_FMT_F32; // 32-bit floats
    if (LMS_SetupStream(device, &streamId) != 0)
        error();

    // Data buffers
    const int bufersize = 10000; // complex samples per buffer
    float buffer[bufersize * 2]; // must hold I+Q values of each sample
    // Start streaming
    LMS_StartStream(&streamId);

#ifdef USE_GNU_PLOT
    GNUPlotPipe gp;
    gp.write("set size square\n set xrange[-1:1]\n set yrange[-1:1]\n");
#endif
    auto t1 = chrono::high_resolution_clock::now();
    auto t2 = t1;

    samplecsv.open("samples_" + to_string(freq / 1e6) + ".csv");

    while (chrono::high_resolution_clock::now() - t1 < chrono::seconds(30)) // run for 10 seconds
    {
        int samplesRead;

        lms_stream_status_t status;
        if (LMS_GetStreamStatus(&streamId, &status) != 0)
        {
            // Handle error
            cout << "Stream Status error" << endl;
        }

        // Receive samples
        samplesRead = LMS_RecvStream(&streamId, buffer, bufersize, NULL, 1000);
        if (samplesRead <= 0)
        {
            cout << "error: " << samplesRead << endl;
        }
        //        cout << "Read " << samplesRead << " samples." << endl;

        for (int i = 0; i < samplesRead; i++)
        {
            samplecsv << buffer[i];
            if (i & 1)
            {
                samplecsv << endl;
            }
            else
            {
                samplecsv << ",";
            }
        }

        // I and Q samples are interleaved in buffer: IQIQIQ...
        /*
            INSERT CODE FOR PROCESSING RECEIVED SAMPLES
        */
        // Plot samples
#ifdef USE_GNU_PLOT
        gp.write("plot '-' with points\n");
        for (int j = 0; j < samplesRead; ++j)
            gp.writef("%f %f\n", buffer[2 * j], buffer[2 * j + 1]);
        gp.write("e\n");
        gp.flush();
#endif
        // Print stats (once per second)
        if (chrono::high_resolution_clock::now() - t2 > chrono::seconds(1))
        {
            t2 = chrono::high_resolution_clock::now();
            lms_stream_status_t status;
            // Get stream status
            LMS_GetStreamStatus(&streamId, &status);
            cout << "RX data rate: " << status.linkRate / 1e6 << " MB/s\n";                       // link data rate
            cout << "RX fifo: " << 100 * status.fifoFilledCount / status.fifoSize << "%" << endl; // percentage of FIFO filled
        }
    }
    samplecsv.close();

    // Stop streaming
    LMS_StopStream(&streamId);            // stream is stopped but can be started again with LMS_StartStream()
    LMS_DestroyStream(device, &streamId); // stream is deallocated and can no longer be used

    // Close device
    LMS_Close(device);

    return 0;
}

#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import decimate, lfilter

# Load the CSV file
data = np.loadtxt("samples_98.249999.csv", delimiter=",")

# Convert the two columns to complex numbers
complex_data = data[:, 0] + 1j * data[:, 1]

# Define the sample rate and frequency
Fs = 8000000  # 8 MHz
Fc = 98500000
F_offset = 250000

# plt.specgram(complex_data, NFFT=2048, Fs=Fs)
# plt.title("x2")
# plt.xlabel("Time (s)")
# plt.ylabel("Frequency (Hz)")
# plt.ylim(-Fs / 2, Fs / 2)
# plt.xlim(0, len(complex_data) / Fs)
# plt.ticklabel_format(style="plain", axis="y")
# plt.show()

# To mix the data down, generate a digital complex exponential
# (with the same length as x1) with phase -F_offset/Fs
fc1 = np.exp(-1.0j * 2.0 * np.pi * F_offset / Fs * np.arange(len(complex_data)))
# Now, just multiply x1 and the digital complex expontential
x2 = complex_data * fc1
# plt.specgram(x2, NFFT=2048, Fs=Fs)
# plt.title("x2")
# plt.xlabel("Time (s)")
# plt.ylabel("Frequency (Hz)")
# plt.ylim(-Fs / 2, Fs / 2)
# plt.xlim(0, len(x2) / Fs)
# plt.ticklabel_format(style="plain", axis="y")
# plt.show()
# An FM broadcast signal has  a bandwidth of 200 kHz
f_bw = 200000
n_taps = 64
# An FM broadcast signal has  a bandwidth of 200 kHz
f_bw = 200000
dec_rate = int(Fs / f_bw)
x4 = decimate(x2, dec_rate)
# Calculate the new sampling rate
Fs_y = Fs / dec_rate
# plt.specgram(x4, NFFT=2048, Fs=Fs_y)
# plt.title("x4")
# plt.ylim(-Fs_y / 2, Fs_y / 2)
# plt.xlim(0, len(x4) / Fs_y)
# plt.ticklabel_format(style="plain", axis="y")
# plt.show()

# Plot the constellation of x4.  What does it look like?
# plt.scatter(np.real(x4[0:50000]), np.imag(x4[0:50000]), color="red", alpha=0.5)
# plt.title("x4")
# plt.xlabel("Real")
# plt.xlim(-0.1, 0.1)
# plt.ylabel("Imag")
# plt.ylim(-0.1, 0.1)
# plt.show()

### Polar discriminator
y5 = x4[1:] * np.conj(x4[:-1])
x5 = np.angle(y5)
# Note: x5 is now an array of real, not complex, values
# As a result, the PSDs will now be plotted single-sided by default (since
# a real signal has a symmetric spectrum)
# Plot the PSD of x5
# plt.psd(x5, NFFT=2048, Fs=Fs_y, color="blue")
# plt.title("x5")
# plt.axvspan(0, 15000, color="red", alpha=0.2)
# plt.axvspan(19000 - 500, 19000 + 500, color="green", alpha=0.4)
# plt.axvspan(19000 * 2 - 15000, 19000 * 2 + 15000, color="orange", alpha=0.2)
# plt.axvspan(19000 * 3 - 1500, 19000 * 3 + 1500, color="blue", alpha=0.2)
# plt.ticklabel_format(style="plain", axis="y")
# plt.show()

# The de-emphasis filter
# Given a signal 'x5' (in a numpy array) with sampling rate Fs_y
d = Fs_y * 75e-6  # Calculate the # of samples to hit the -3dB point
x = np.exp(-1 / d)  # Calculate the decay between each sample
b = [1 - x]  # Create the filter coefficients
a = [1, -x]
x6 = lfilter(b, a, x5)

# Find a decimation rate to achieve audio sampling rate between 44-48 kHz
audio_freq = 44100.0
dec_audio = int(Fs_y / audio_freq)
Fs_audio = Fs_y / dec_audio

x7 = decimate(x6, dec_audio)

# Scale audio to adjust volume
x7 *= 10000 / np.max(np.abs(x7))
# Save to file as 16-bit signed single-channel audio samples
x7.astype("int16").tofile("wbfm-mono.raw")

From a quick look at the code this looks suspicious:

    // Enable test signal generation
    // To receive data from RF, remove this line or change signal to LMS_TESTSIG_NONE
    if (LMS_SetTestSignal(device, LMS_CH_RX, 0, LMS_TESTSIG_NCODIV8, 0, 0) != 0)
        error();

Think you need to remove this block and give it a whirl again.

I think it did something. I got an audio file with half a second of static.

I tried setting the antenna to each of the three defined values with no success, and I’m wondering why it’s only writing out a fraction of the time that got sampled.
Bedtime…

I think what is going on is the writing is code unable to keep up with the input sample rate. You should be able to see it from the RX fifo usage print statistics. If it is at 0% you’re good, if it is at 100% then probably processing code is not fast enough.

To solve this problem you can try replacing std::endl in file writing with '\n' to avoid file flush at every line. On my macbook it helped but not enough, but maybe your SSD is faster. What I ended up doing is to pre-allocate one big global array, fill it in from the streaming loop, and write to file after the streaming loop. This way the slow file IO does not cause samples drop.

After doing so the Python code you’ve provided seems to be properly visualizing FFT and decoding the WFM local station.