Add SoapySDR support ()

This commit is contained in:
Christian W. Zuckschwerdt 2018-11-01 17:53:14 +01:00 committed by GitHub
parent 2ceb535357
commit d596f6aa8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 715 additions and 38 deletions

View file

@ -80,7 +80,22 @@ endif()
########################################################################
find_package(PkgConfig)
find_package(LibRTLSDR)
list(APPEND SDR_LIBRARIES ${LIBRTLSDR_LIBRARIES})
if(LIBRTLSDR_FOUND)
include_directories(${LIBRTLSDR_INCLUDE_DIRS})
list(APPEND SDR_LIBRARIES ${LIBRTLSDR_LIBRARIES})
ADD_DEFINITIONS(-DRTLSDR)
else()
message(STATUS "RTL-SDR development files not found, RTL-SDR device input won't be possible.")
endif()
find_package(SoapySDR "0.6" NO_MODULE)
if(SoapySDR_FOUND)
include_directories(${SoapySDR_INCLUDE_DIRS})
list(APPEND SDR_LIBRARIES ${SoapySDR_LIBRARIES})
ADD_DEFINITIONS(-DSOAPYSDR)
else()
message(STATUS "Soapy SDR development files not found, Soapy SDR device input won't be possible.")
endif()
# cmake -DCMAKE_BUILD_TYPE=Profile ..
# CPUPROFILE=prof.out ./src/rtl_433 ...
@ -106,7 +121,6 @@ endif()
########################################################################
include_directories(
${CMAKE_SOURCE_DIR}/include
${LIBRTLSDR_INCLUDE_DIRS}
)
########################################################################

View file

@ -57,7 +57,7 @@ Usage: = Tuner options =
[-E] Stop after outputting successful event(s)
[-V] Output the version string and exit
[-h] Output this usage help and exit
Use -R, -X, -F, -r, or -w without argument for more help
Use -d, -R, -X, -F, -r, or -w without argument for more help
Supported device protocols:
[01] Silvercrest Remote Control

View file

@ -16,9 +16,18 @@ LT_INIT
AC_PROG_LIBTOOL
LT_LIB_M
PKG_CHECK_MODULES(LIBRTLSDR, librtlsdr)
LIBS="$LIBS $LIBRTLSDR_LIBS"
CFLAGS="$CFLAGS $LIBRTLSDR_CFLAGS"
PKG_CHECK_MODULES([LIBRTLSDR], [librtlsdr], [HAVE_LIBRTLSDR=1], [HAVE_LIBRTLSDR=0])
AM_CONDITIONAL([USE_LIBRTLSDR], [test "$HAVE_LIBRTLSDR" -eq 1])
AM_COND_IF([USE_LIBRTLSDR],
[ LIBS="$LIBS $LIBRTLSDR_LIBS"
CFLAGS="$CFLAGS $LIBRTLSDR_CFLAGS -DRTLSDR" ])
PKG_CHECK_MODULES([SOAPYSDR], [SoapySDR], [HAVE_SOAPYSDR=1], [HAVE_SOAPYSDR=0])
AM_CONDITIONAL([USE_SOAPYSDR], [test "$HAVE_SOAPYSDR" -eq 1])
AM_COND_IF([USE_SOAPYSDR],
[ LIBS="$LIBS $SOAPYSDR_LIBS"
CFLAGS="$CFLAGS $SOAPYSDR_CFLAGS -DSOAPYSDR" ])
AC_PATH_PROG(DOXYGEN,doxygen,false)
AM_CONDITIONAL(HAVE_DOXYGEN, test $DOXYGEN != false)

View file

