Skip to content
Nicholas Corgan edited this page Oct 30, 2021 · 5 revisions

LuaJIT bindings for SoapySDR

If you've stumbled upon this page, this is referring to in-progress development, and the information within can change at any point before release.

See this branch.

The LuaJIT bindings for SoapySDR give the user access to the full SoapySDR functionality in LuaJIT without having to directly call into the C API. The API is largely identical to the C++ API, but the functions themselves are more 1-to-1 with the C API, with overloads removed and more descriptive function names. Otherwise, there are only small changes, which will be explained below. Full API documentation is available (TODO: link when uploaded) here.

Dependencies

The only requirement for this API is LuaJIT itself, as the calls into the native SoapySDR library are done through LuaJIT's FFI functionality. To install the LuaJIT bindings, LuaJIT must be found on the system at build-time.

Debian/Ubuntu:

sudo apt-get install luajit

Windows+MSVC:

  • Install LuaJIT from prebuilt installers.

Basic example

local SoapySDR = require("SoapySDR")

-- Enumerate devices.
local results = SoapySDR.enumerateDevices()

-- Create device instance.
-- Args can be user-defined or from the enumeration result.
local args = {driver = "rtl-sdr"}
local sdr = SoapySDR.Device(args)

-- Query device info.
print("Antennas:")
local antennas = sdr:listAntennas(SoapySDR.Direction.RX, 0)
for i = 0, #antennas-1 do
    print(" * " .. antennas[i])
done

print("Gains:")
local gains = sdr:listGains(SoapySDR.Direction.RX, 0)
for i = 0, #gains-1 do
    print(" * " .. gains[i])
done

print("Frequency ranges:")
local freqRanges = sdr:getFrequencyRange(SoapySDR.Direction.RX, 0)
for i = 0, #freqRanges-1 do
    print(" * " .. tostring(freqRanges[i]))
done

-- Apply settings.
sdr:setSampleRate(SoapySDR.Direction.RX, 0, 1e6)
sdr:setFrequency(SoapySDR.Direction.RX, 0, 912.3e6)

-- Setup a stream (complex floats).
local rxStream = sdr:setupStream(SoapySDR.Direction.RX, SoapySDR.Format.CF32, {0})
sdr:activateStream(rxStream) -- Start streaming

-- Create a reusable buffer for RX samples.
local mtu = sdr:getStreamMTU(rxStream)
local cf32Buff = ffi.new("complex float[?]", mtu)
local cf32Buff2D = ffi.new("complex float*[1]", {cf32Buff})

-- Receive some samples.
for i = 0,10 do
    local ret, flags, timeNs = unpack(sdr:readStream(rxStream, cf32Buff2D, mtu)
    print(ret) -- Num samples or error code
    print(flags) -- Flags set by receive operation
    print(timeNs) -- Timestamp for receive buffer
done

-- Shut down the stream.
sdr:deactiveStream(rxStream) -- Stop streaming
sdr:closeStream(rxStream)

More examples

TODO

Native Lua types vs. LuaJIT types

TODO

Factory API

The Device::make() and Device::unmake() factory functions are not used in Python. Rather, the Device constructor directly calls into the make() factory, and the Device destructor directly calls into unmake() for cleanup. In addition, there are no associated thread-safety issues because the factory calls are protected by a Python mutex.

Stream API

The readStream() and writeStream() calls do not support C++'s return-by-reference. Therefore, any readback parameters are not passed into the associated calls. Rather, they are returned in the result object which contains the normal return code, output flags, and output timeNs.

Rather than a pointer array, the readStream() and writeStream() calls expect a list of numpy arrays for the receive and transmit buffers. Notice that both calls support multiple channels; they must have a list of buffers, one per channel. Also, be careful about data types. In the example the stream was configured for complex float32, and we use the corresponding numpy data type: numpy.complex64.

Clone this wiki locally