2017-02-18 12:51:26 +00:00
/* SoapySDR device access
*
* ( C ) 2017 by Andreas Eversberg < jolly @ eversberg . eu >
* All Rights Reserved
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2021-09-15 07:21:13 +00:00
/* how time stamp process works:
*
* TX and RX time stamps are not valid in the beginning .
*
* If a first chunk is received from SDR , RX time becomes valid . The duration
* of the received chunk is added to the RX time stamp , so it becomes the time
* of the next expected chunk .
*
* If a RX time stamp is valid and first chunk is to be transmitted ( tosend ( )
* is called ) , TX time stamp becomes valid and is set to RX time stamp , but
2021-09-18 09:43:01 +00:00
* advanced by the duration of the buffer size . tosend ( ) always returns
2021-09-15 07:21:13 +00:00
* the number of samples that are needed , to make TX time stamp advance RX time
2021-09-18 09:43:01 +00:00
* stamp by given buffer size .
2021-09-15 07:21:13 +00:00
*
* If chunk is transmitted to SDR , the TX time stamp is advanced by the
* duration of the transmitted chunk .
*/
2017-07-24 08:09:05 +00:00
# include <stdio.h>
2017-02-18 12:51:26 +00:00
# include <stdlib.h>
# include <stdint.h>
# include <string.h>
# include <errno.h>
# include <math.h>
2021-09-15 07:21:13 +00:00
# include <pthread.h>
2017-02-18 12:51:26 +00:00
# include <SoapySDR/Device.h>
# include <SoapySDR/Formats.h>
# include "soapy.h"
2017-11-18 07:58:57 +00:00
# include "../libdebug/debug.h"
2021-01-25 14:16:29 +00:00
# include "../liboptions/options.h"
2017-11-17 21:51:18 +00:00
extern int sdr_rx_overflow ;
2017-02-18 12:51:26 +00:00
static SoapySDRDevice * sdr = NULL ;
SoapySDRStream * rxStream = NULL ;
SoapySDRStream * txStream = NULL ;
static int tx_samps_per_buff , rx_samps_per_buff ;
static double samplerate ;
2021-09-15 07:21:13 +00:00
static pthread_mutex_t timestamp_mutex ;
static int use_time_stamps ;
static int rx_valid = 0 ;
static long long rx_timeNs = 0 ;
static int tx_valid = 0 ;
static long long tx_timeNs = 0 ;
static long long Ns_per_sample ;
2017-02-18 12:51:26 +00:00
2017-07-11 18:26:40 +00:00
static int parse_args ( SoapySDRKwargs * args , const char * _args_string )
2017-02-18 12:51:26 +00:00
{
2021-01-25 14:16:29 +00:00
char * args_string = options_strdup ( _args_string ) , * key , * val ;
2017-02-18 12:51:26 +00:00
2017-07-11 18:26:40 +00:00
memset ( args , 0 , sizeof ( * args ) ) ;
while ( args_string & & * args_string ) {
key = args_string ;
2017-02-18 12:51:26 +00:00
val = strchr ( key , ' = ' ) ;
if ( ! val ) {
2017-07-11 18:26:40 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Error parsing SDR args: No '=' after key \n " ) ;
2017-02-18 12:51:26 +00:00
soapy_close ( ) ;
return - EIO ;
}
2017-03-23 16:38:41 +00:00
* val + + = ' \0 ' ;
2017-07-11 18:26:40 +00:00
args_string = strchr ( val , ' , ' ) ;
if ( args_string )
* args_string + + = ' \0 ' ;
PDEBUG ( DSOAPY , DEBUG_DEBUG , " SDR device args: key='%s' value='%s' \n " , key , val ) ;
SoapySDRKwargs_set ( args , key , val ) ;
2017-02-18 12:51:26 +00:00
}
2017-07-11 18:26:40 +00:00
return 0 ;
}
2021-09-15 07:21:13 +00:00
int soapy_open ( size_t channel , const char * _device_args , const char * _stream_args , const char * _tune_args , const char * tx_antenna , const char * rx_antenna , const char * clock_source , double tx_frequency , double rx_frequency , double lo_offset , double rate , double tx_gain , double rx_gain , double bandwidth , int timestamps )
2017-07-11 18:26:40 +00:00
{
double got_frequency , got_rate , got_gain , got_bandwidth ;
2018-06-24 09:36:56 +00:00
const char * got_antenna , * got_clock ;
2017-07-11 18:26:40 +00:00
size_t num_channels ;
SoapySDRKwargs device_args ;
SoapySDRKwargs stream_args ;
SoapySDRKwargs tune_args ;
int rc ;
2021-09-15 07:21:13 +00:00
use_time_stamps = timestamps ;
if ( use_time_stamps & & ( 1000000000LL % ( long long ) rate ) ) {
2021-12-05 11:46:19 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " The given sample duration is not a multiple of a nano second. I.e. we can't divide 10^9 by sample rate of %.0f. Please choose a different sample rate for time stamp support! \n " , rate ) ;
2021-09-15 07:21:13 +00:00
use_time_stamps = 0 ;
}
Ns_per_sample = 1000000000LL / ( long long ) rate ;
2017-07-11 18:26:40 +00:00
samplerate = rate ;
/* parsing ARGS */
PDEBUG ( DSOAPY , DEBUG_INFO , " Using device args \" %s \" \n " , _device_args ) ;
rc = parse_args ( & device_args , _device_args ) ;
if ( rc < 0 )
return rc ;
PDEBUG ( DSOAPY , DEBUG_INFO , " Using stream args \" %s \" \n " , _stream_args ) ;
rc = parse_args ( & stream_args , _stream_args ) ;
if ( rc < 0 )
return rc ;
PDEBUG ( DSOAPY , DEBUG_INFO , " Using tune args \" %s \" \n " , _tune_args ) ;
rc = parse_args ( & tune_args , _tune_args ) ;
if ( rc < 0 )
return rc ;
2017-12-04 13:12:11 +00:00
if ( lo_offset ) {
char val [ 32 ] ;
snprintf ( val , sizeof ( val ) , " %.0f " , lo_offset ) ;
val [ sizeof ( val ) - 1 ] = ' \0 ' ;
SoapySDRKwargs_set ( & tune_args , " OFFSET " , val ) ;
}
2017-07-11 18:26:40 +00:00
/* create SoapySDR device */
sdr = SoapySDRDevice_make ( & device_args ) ;
2017-02-18 12:51:26 +00:00
if ( ! sdr ) {
2017-07-11 18:26:40 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to create SoapySDR \n " ) ;
2017-02-18 12:51:26 +00:00
soapy_close ( ) ;
return - EIO ;
}
2018-06-24 09:36:56 +00:00
/* clock source */
if ( clock_source & & clock_source [ 0 ] ) {
if ( ! strcasecmp ( clock_source , " list " ) ) {
char * * clocks ;
size_t clocks_length ;
int i ;
clocks = SoapySDRDevice_listClockSources ( sdr , & clocks_length ) ;
if ( ! clocks ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to request list of clock sources! \n " ) ;
soapy_close ( ) ;
return - EIO ;
}
if ( clocks_length ) {
for ( i = 0 ; i < ( int ) clocks_length ; i + + )
PDEBUG ( DSOAPY , DEBUG_NOTICE , " Clock source: '%s' \n " , clocks [ i ] ) ;
got_clock = SoapySDRDevice_getClockSource ( sdr ) ;
PDEBUG ( DSOAPY , DEBUG_NOTICE , " Default clock source: '%s' \n " , got_clock ) ;
} else
PDEBUG ( DSOAPY , DEBUG_NOTICE , " There are no clock sources configurable for this device. \n " ) ;
soapy_close ( ) ;
return 1 ;
}
if ( SoapySDRDevice_setClockSource ( sdr , clock_source ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set clock source to '%s' \n " , clock_source ) ;
soapy_close ( ) ;
return - EIO ;
}
got_clock = SoapySDRDevice_getClockSource ( sdr ) ;
if ( ! ! strcasecmp ( clock_source , got_clock ) ) {
PDEBUG ( DSOAPY , DEBUG_NOTICE , " Given clock source '%s' was accepted, but driver claims to use '%s' \n " , clock_source , got_clock ) ;
soapy_close ( ) ;
return - EINVAL ;
}
}
2018-05-21 14:14:47 +00:00
if ( rx_frequency ) {
2017-07-11 18:26:40 +00:00
/* get number of channels and check if requested channel is in range */
2018-05-21 14:14:47 +00:00
num_channels = SoapySDRDevice_getNumChannels ( sdr , SOAPY_SDR_RX ) ;
PDEBUG ( DSOAPY , DEBUG_DEBUG , " We have %d RX channel, selecting channel #%d \n " , ( int ) num_channels , ( int ) channel ) ;
2017-07-11 18:26:40 +00:00
if ( channel > = num_channels ) {
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Requested channel #%d (capable of RX) does not exist. Please select channel %d..%d! \n " , ( int ) channel , 0 , ( int ) num_channels - 1 ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return - EIO ;
}
/* antenna */
2018-05-21 14:14:47 +00:00
if ( rx_antenna & & rx_antenna [ 0 ] ) {
if ( ! strcasecmp ( rx_antenna , " list " ) ) {
2017-07-11 18:26:40 +00:00
char * * antennas ;
size_t antennas_length ;
int i ;
2018-05-21 14:14:47 +00:00
antennas = SoapySDRDevice_listAntennas ( sdr , SOAPY_SDR_RX , channel , & antennas_length ) ;
2017-07-11 18:26:40 +00:00
if ( ! antennas ) {
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to request list of RX antennas! \n " ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return - EIO ;
}
for ( i = 0 ; i < ( int ) antennas_length ; i + + )
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_NOTICE , " RX Antenna: '%s' \n " , antennas [ i ] ) ;
got_antenna = SoapySDRDevice_getAntenna ( sdr , SOAPY_SDR_RX , channel ) ;
PDEBUG ( DSOAPY , DEBUG_NOTICE , " Default RX Antenna: '%s' \n " , got_antenna ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return 1 ;
}
2018-05-21 14:14:47 +00:00
if ( SoapySDRDevice_setAntenna ( sdr , SOAPY_SDR_RX , channel , rx_antenna ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set RX antenna to '%s' \n " , rx_antenna ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return - EIO ;
}
2018-05-21 14:14:47 +00:00
got_antenna = SoapySDRDevice_getAntenna ( sdr , SOAPY_SDR_RX , channel ) ;
if ( ! ! strcasecmp ( rx_antenna , got_antenna ) ) {
PDEBUG ( DSOAPY , DEBUG_NOTICE , " Given RX antenna '%s' was accepted, but driver claims to use '%s' \n " , rx_antenna , got_antenna ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return - EINVAL ;
}
}
2017-03-20 18:57:37 +00:00
/* set rate */
2018-05-21 14:14:47 +00:00
if ( SoapySDRDevice_setSampleRate ( sdr , SOAPY_SDR_RX , channel , rate ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set RX rate to %.0f Hz \n " , rate ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EIO ;
}
2017-02-18 12:51:26 +00:00
2017-03-20 18:57:37 +00:00
/* see what rate actually is */
2018-05-21 14:14:47 +00:00
got_rate = SoapySDRDevice_getSampleRate ( sdr , SOAPY_SDR_RX , channel ) ;
2017-07-24 08:09:05 +00:00
if ( fabs ( got_rate - rate ) > 1.0 ) {
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Given RX rate %.3f Hz is not supported, try %.3f Hz \n " , rate , got_rate ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EINVAL ;
}
2017-02-18 12:51:26 +00:00
2018-05-21 14:14:47 +00:00
if ( rx_gain ) {
2017-07-11 18:26:40 +00:00
/* set gain */
2018-05-21 14:14:47 +00:00
if ( SoapySDRDevice_setGain ( sdr , SOAPY_SDR_RX , channel , rx_gain ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set RX gain to %.0f \n " , rx_gain ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return - EIO ;
}
/* see what gain actually is */
2018-05-21 14:14:47 +00:00
got_gain = SoapySDRDevice_getGain ( sdr , SOAPY_SDR_RX , channel ) ;
if ( fabs ( got_gain - rx_gain ) > 0.001 ) {
PDEBUG ( DSOAPY , DEBUG_NOTICE , " Given RX gain %.3f is not supported, we use %.3f \n " , rx_gain , got_gain ) ;
rx_gain = got_gain ;
2017-07-11 18:26:40 +00:00
}
2017-03-20 18:57:37 +00:00
}
2017-02-18 12:51:26 +00:00
2018-05-21 14:14:47 +00:00
/* hack to make limesdr tune rx to tx */
if ( tx_frequency = = rx_frequency )
rx_frequency + = 1.0 ;
2017-03-20 18:57:37 +00:00
/* set frequency */
2018-05-21 14:14:47 +00:00
if ( SoapySDRDevice_setFrequency ( sdr , SOAPY_SDR_RX , channel , rx_frequency , & tune_args ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set RX frequency to %.0f Hz \n " , rx_frequency ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EIO ;
}
2017-02-18 12:51:26 +00:00
2017-03-20 18:57:37 +00:00
/* see what frequency actually is */
2018-05-21 14:14:47 +00:00
got_frequency = SoapySDRDevice_getFrequency ( sdr , SOAPY_SDR_RX , channel ) ;
if ( fabs ( got_frequency - rx_frequency ) > 100.0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Given RX frequency %.0f Hz is not supported, try %.0f Hz \n " , rx_frequency , got_frequency ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EINVAL ;
}
2017-02-18 12:51:26 +00:00
2017-03-20 18:57:37 +00:00
/* set bandwidth */
2018-05-21 14:14:47 +00:00
if ( SoapySDRDevice_setBandwidth ( sdr , SOAPY_SDR_RX , channel , bandwidth ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set RX bandwidth to %.0f Hz \n " , bandwidth ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EIO ;
}
2017-03-18 08:30:26 +00:00
2017-03-20 18:57:37 +00:00
/* see what bandwidth actually is */
2018-05-21 14:14:47 +00:00
got_bandwidth = SoapySDRDevice_getBandwidth ( sdr , SOAPY_SDR_RX , channel ) ;
2017-07-11 18:26:40 +00:00
if ( fabs ( got_bandwidth - bandwidth ) > 100.0 ) {
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Given RX bandwidth %.0f Hz is not supported, try %.0f Hz \n " , bandwidth , got_bandwidth ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EINVAL ;
}
2017-03-18 08:30:26 +00:00
2017-03-20 18:57:37 +00:00
/* set up streamer */
2021-10-31 06:13:44 +00:00
# ifdef SOAPY_0_8_0_OR_HIGHER
2021-06-22 09:16:36 +00:00
if ( ! ( rxStream = SoapySDRDevice_setupStream ( sdr , SOAPY_SDR_RX , SOAPY_SDR_CF32 , & channel , 1 , & stream_args ) ) )
# else
if ( SoapySDRDevice_setupStream ( sdr , & rxStream , SOAPY_SDR_RX , SOAPY_SDR_CF32 , & channel , 1 , & stream_args ) ! = 0 )
# endif
{
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set RX streamer args \n " ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EIO ;
}
2017-02-18 12:51:26 +00:00
2017-03-20 18:57:37 +00:00
/* get buffer sizes */
2018-05-21 14:14:47 +00:00
rx_samps_per_buff = SoapySDRDevice_getStreamMTU ( sdr , rxStream ) ;
if ( rx_samps_per_buff = = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to get RX streamer sample buffer \n " ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EIO ;
}
2017-02-18 12:51:26 +00:00
}
2017-03-20 18:57:37 +00:00
2018-05-21 14:14:47 +00:00
if ( tx_frequency ) {
2017-07-11 18:26:40 +00:00
/* get number of channels and check if requested channel is in range */
2018-05-21 14:14:47 +00:00
num_channels = SoapySDRDevice_getNumChannels ( sdr , SOAPY_SDR_TX ) ;
PDEBUG ( DSOAPY , DEBUG_DEBUG , " We have %d TX channel, selecting channel #%d \n " , ( int ) num_channels , ( int ) channel ) ;
2017-07-11 18:26:40 +00:00
if ( channel > = num_channels ) {
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Requested channel #%d (capable of TX) does not exist. Please select channel %d..%d! \n " , ( int ) channel , 0 , ( int ) num_channels - 1 ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return - EIO ;
}
/* antenna */
2018-05-21 14:14:47 +00:00
if ( tx_antenna & & tx_antenna [ 0 ] ) {
if ( ! strcasecmp ( tx_antenna , " list " ) ) {
2017-07-11 18:26:40 +00:00
char * * antennas ;
size_t antennas_length ;
int i ;
2018-05-21 14:14:47 +00:00
antennas = SoapySDRDevice_listAntennas ( sdr , SOAPY_SDR_TX , channel , & antennas_length ) ;
2017-07-11 18:26:40 +00:00
if ( ! antennas ) {
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to request list of TX antennas! \n " ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return - EIO ;
}
for ( i = 0 ; i < ( int ) antennas_length ; i + + )
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_NOTICE , " TX Antenna: '%s' \n " , antennas [ i ] ) ;
got_antenna = SoapySDRDevice_getAntenna ( sdr , SOAPY_SDR_TX , channel ) ;
PDEBUG ( DSOAPY , DEBUG_NOTICE , " Default TX Antenna: '%s' \n " , got_antenna ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return 1 ;
}
2018-05-21 14:14:47 +00:00
if ( SoapySDRDevice_setAntenna ( sdr , SOAPY_SDR_TX , channel , tx_antenna ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set TX antenna to '%s' \n " , tx_antenna ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return - EIO ;
}
2018-05-21 14:14:47 +00:00
got_antenna = SoapySDRDevice_getAntenna ( sdr , SOAPY_SDR_TX , channel ) ;
if ( ! ! strcasecmp ( tx_antenna , got_antenna ) ) {
PDEBUG ( DSOAPY , DEBUG_NOTICE , " Given TX antenna '%s' was accepted, but driver claims to use '%s' \n " , tx_antenna , got_antenna ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return - EINVAL ;
}
}
2017-03-20 18:57:37 +00:00
/* set rate */
2018-05-21 14:14:47 +00:00
if ( SoapySDRDevice_setSampleRate ( sdr , SOAPY_SDR_TX , channel , rate ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set TX rate to %.0f Hz \n " , rate ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EIO ;
}
/* see what rate actually is */
2018-05-21 14:14:47 +00:00
got_rate = SoapySDRDevice_getSampleRate ( sdr , SOAPY_SDR_TX , channel ) ;
2017-07-24 08:09:05 +00:00
if ( fabs ( got_rate - rate ) > 1.0 ) {
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Given TX rate %.3f Hz is not supported, try %.3f Hz \n " , rate , got_rate ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EINVAL ;
}
2018-05-21 14:14:47 +00:00
if ( tx_gain ) {
2017-07-11 18:26:40 +00:00
/* set gain */
2018-05-21 14:14:47 +00:00
if ( SoapySDRDevice_setGain ( sdr , SOAPY_SDR_TX , channel , tx_gain ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set TX gain to %.0f \n " , tx_gain ) ;
2017-07-11 18:26:40 +00:00
soapy_close ( ) ;
return - EIO ;
}
/* see what gain actually is */
2018-05-21 14:14:47 +00:00
got_gain = SoapySDRDevice_getGain ( sdr , SOAPY_SDR_TX , channel ) ;
if ( fabs ( got_gain - tx_gain ) > 0.001 ) {
PDEBUG ( DSOAPY , DEBUG_NOTICE , " Given TX gain %.3f is not supported, we use %.3f \n " , tx_gain , got_gain ) ;
tx_gain = got_gain ;
2017-07-11 18:26:40 +00:00
}
2017-03-20 18:57:37 +00:00
}
/* set frequency */
2018-05-21 14:14:47 +00:00
if ( SoapySDRDevice_setFrequency ( sdr , SOAPY_SDR_TX , channel , tx_frequency , & tune_args ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set TX frequency to %.0f Hz \n " , tx_frequency ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EIO ;
}
/* see what frequency actually is */
2018-05-21 14:14:47 +00:00
got_frequency = SoapySDRDevice_getFrequency ( sdr , SOAPY_SDR_TX , channel ) ;
if ( fabs ( got_frequency - tx_frequency ) > 100.0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Given TX frequency %.0f Hz is not supported, try %.0f Hz \n " , tx_frequency , got_frequency ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EINVAL ;
}
/* set bandwidth */
2018-05-21 14:14:47 +00:00
if ( SoapySDRDevice_setBandwidth ( sdr , SOAPY_SDR_TX , channel , bandwidth ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set TX bandwidth to %.0f Hz \n " , bandwidth ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EIO ;
}
/* see what bandwidth actually is */
2018-05-21 14:14:47 +00:00
got_bandwidth = SoapySDRDevice_getBandwidth ( sdr , SOAPY_SDR_TX , channel ) ;
2017-07-11 18:26:40 +00:00
if ( fabs ( got_bandwidth - bandwidth ) > 100.0 ) {
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Given TX bandwidth %.0f Hz is not supported, try %.0f Hz \n " , bandwidth , got_bandwidth ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EINVAL ;
}
/* set up streamer */
2021-10-31 06:13:44 +00:00
# ifdef SOAPY_0_8_0_OR_HIGHER
2021-06-22 09:16:36 +00:00
if ( ! ( txStream = SoapySDRDevice_setupStream ( sdr , SOAPY_SDR_TX , SOAPY_SDR_CF32 , & channel , 1 , & stream_args ) ) )
# else
if ( SoapySDRDevice_setupStream ( sdr , & txStream , SOAPY_SDR_TX , SOAPY_SDR_CF32 , & channel , 1 , & stream_args ) ! = 0 )
# endif
{
2018-05-21 14:14:47 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to set TX streamer args \n " ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EIO ;
}
/* get buffer sizes */
2018-05-21 14:14:47 +00:00
tx_samps_per_buff = SoapySDRDevice_getStreamMTU ( sdr , txStream ) ;
if ( tx_samps_per_buff = = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to get TX streamer sample buffer \n " ) ;
2017-03-20 18:57:37 +00:00
soapy_close ( ) ;
return - EIO ;
}
2017-02-18 12:51:26 +00:00
}
2021-09-15 07:21:13 +00:00
/* create mutex for time stamp protection */
rc = pthread_mutex_init ( & timestamp_mutex , NULL ) ;
if ( rc < 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Mutex init failed! \n " ) ;
return rc ;
}
2017-02-25 06:09:53 +00:00
return 0 ;
}
/* start streaming */
int soapy_start ( void )
{
2017-02-18 12:51:26 +00:00
/* enable rx stream */
if ( SoapySDRDevice_activateStream ( sdr , rxStream , 0 , 0 , 0 ) ! = 0 ) {
2017-07-11 18:26:40 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to issue RX stream command \n " ) ;
return - EIO ;
}
/* enable tx stream */
if ( SoapySDRDevice_activateStream ( sdr , txStream , 0 , 0 , 0 ) ! = 0 ) {
PDEBUG ( DSOAPY , DEBUG_ERROR , " Failed to issue TX stream command \n " ) ;
2017-02-18 12:51:26 +00:00
return - EIO ;
}
return 0 ;
}
void soapy_close ( void )
{
2017-07-11 18:26:40 +00:00
PDEBUG ( DSOAPY , DEBUG_DEBUG , " Clean up SoapySDR \n " ) ;
2017-02-18 12:51:26 +00:00
if ( txStream ) {
2017-07-11 18:26:40 +00:00
SoapySDRDevice_deactivateStream ( sdr , txStream , 0 , 0 ) ;
2017-02-18 12:51:26 +00:00
SoapySDRDevice_closeStream ( sdr , txStream ) ;
txStream = NULL ;
}
if ( rxStream ) {
SoapySDRDevice_deactivateStream ( sdr , rxStream , 0 , 0 ) ;
SoapySDRDevice_closeStream ( sdr , rxStream ) ;
rxStream = NULL ;
}
if ( sdr ) {
SoapySDRDevice_unmake ( sdr ) ;
sdr = NULL ;
2021-09-15 07:21:13 +00:00
pthread_mutex_destroy ( & timestamp_mutex ) ;
2017-02-18 12:51:26 +00:00
}
}
int soapy_send ( float * buff , int num )
{
const void * buffs_ptr [ 1 ] ;
int chunk ;
int sent = 0 , count ;
int flags = 0 ;
while ( num ) {
chunk = num ;
if ( chunk > tx_samps_per_buff )
chunk = tx_samps_per_buff ;
2021-09-15 07:21:13 +00:00
/* write TX stream */
2017-02-18 12:51:26 +00:00
buffs_ptr [ 0 ] = buff ;
2021-09-15 07:21:13 +00:00
if ( use_time_stamps )
flags | = SOAPY_SDR_HAS_TIME ;
count = SoapySDRDevice_writeStream ( sdr , txStream , buffs_ptr , chunk , & flags , tx_timeNs , 1000000 ) ;
2017-07-24 08:09:05 +00:00
if ( count < = 0 ) {
2017-08-16 16:50:37 +00:00
PDEBUG ( DUHD , DEBUG_ERROR , " Failed to write to TX streamer (error=%d) \n " , count ) ;
2017-02-18 12:51:26 +00:00
break ;
2017-07-24 08:09:05 +00:00
}
2021-09-15 07:21:13 +00:00
/* process TX time stamp */
if ( ! tx_valid )
PDEBUG ( DSOAPY , DEBUG_ERROR , " SDR TX: tosend() was not called before, prease fix! \n " ) ;
else {
pthread_mutex_lock ( & timestamp_mutex ) ;
tx_timeNs + = count * Ns_per_sample ;
pthread_mutex_unlock ( & timestamp_mutex ) ;
}
/* increment transmit counters */
2017-02-18 12:51:26 +00:00
sent + = count ;
buff + = count * 2 ;
num - = count ;
}
return sent ;
}
/* read what we got, return 0, if buffer is empty, otherwise return the number of samples */
int soapy_receive ( float * buff , int max )
{
void * buffs_ptr [ 1 ] ;
int got = 0 , count ;
long long timeNs ;
int flags = 0 ;
while ( 1 ) {
if ( max < rx_samps_per_buff ) {
2017-03-18 08:57:38 +00:00
/* no more space this time */
2017-11-17 21:51:18 +00:00
sdr_rx_overflow = 1 ;
2017-02-18 12:51:26 +00:00
break ;
}
/* read RX stream */
buffs_ptr [ 0 ] = buff ;
count = SoapySDRDevice_readStream ( sdr , rxStream , buffs_ptr , rx_samps_per_buff , & flags , & timeNs , 0 ) ;
if ( count > 0 ) {
2021-09-15 07:21:13 +00:00
if ( ! use_time_stamps | | ! ( flags & SOAPY_SDR_HAS_TIME ) ) {
if ( use_time_stamps ) {
2021-12-12 09:49:32 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " SDR RX: No time stamps available. This may cause little gaps and problems with time slot based networks, like C-Netz. \n " ) ;
2021-09-15 07:21:13 +00:00
use_time_stamps = 0 ;
}
timeNs = rx_timeNs ;
}
/* process RX time stamp */
if ( ! rx_valid ) {
rx_timeNs = timeNs ;
rx_valid = 1 ;
}
pthread_mutex_lock ( & timestamp_mutex ) ;
if ( rx_timeNs ! = timeNs )
2021-12-12 09:49:32 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " SDR RX overflow, seems we are too slow. Use lower SDR sample rate, if this happens too often. \n " ) ;
2021-09-15 07:21:13 +00:00
rx_timeNs = timeNs + count * Ns_per_sample ;
pthread_mutex_unlock ( & timestamp_mutex ) ;
2017-02-18 12:51:26 +00:00
/* commit received data to buffer */
got + = count ;
buff + = count * 2 ;
max - = count ;
} else {
/* got nothing this time */
break ;
}
}
return got ;
}
2017-03-04 05:35:38 +00:00
/* estimate number of samples that can be sent */
2021-09-18 09:43:01 +00:00
int soapy_get_tosend ( int buffer_size )
2017-02-18 12:51:26 +00:00
{
2017-03-04 05:35:38 +00:00
int tosend ;
2017-02-18 12:51:26 +00:00
2021-09-15 07:21:13 +00:00
/* if no RX time stamp is set, we must wait until we receive a valid time stamp */
if ( ! rx_valid )
2017-03-04 05:35:38 +00:00
return 0 ;
2017-02-18 12:51:26 +00:00
2021-09-15 07:21:13 +00:00
/* RX time stamp is valid the first time, set the TX time stamp in advance */
if ( ! tx_valid ) {
2021-09-18 09:43:01 +00:00
tx_timeNs = rx_timeNs + buffer_size * Ns_per_sample ;
2021-09-15 07:21:13 +00:00
tx_valid = 1 ;
return 0 ;
}
2017-02-18 12:51:26 +00:00
/* we check how advance our transmitted time stamp is */
2021-09-15 07:21:13 +00:00
pthread_mutex_lock ( & timestamp_mutex ) ;
2021-09-18 09:43:01 +00:00
tosend = buffer_size - ( tx_timeNs - rx_timeNs ) / Ns_per_sample ;
2021-09-15 07:21:13 +00:00
pthread_mutex_unlock ( & timestamp_mutex ) ;
/* in case of underrun */
2021-09-18 09:43:01 +00:00
if ( tosend > buffer_size ) {
2021-09-15 07:21:13 +00:00
PDEBUG ( DSOAPY , DEBUG_ERROR , " SDR TX underrun, seems we are too slow. Use lower SDR sample rate. \n " ) ;
2021-09-18 09:43:01 +00:00
tosend = buffer_size ;
2017-03-18 08:57:38 +00:00
}
2021-09-15 07:21:13 +00:00
2021-09-18 09:43:01 +00:00
/* race condition and routing errors may cause TX time stamps to be in advance of slightly more than buffer_size */
2017-07-24 08:09:05 +00:00
if ( tosend < 0 )
tosend = 0 ;
2017-02-18 12:51:26 +00:00
2017-03-04 05:35:38 +00:00
return tosend ;
2017-02-18 12:51:26 +00:00
}