@ -17,21 +17,116 @@
typedef struct sdr_dev sdr_dev_t;
typedef void (*sdr_read_cb_t)(unsigned char *buf, uint32_t len, void *ctx);
int sdr_open(sdr_dev_t **out_dev, int *sample_size, char const *dev_query, int verbose);
/*!
* Find the closest matching device, optionally report status
*
* \param out_dev device output returned
* \param sample_size stream output sample width returned
* \param dev_query a string to be parsed as device spec
* \param verbose the verbosity level for reports to stderr
* \return dev 0 if successful
*/
int sdr_open(sdr_dev_t **out_dev, int *sample_size, char *dev_query, int verbose);
/*!
* Close the device, optionally report status
*
* \param dev the device handle
* \param verbose the verbosity level for reports to stderr
* \return 0 on success
*/
int sdr_close(sdr_dev_t *dev);
/*!
* Set device frequency, optionally report status
*
* \param dev the device handle
* \param frequency in Hz
* \param verbose the verbosity level for reports to stderr
* \return 0 on success
*/
int sdr_set_center_freq(sdr_dev_t *dev, uint32_t freq, int verbose);
/*!
* Get device frequency
*
* \param dev the device handle
* \return frequency in Hz on success, 0 otherwise
*/
uint32_t sdr_get_center_freq(sdr_dev_t *dev);
/*!
* Set the frequency correction value for the device, optionally report status
*
* \param dev the device handle
* \param ppm_error correction value in parts per million (ppm)
* \param verbose the verbosity level for reports to stderr
* \return 0 on success
*/
int sdr_set_freq_correction(sdr_dev_t *dev, int ppm, int verbose);
/*!
* Enable auto gain, optionally report status
*
* \param dev the device handle
* \param verbose the verbosity level for reports to stderr
* \return 0 on success
*/
int sdr_set_auto_gain(sdr_dev_t *dev, int verbose);
int sdr_set_tuner_gain(sdr_dev_t *dev, int gain, int verbose);
/*!
* Set tuner gain or gain elements, optionally report status
*
* \param dev the device handle
* \param gain_str in tenths of a dB for RTL-SDR, string of gain element pairs (example LNA=40,VGA=20,AMP=0), or string of overall gain, in dB
* \param verbose the verbosity level for reports to stderr
* \return 0 on success
*/
int sdr_set_tuner_gain(sdr_dev_t *dev, char *gain_str, int verbose);
/*!
* Set device sample rate, optionally report status
*
* \param dev the device handle
* \param samp_rate in samples/second
* \param verbose the verbosity level for reports to stderr
* \return 0 on success
*/
int sdr_set_sample_rate(sdr_dev_t *dev, uint32_t rate, int verbose);
/*!
* Get device sample rate
*
* \param dev the device handle
* \return sample rate in samples/second on success, 0 otherwise
*/
uint32_t sdr_get_sample_rate(sdr_dev_t *dev);
int sdr_reset(sdr_dev_t *dev);
/*!
* Activate stream (only needed for SoapySDR)
*
* \param dev the device handle
* \return 0 on success
*/
int sdr_activate(sdr_dev_t *dev);
/*!
* Deactivate stream (only needed for SoapySDR)
*
* \param dev the device handle
* \return 0 on success
*/
int sdr_deactivate(sdr_dev_t *dev);
/*!
* Reset buffer (only needed for RTL-SDR), optionally report status
*
* \param dev the device handle
* \param verbose the verbosity level for reports to stderr
* \return 0 on success
*/
int sdr_reset(sdr_dev_t *dev, int verbose);
int sdr_start(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len);
int sdr_stop(sdr_dev_t *dev);

View file

