Skip to content

AirStack Application Notes

Continuous Streaming

Data streams are guaranteed to be continuous in with the AirStack driver, SoapyAIRT. That is, there is no gap in time between the last sample in a buffer and the first sample in the following buffer. The following sections outline best practices to maintain a continuous data stream as well as highlighting conditions to avoid where streams may become discontinuous.

Errors - Anytime the SoapyAIRT API returns an error code, any currently active data stream can no longer be considered continuous. Different data streams share a common hardware resource, so errors with one stream can affect others. As a result, users may want to restart all streams if an error is encountered. For application compatibility, we do attempt to restart any stream automatically, however it is STRONGLY recommended that users manually restart affected streams to attempt to fix the root cause of the error. Error codes and their possible root causes are shown below:

Error Code Error Name Description
-1 SOAPY_SDR_TIMEOUT Inactive hardware that has not been properly configured. Has the stream not been activated?
-2 SOAPY_SDR_STREAM_ERROR Generic streaming error. See logs for more info.
-3 SOAPY_SDR_CORRUPTION Multiple channels in a single stream are reporting different issues (likely cause is a single channel had an overflow). See kernel logs for specific details.
-4 SOAPY_SDR_OVERFLOW All channels in the stream have overflowed. The application is not reading samples from the radio fast enough.
-5 SOAPY_SDR_NOT_SUPPORTED You are asking the AIR-T to do something that it cannot do.
-6 SOAPY_SDR_TIME_ERROR As of AirStack 0.5 you should not see this error reported by the AIR-T.
-7 SOAPY_SDR_UNDERFLOW See "TX Streams" below. You are not providing samples to the radio fast enough.

​Finally, it should be noted that in order to make logging more verbose (especially useful when providing log information / output to customer support) that an environment variable may be set appropriately. This is done by export SOAPY_SDR_LOG_LEVEL=DEBUG in the terminal. ​

Multiple Channel Synchronization - For streams with multiple channels, there are no guarantees that the sample data will be synchronized in any meaningful way. If you require channels to be synchronized, the recommended way of doing so is via the hardware trigger (TODO: LINK GOES HERE). Note that any condition that would cause a discontinuity in the stream (i.e., errors and changes to SDR settings) will cause the synchronization to no longer hold.

Changing SDR Settings - There are specific cases where changing a SDR setting will result in discontinuous data.

First, if the sample rate is changed, any active stream that uses the channel affected by the change will be flushed to remove any samples with the old setting applied. As a result, the data stream will no longer be continuous.

Second, all active data streams are no longer continuous if frequency is changed. This occurs because the RF front end must recalibrate for LO suppression and quadrature error correction. Note that this means that changing the TX frequency may affect RX streams and vice-versa.
Overall, the best practice is to change SDR settings/parameters without an active stream (i.e., deactivate all streams, apply the new setting, and then re-active the streams). We perform these steps inside of the AirStack drivers if an active stream is detected while SDR settings are being changed because we recognize that not all applications can control when settings are being manipulated. With that said, we recommend restarting streams manually in the application code itself in order to explicitly document the fact that a discontinuity will occur when the setting is being changed.

TX Streams -The SoapyAIRT API expects all TX buffers passed to it to be continuous, unless the SOAPY_SDR_END_BURST flag is set when calling writeStream(). That is, without the flag set, the API expects each buffer to be part of a continuous transmission. The flag being set indicates that a transmission has ended and the API expects there to be a gap in time between the current call to writeStream() and any subsequent data transfer.
Note that, by default, underflow conditions (gaps in time between two buffers we are expecting to be continuous) are not reported by the SoapyAIRT API. This is due to compatibility issues with returning the error code and how other applications handle the error. Users can enable underflow checking/reporting by adding report_tx_underflow=true to the device arguments passed in to the SoapySDR constructor (it is not currently recommended to enable this feature in any GNU Radio applications).
As of AirStack 0.5.3, the transmit buffer can be managed through the SoapySDR API via the tx_buffer_size device/stream argument. We recommend that you set the TX buffer size to the largest transfer size (in number of samples) you expect to make in your application, as transfers larger than this will result in a truncated transfer (i.e., only the number of samples will be transmitted that can fit in the buffer). To set the tx_buffer_size, either set it in the device constructor (e.g., sdr = SoapySDR.Device(dict(driver="SoapyAIRT", tx_buffer_size="4096")) or when setting up a TX stream as shown below.

stream_args = dict(tx_buffer_size="4096")
tx_stream = sdr.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CS16, channel_indexes, stream_args)

For advanced users interested in eking out every ounce of performance, the tx_buffer_size can be set to zero. This creates a scenario where the buffer passed to writeStream() will be used directly for DMA transfers (aka. "zero copy"). This may or may not result in a performance increase for your application, as a memory copy operation is replaced with a memory mapping operation. Benchmarking calls to writeStream() is recommended to test if and how much performance is improved. This feature can only be enabled for CS16 streams and requires that the buffer passed to writeStream() is aligned to a 128 byte boundary and is a multiple in size of 128 bytes. The best way to ensure such alignment is to use the C++ SoapySDR API and to allocate your TX buffer with std::aligned_alloc().

External Clock Reference

Every AIR-T model supports receiving an external 10 MHz reference clock to act as the device clock for applications that require synchronization between multiple AIR-Ts. This can be accomplished via piping in a signal to the REF input on the AIR-T. For details on the specifications of the external signal, please see the Product Guide) for your hardware.

To enable this feature in software, you will need to either set the clk_src device argument or call setClockSource() via the SoapySDR API (e.g., sdr.setClockSource(“external”)). The clk_src parameter can be set via the device constructor (e.g., sdr = SoapySDR.Device(dict(driver="SoapyAIRT", clk_src="external"))) or if you are using GNU Radio, simply add clk_src=”external" to the Device arguments field in the sink or source block.
The AIR-T model 8201 comes with a GPS module that can be used as a reference clock if desired. To enable this functionality, follow the same software steps as for the external reference, except replace external with GPS. No hardware modifications are required on the 8201 to use the GPS reference, simply enable this feature via software. Note that the GPS clock may not function if there is not a clear GPS signal to the device. As a result, it is recommended to revert back to the internal clock in the event of a transient GPS signal, as shown in the Python code example below.

sdr = SoapySDR.Device(dict(driver="SoapyAIRT"))
try:
    sdr.setClockSource("GPS")
except RuntimeError as e:
    print("GPS Clock Reference Failed!")
    print(e)
    sdr.setClockSource("internal")


Last update: May 9, 2022