@ -161,7 +161,7 @@ void usage(r_device *devices, int exit_code)
"\t[-E] Stop after outputting successful event(s)\n"
"\t[-V] Output the version string and exit\n"
"\t[-h] Output this usage help and exit\n"
"\t\t Use -R, -X, -F, -r, or -w without argument for more help\n\n",
"\t\t Use -d, -R, -X, -F, -r, or -w without argument for more help\n\n",
DEFAULT_FREQUENCY, DEFAULT_HOP_TIME, DEFAULT_SAMPLE_RATE, DEFAULT_LEVEL_LIMIT);
if (devices) {
@ -176,6 +176,28 @@ void usage(r_device *devices, int exit_code)
exit(exit_code);
}
void help_device(void)
{
fprintf(stderr,
#ifdef RTLSDR
"\tRTL-SDR device driver is available.\n"
#else
"\tRTL-SDR device driver is not available.\n"
#endif
"[-d <RTL-SDR USB device index>] (default: 0)\n"
"[-d :<RTL-SDR USB device serial (can be set with rtl_eeprom -s)>]\n"
"\tTo set gain for RTL-SDR use -g X to set an overall gain in tenths of dB.\n"
#ifdef SOAPYSDR
"\tSoapySDR device driver is available.\n"
#else
"\tSoapySDR device driver is not available.\n"
#endif
"[-d \"\" Open default SoapySDR device\n"
"[-d driver=rtlsdr Open e.g. specific SoapySDR device\n"
"\tTo set gain for SoapySDR use -g ELEM=val,ELEM=val,... e.g. -g LNA=20,TIA=8,PGA=2 (for LimeSDR).\n");
exit(0);
}
void help_output(void)
{
fprintf(stderr,
@ -1177,7 +1199,7 @@ int main(int argc, char **argv) {
FILE *in_file;
int n_read;
int r = 0, opt;
int gain = 0;
char *gain_str = NULL;
int i;
int ppm_error = 0;
struct dm_state *demod;
@ -1225,7 +1247,7 @@ int main(int argc, char **argv) {
demod->hop_time = atoi_time(optarg, "-H: ");
break;
case 'g':
gain = (int) (atof(optarg) * 10); /* tenths of a dB */
gain_str = optarg;
break;
case 'G':
register_all = 1;
@ -1368,6 +1390,7 @@ int main(int argc, char **argv) {
// handle missing arguments as help request
if (optopt == 'R') usage(devices, 0);
else if (optopt == 'X') flex_create_device(NULL);
else if (optopt == 'd') help_device();
else if (optopt == 'F') help_output();
else if (optopt == 'r') help_read();
else if (optopt == 'w') help_write();
@ -1445,13 +1468,8 @@ int main(int argc, char **argv) {
fprintf(stderr, "Bit detection level set to %d%s.\n", demod->level_limit, (demod->level_limit ? "" : " (Auto)"));
if (0 == gain) {
/* Enable automatic gain */
r = sdr_set_auto_gain(dev, !quiet_mode);
} else {
/* Set manual gain */
r = sdr_set_tuner_gain(dev, gain, !quiet_mode);
}
/* Enable automatic gain if gain_str empty (or 0 for RTL-SDR), set manual gain otherwise */
r = sdr_set_tuner_gain(dev, gain_str, !quiet_mode);
if (ppm_error)
r = sdr_set_freq_correction(dev, ppm_error, !quiet_mode);
@ -1534,9 +1552,10 @@ int main(int argc, char **argv) {
}
/* Reset endpoint before we start reading from it (mandatory) */
r = sdr_reset(dev);
r = sdr_reset(dev, !quiet_mode);
if (r < 0)
fprintf(stderr, "WARNING: Failed to reset buffers.\n");
r = sdr_activate(dev);
if (frequencies == 0) {
frequency[0] = DEFAULT_FREQUENCY;
@ -1555,6 +1574,7 @@ int main(int argc, char **argv) {
/* Set the frequency */
center_frequency = frequency[frequency_current];
r = sdr_set_center_freq(dev, center_frequency, !quiet_mode);
#ifndef _WIN32
signal(SIGALRM, sighandler);
alarm(3); // require callback to run every 3 second, abort otherwise
@ -1579,6 +1599,8 @@ int main(int argc, char **argv) {
if (dumper->file && (dumper->file != stdout))
fclose(dumper->file);
r = sdr_deactivate(dev);
for (i = 0; i < demod->r_dev_num; i++)
free(demod->r_devs[i]);

573
src/sdr.c
View file

@ -2,6 +2,10 @@
* SDR input from RTLSDR or SoapySDR
*
* Copyright (C) 2018 Christian Zuckschwerdt
* based on code
* Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de>
* Copyright (C) 2014 by Kyle Keen <keenerd@gmail.com>
* Copyright (C) 2016 by Robert X. Seger
*
* 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
@ -11,17 +15,37 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sdr.h"
#include "rtl-sdr.h"
#include "util.h"
#ifdef RTLSDR
#include "rtl-sdr.h"
#endif
#ifdef SOAPYSDR
#include "optparse.h"
#include <SoapySDR/Device.h>
#include <SoapySDR/Formats.h>
#include <SoapySDR/Logger.h>
#endif
struct sdr_dev {
rtlsdr_dev_t *rtlsdr_dev;
int sample_size;
#ifdef SOAPYSDR
SoapySDRDevice *soapy_dev;
SoapySDRStream *soapy_stream;
double fullScale;
int running;
#endif
#ifdef RTLSDR
rtlsdr_dev_t *rtlsdr_dev;
#endif
int sample_size;
};
int sdr_open(sdr_dev_t **out_dev, int *sample_size, char const *dev_query, int verbose)
/* RTL-SDR helpers */
#ifdef RTLSDR
static int sdr_open_rtl(sdr_dev_t **out_dev, int *sample_size, char *dev_query, int verbose)
{
uint32_t device_count = rtlsdr_get_device_count();
if (!device_count) {
@ -89,18 +113,421 @@ int sdr_open(sdr_dev_t **out_dev, int *sample_size, char const *dev_query, int v
} else {
*out_dev = dev;
}
return r;
}
#endif
/* SoapySDR helpers */
#ifdef SOAPYSDR
static int soapysdr_set_bandwidth(SoapySDRDevice *dev, uint32_t bandwidth)
{
int r;
r = (int)SoapySDRDevice_setBandwidth(dev, SOAPY_SDR_RX, 0, (double)bandwidth);
uint32_t applied_bw = 0;
if (r != 0) {
fprintf(stderr, "WARNING: Failed to set bandwidth.\n");
} else if (bandwidth > 0) {
applied_bw = (uint32_t)SoapySDRDevice_getBandwidth(dev, SOAPY_SDR_RX, 0);
if (applied_bw)
fprintf(stderr, "Bandwidth parameter %u Hz resulted in %u Hz.\n", bandwidth, applied_bw);
else
fprintf(stderr, "Set bandwidth parameter %u Hz.\n", bandwidth);
} else {
fprintf(stderr, "Bandwidth set to automatic resulted in %u Hz.\n", applied_bw);
}
return r;
}
static int soapysdr_direct_sampling(SoapySDRDevice *dev, int on)
{
int r = 0;
char *value, *set_value;
if (on == 0)
value = "0";
else if (on == 1)
value = "1";
else if (on == 2)
value = "2";
else
return -1;
SoapySDRDevice_writeSetting(dev, "direct_samp", value);
set_value = SoapySDRDevice_readSetting(dev, "direct_samp");
if (set_value == NULL) {
fprintf(stderr, "WARNING: Failed to set direct sampling mode.\n");
return r;
}
if (atoi(set_value) == 0) {
fprintf(stderr, "Direct sampling mode disabled.\n");}
if (atoi(set_value) == 1) {
fprintf(stderr, "Enabled direct sampling mode, input 1/I.\n");}
if (atoi(set_value) == 2) {
fprintf(stderr, "Enabled direct sampling mode, input 2/Q.\n");}
if (on == 3) {
fprintf(stderr, "Enabled no-mod direct sampling mode.\n");}
return r;
}
static int soapysdr_offset_tuning(SoapySDRDevice *dev)
{
int r = 0;
SoapySDRDevice_writeSetting(dev, "offset_tune", "true");
char *set_value = SoapySDRDevice_readSetting(dev, "offset_tune");
if (strcmp(set_value, "true") != 0) {
/* TODO: detection of failure modes
if ( r == -2 )
fprintf(stderr, "WARNING: Failed to set offset tuning: tuner doesn't support offset tuning!\n");
else if ( r == -3 )
fprintf(stderr, "WARNING: Failed to set offset tuning: direct sampling not combinable with offset tuning!\n");
else
*/
fprintf(stderr, "WARNING: Failed to set offset tuning.\n");
} else {
fprintf(stderr, "Offset tuning mode enabled.\n");
}
return r;
}
static int soapysdr_auto_gain(SoapySDRDevice *dev, int verbose)
{
int r = 0;
r = SoapySDRDevice_hasGainMode(dev, SOAPY_SDR_RX, 0);
if (r) {
r = SoapySDRDevice_setGainMode(dev, SOAPY_SDR_RX, 0, 1);
if (r != 0) {
fprintf(stderr, "WARNING: Failed to enable automatic gain.\n");
} else {
if (verbose)
fprintf(stderr, "Tuner set to automatic gain.\n");
}
}
// Per-driver hacks TODO: clean this up
char *driver = SoapySDRDevice_getDriverKey(dev);
if (strcmp(driver, "HackRF") == 0) {
// HackRF has three gains LNA, VGA, and AMP, setting total distributes amongst, 116.0 dB seems to work well,
// even though it logs HACKRF_ERROR_INVALID_PARAM? https://github.com/rxseger/rx_tools/issues/9
// Total gain is distributed amongst all gains, 116 = 37,65,1; the LNA is OK (<40) but VGA is out of range (65 > 62)
// TODO: generic means to set all gains, of any SDR? string parsing LNA=#,VGA=#,AMP=#?
r = SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, 0, "LNA", 40.); // max 40
if (r != 0) {
fprintf(stderr, "WARNING: Failed to set LNA tuner gain.\n");
}
r = SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, 0, "VGA", 20.); // max 65
if (r != 0) {
fprintf(stderr, "WARNING: Failed to set VGA tuner gain.\n");
}
r = SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, 0, "AMP", 0.); // on or off
if (r != 0) {
fprintf(stderr, "WARNING: Failed to set AMP tuner gain.\n");
}
}
// otherwise leave unset, hopefully the driver has good defaults
return r;
}
static int soapysdr_gain_str_set(SoapySDRDevice *dev, char *gain_str, int verbose)
{
SoapySDRKwargs args = {0};
int r = 0;
// Disable automatic gain
r = SoapySDRDevice_hasGainMode(dev, SOAPY_SDR_RX, 0);
if (r) {
r = SoapySDRDevice_setGainMode(dev, SOAPY_SDR_RX, 0, 0);
if (r != 0) {
fprintf(stderr, "WARNING: Failed to disable automatic gain.\n");
} else {
if (verbose)
fprintf(stderr, "Tuner set to manual gain.\n");
}
}
if (strchr(gain_str, '=')) {
// Set each gain individually (more control)
char *name;
char *value;
while (getkwargs(&gain_str, &name, &value)) {
double num = atof(value);
if (verbose)
fprintf(stderr, "Setting gain element %s: %f dB\n", name, num);
r = SoapySDRDevice_setGainElement(dev, SOAPY_SDR_RX, 0, name, num);
if (r != 0) {
fprintf(stderr, "WARNING: setGainElement(%s, %f) failed: %d\n", name, num, r);
}
}
} else {
// Set overall gain and let SoapySDR distribute amongst components
double value = atof(gain_str);
r = SoapySDRDevice_setGain(dev, SOAPY_SDR_RX, 0, value);
if (r != 0) {
fprintf(stderr, "WARNING: Failed to set tuner gain.\n");
} else {
if (verbose)
fprintf(stderr, "Tuner gain set to %0.2f dB.\n", value);
}
// read back and print each individual gain element
if (verbose) {
size_t len = 0;
char **gains = SoapySDRDevice_listGains(dev, SOAPY_SDR_RX, 0, &len);
fprintf(stderr, "Gain elements: ");
for (size_t i = 0; i < len; ++i) {
double gain = SoapySDRDevice_getGain(dev, SOAPY_SDR_RX, 0);
fprintf(stderr, "%s=%g ", gains[i], gain);
}
fprintf(stderr, "\n");
}
}
return r;
}
static void soapysdr_show_device_info(SoapySDRDevice *dev)
{
size_t len = 0, i = 0;
char **antennas = NULL;
char **gains = NULL;
char **frequencies = NULL;
SoapySDRRange *frequencyRanges = NULL;
SoapySDRRange *rates = NULL;
SoapySDRRange *bandwidths = NULL;
double fullScale;
char **stream_formats = NULL;
char *native_stream_format = NULL;
SoapySDRKwargs args;
char *hwkey = NULL;
int direction = SOAPY_SDR_RX;
size_t channel = 0;
hwkey = SoapySDRDevice_getHardwareKey(dev);
fprintf(stderr, "Using device %s: ", hwkey);
args = SoapySDRDevice_getHardwareInfo(dev);
for (i = 0; i < args.size; ++i) {
fprintf(stderr, "%s=%s ", args.keys[i], args.vals[i]);
}
fprintf(stderr, "\n");
antennas = SoapySDRDevice_listAntennas(dev, direction, channel, &len);
fprintf(stderr, "Found %zu antenna(s): ", len);
for (i = 0; i < len; ++i) {
fprintf(stderr, "%s ", antennas[i]);
}
fprintf(stderr, "\n");
gains = SoapySDRDevice_listGains(dev, direction, channel, &len);
fprintf(stderr, "Found %zu gain(s): ", len);
for (i = 0; i < len; ++i) {
SoapySDRRange gainRange = SoapySDRDevice_getGainRange(dev, direction, channel);
fprintf(stderr, "%s %.0f - %.0f (step %.0f) ", gains[i], gainRange.minimum, gainRange.maximum, gainRange.step);
}
fprintf(stderr, "\n");
frequencies = SoapySDRDevice_listFrequencies(dev, direction, channel, &len);
fprintf(stderr, "Found %zu frequencies: ", len);
for (i = 0; i < len; ++i) {
fprintf(stderr, "%s ", frequencies[i]);
}
fprintf(stderr, "\n");
frequencyRanges = SoapySDRDevice_getFrequencyRange(dev, direction, channel, &len);
fprintf(stderr, "Found %zu frequency range(s): ", len);
for (i = 0; i < len; ++i) {
fprintf(stderr, "%.0f - %.0f (step %.0f) ", frequencyRanges[i].minimum, frequencyRanges[i].maximum, frequencyRanges[i].step);
}
fprintf(stderr, "\n");
rates = SoapySDRDevice_getSampleRateRange(dev, direction, channel, &len);
fprintf(stderr, "Found %zu sample rate range(s): ", len);
for (i = 0; i < len; ++i) {
if (rates[i].minimum == rates[i].maximum)
fprintf(stderr, "%.0f ", rates[i].minimum);
else
fprintf(stderr, "%.0f - %.0f (step %.0f) ", rates[i].minimum, rates[i].maximum, rates[i].step);
}
fprintf(stderr, "\n");
bandwidths = SoapySDRDevice_getBandwidthRange(dev, direction, channel, &len);
fprintf(stderr, "Found %zu bandwidth range(s): ", len);
for (i = 0; i < len; ++i) {
fprintf(stderr, "%.0f - %.0f (step %.0f) ", bandwidths[i].minimum, bandwidths[i].maximum, bandwidths[i].step);
}
fprintf(stderr, "\n");
double bandwidth = SoapySDRDevice_getBandwidth(dev, direction, channel);
fprintf(stderr, "Found current bandwidth %.0f\n", bandwidth);
stream_formats = SoapySDRDevice_getStreamFormats(dev, direction, channel, &len);
fprintf(stderr, "Found %zu stream format(s): ", len);
for (i = 0; i < len; ++i) {
fprintf(stderr, "%s ", stream_formats[i]);
}
fprintf(stderr, "\n");
native_stream_format = SoapySDRDevice_getNativeStreamFormat(dev, direction, channel, &fullScale);
fprintf(stderr, "Found native stream format: %s (full scale: %.1f)\n", native_stream_format, fullScale);
}
static int sdr_open_soapy(sdr_dev_t **out_dev, int *sample_size, char *dev_query, int verbose)
{
if (verbose)
SoapySDR_setLogLevel(SOAPY_SDR_DEBUG);
sdr_dev_t *dev = calloc(1, sizeof(sdr_dev_t));
dev->soapy_dev = SoapySDRDevice_makeStrArgs(dev_query);
if (!dev->soapy_dev) {
if (verbose)
fprintf(stderr, "Failed to open sdr device matching '%s'.\n", dev_query);
free(dev);
return -1;
}
if (verbose)
soapysdr_show_device_info(dev->soapy_dev);
// select a stream format, in preference order: native CU8, CS8, CS16, forced CS16
// stream_formats = SoapySDRDevice_getStreamFormats(dev->soapy_dev, SOAPY_SDR_RX, 0, &len);
char *format = SoapySDRDevice_getNativeStreamFormat(dev->soapy_dev, SOAPY_SDR_RX, 0, &dev->fullScale);
if (!strcmp(SOAPY_SDR_CU8, format)) {
// actually not supported by SoapySDR
*sample_size = sizeof(uint8_t); // CU8
// } else if (!strcmp(SOAPY_SDR_CS8, format)) {
// // TODO: CS8 needs conversion to CU8
// // e.g. RTL-SDR (8 bit), scale is 128.0
// *sample_size = sizeof(int8_t); // CS8
} else if (!strcmp(SOAPY_SDR_CS16, format)) {
// e.g. LimeSDR-mini (12 bit), scale is 2048.0
*sample_size = sizeof(int16_t); // CS16
} else {
// force CS16
format = SOAPY_SDR_CS16;
*sample_size = sizeof(int16_t); // CS16
dev->fullScale = 32768.0; // assume max for SOAPY_SDR_CS16
}
SoapySDRKwargs stream_args = {0};
if (SoapySDRDevice_setupStream(dev->soapy_dev, &dev->soapy_stream, SOAPY_SDR_RX, format, NULL, 0, &stream_args) != 0) {
if (verbose)
fprintf(stderr, "Failed to setup sdr device\n");
free(dev);
return -3;
}
*out_dev = dev;
return 0;
}
static int soapysdr_read_loop(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
{
size_t buf_elems = buf_len / 2; // div sizeof int16_t, or demod->sample_size, maybe?
int16_t *buffer = malloc(buf_elems * SoapySDR_formatToSize(SOAPY_SDR_CS16));
dev->running = 1;
do {
void *buffs[] = {buffer};
int flags = 0;
long long timeNs = 0;
long timeoutUs = 1000000; // 1 second
unsigned n_read = 0, i;
int r;
do {
buffs[0] = &buffer[n_read * 2];
r = SoapySDRDevice_readStream(dev->soapy_dev, dev->soapy_stream, buffs, buf_elems - n_read, &flags, &timeNs, timeoutUs);
if (r < 0)
break;
n_read += r; // r is number of elements read, elements=complex pairs, so buffer length is twice
//fprintf(stderr, "readStream ret=%d, flags=%d, timeNs=%lld (%zu - %u)\n", r, flags, timeNs, buf_elems, n_read);
} while (n_read < buf_elems);
//fprintf(stderr, "readStream ret=%d (%d), flags=%d, timeNs=%lld\n", n_read, buf_len, flags, timeNs);
if (r < 0) {
if (r == SOAPY_SDR_OVERFLOW) {
fprintf(stderr, "O");
fflush(stderr);
continue;
}
fprintf(stderr, "WARNING: sync read failed. %d\n", r);
}
// convert to CS16 or CU8 if needed
// if converting CS8 to CU8 -- vectorized with -O3
//for (i = 0; i < n_read * 2; ++i)
// cu8buf[i] = (int8_t)cu8buf[i] + 128;
// rescale cs16 buffer
if (dev->fullScale == 2048.0) {
for (i = 0; i < n_read * 2; ++i)
buffer[i] <<= 4;
} else if (dev->fullScale != 32768.0) {
int upscale = 32768 / dev->fullScale;
for (i = 0; i < n_read * 2; ++i)
buffer[i] *= upscale;
}
if (n_read > 0) // prevent a crash in callback
cb((unsigned char *)buffer, n_read * 2 * 2, ctx);
} while (dev->running);
return 0;
}
#endif
/* Public API */
int sdr_open(sdr_dev_t **out_dev, int *sample_size, char *dev_query, int verbose)
{
#if !defined(RTLSDR) && !defined(SOAPYSDR)
if (verbose)
fprintf(stderr, "No input drivers (RTL-SDR or SoapySDR) compiled in.\n");
return -1;
#endif
#ifdef RTLSDR
/* Open RTLSDR by default or if index or serial given, if available */
if (!dev_query || *dev_query == ':' || (*dev_query >= '0' && *dev_query <= '9'))
return sdr_open_rtl(out_dev, sample_size, dev_query, verbose);
#endif
#ifdef SOAPYSDR
/* Open SoapySDR otherwise, if available */
return sdr_open_soapy(out_dev, sample_size, dev_query, verbose);
#endif
}
int sdr_close(sdr_dev_t *dev)
{
return rtlsdr_close(dev->rtlsdr_dev);
#ifdef SOAPYSDR
if (dev->soapy_dev)
return SoapySDRDevice_unmake(dev->soapy_dev);
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev)
return rtlsdr_close(dev->rtlsdr_dev);
#endif
return -1;
}
int sdr_set_center_freq(sdr_dev_t *dev, uint32_t freq, int verbose)
{
int r = rtlsdr_set_center_freq(dev->rtlsdr_dev, freq);
int r = -1;
#ifdef SOAPYSDR
SoapySDRKwargs args = {0};
if (dev->soapy_dev) {
r = SoapySDRDevice_setFrequency(dev->soapy_dev, SOAPY_SDR_RX, 0, (double)freq, &args);
}
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev)
r = rtlsdr_set_center_freq(dev->rtlsdr_dev, freq);
#endif
if (verbose) {
if (r < 0)
fprintf(stderr, "WARNING: Failed to set center freq.\n");
@ -112,24 +539,48 @@ int sdr_set_center_freq(sdr_dev_t *dev, uint32_t freq, int verbose)
uint32_t sdr_get_center_freq(sdr_dev_t *dev)
{
return rtlsdr_get_center_freq(dev->rtlsdr_dev);
#ifdef SOAPYSDR
if (dev->soapy_dev)
return (int)SoapySDRDevice_getFrequency(dev->soapy_dev, SOAPY_SDR_RX, 0);
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev)
return rtlsdr_get_center_freq(dev->rtlsdr_dev);
#endif
return 0;
}
int sdr_set_freq_correction(sdr_dev_t *dev, int ppm, int verbose)
{
int r = rtlsdr_set_freq_correction(dev->rtlsdr_dev, ppm);
int r = -1;
#ifdef SOAPYSDR
if (dev->soapy_dev)
r = SoapySDRDevice_setFrequencyComponent(dev->soapy_dev, SOAPY_SDR_RX, 0, "CORR", (double)ppm, NULL);
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev)
r = rtlsdr_set_freq_correction(dev->rtlsdr_dev, ppm);
#endif
if (verbose) {
if (r < 0)
fprintf(stderr, "WARNING: Failed to set frequency correction.\n");
else
fprintf(stderr, "Frequency correction set to %d.\n", ppm);
fprintf(stderr, "Frequency correction set to %d ppm.\n", ppm);
}
return r;
}
int sdr_set_auto_gain(sdr_dev_t *dev, int verbose)
{
int r = rtlsdr_set_tuner_gain_mode(dev->rtlsdr_dev, 0);
int r = -1;
#ifdef SOAPYSDR
if (dev->soapy_dev)
r = soapysdr_auto_gain(dev->soapy_dev, verbose);
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev)
r = rtlsdr_set_tuner_gain_mode(dev->rtlsdr_dev, 0);
#endif
if (verbose) {
if (r < 0)
fprintf(stderr, "WARNING: Failed to enable automatic gain.\n");
@ -139,8 +590,24 @@ int sdr_set_auto_gain(sdr_dev_t *dev, int verbose)
return r;
}
int sdr_set_tuner_gain(sdr_dev_t *dev, int gain, int verbose)
int sdr_set_tuner_gain(sdr_dev_t *dev, char *gain_str, int verbose)
{
if (!gain_str || !*gain_str) {
/* Enable automatic gain */
return sdr_set_auto_gain(dev, verbose);
}
#ifdef SOAPYSDR
/* Enable manual gain */
if (dev->soapy_dev)
return soapysdr_gain_str_set(dev->soapy_dev, gain_str, verbose);
#endif
#ifdef RTLSDR
int gain = (int)(atof(gain_str) * 10); /* tenths of a dB */
if (gain == 0) {
/* Enable automatic gain */
return sdr_set_auto_gain(dev, verbose);
}
/* Enable manual gain */
int r = rtlsdr_set_tuner_gain_mode(dev->rtlsdr_dev, 1);
if (verbose)
@ -156,35 +623,105 @@ int sdr_set_tuner_gain(sdr_dev_t *dev, int gain, int verbose)
fprintf(stderr, "Tuner gain set to %f dB.\n", gain / 10.0);
}
return r;
#endif
return -1;
}
int sdr_set_sample_rate(sdr_dev_t *dev, uint32_t rate, int verbose)
{
int r = rtlsdr_set_sample_rate(dev->rtlsdr_dev, rate);
int r = -1;
#ifdef SOAPYSDR
if (dev->soapy_dev)
r = SoapySDRDevice_setSampleRate(dev->soapy_dev, SOAPY_SDR_RX, 0, (double)rate);
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev)
r = rtlsdr_set_sample_rate(dev->rtlsdr_dev, rate);
#endif
if (verbose) {
if (r < 0)
fprintf(stderr, "WARNING: Failed to set sample rate.\n");
else
fprintf(stderr, "Sample rate set to %d.\n", sdr_get_sample_rate(dev)); // Unfortunately, doesn't return real rate
fprintf(stderr, "Sample rate set to %d S/s.\n", sdr_get_sample_rate(dev)); // Unfortunately, doesn't return real rate
}
return r;
}
uint32_t sdr_get_sample_rate(sdr_dev_t *dev)
{
return rtlsdr_get_sample_rate(dev->rtlsdr_dev);
#ifdef SOAPYSDR
if (dev->soapy_dev)
return (int)SoapySDRDevice_getSampleRate(dev->soapy_dev, SOAPY_SDR_RX, 0);
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev)
return rtlsdr_get_sample_rate(dev->rtlsdr_dev);
#endif
return 0;
}
int sdr_reset(sdr_dev_t *dev)
int sdr_activate(sdr_dev_t *dev)
{
return rtlsdr_reset_buffer(dev->rtlsdr_dev);
#ifdef SOAPYSDR
if (dev->soapy_dev) {
if (SoapySDRDevice_activateStream(dev->soapy_dev, dev->soapy_stream, 0, 0, 0) != 0) {
fprintf(stderr, "Failed to activate stream\n");
exit(1);
}
}
#endif
return 0;
}
int sdr_deactivate(sdr_dev_t *dev)
{
#ifdef SOAPYSDR
if (dev->soapy_dev) {
SoapySDRDevice_deactivateStream(dev->soapy_dev, dev->soapy_stream, 0, 0);
SoapySDRDevice_closeStream(dev->soapy_dev, dev->soapy_stream);
}
#endif
return 0;
}
int sdr_reset(sdr_dev_t *dev, int verbose)
{
int r = 0;
#ifdef RTLSDR
if (dev->rtlsdr_dev)
r = rtlsdr_reset_buffer(dev->rtlsdr_dev);
#endif
if (verbose) {
if (r < 0)
fprintf(stderr, "WARNING: Failed to reset buffers.\n");
}
return r;
}
int sdr_start(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len)
{
return rtlsdr_read_async(dev->rtlsdr_dev, cb, ctx, buf_num, buf_len);
#ifdef SOAPYSDR
if (dev->soapy_dev)
return soapysdr_read_loop(dev, cb, ctx, buf_num, buf_len);
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev)
return rtlsdr_read_async(dev->rtlsdr_dev, cb, ctx, buf_num, buf_len);
#endif
return -1;
}
int sdr_stop(sdr_dev_t *dev)
{
return rtlsdr_cancel_async(dev->rtlsdr_dev);
#ifdef SOAPYSDR
if (dev->soapy_dev) {
dev->running = 0;
return 0;
}
#endif
#ifdef RTLSDR
if (dev->rtlsdr_dev)
return rtlsdr_cancel_async(dev->rtlsdr_dev);
#endif
return -1;
}