SoapySDR support
There is no time stamp handling. It is just TX'ing in advance and synchronous to received RX rate.
This commit is contained in:
parent
17b6124122
commit
438ce08add
15
configure.ac
15
configure.ac
|
@ -26,10 +26,17 @@ AC_CANONICAL_HOST
|
||||||
|
|
||||||
PKG_CHECK_MODULES(ALSA, alsa >= 1.0)
|
PKG_CHECK_MODULES(ALSA, alsa >= 1.0)
|
||||||
|
|
||||||
have_sdr=no
|
|
||||||
PKG_CHECK_MODULES(UHD, uhd >= 3.0.0, have_sdr=yes have_uhd=yes, have_uhd=no)
|
with_sdr=no
|
||||||
AM_CONDITIONAL(HAVE_UHD, test "x$have_uhd" == "xyes" )
|
AC_ARG_WITH([uhd], [AS_HELP_STRING([--with-uhd], [compile with UHD driver @<:@default=check@:>@]) ], [], [with_uhd="check"])
|
||||||
AM_CONDITIONAL(HAVE_SDR, test "x$have_sdr" == "xyes" )
|
AC_ARG_WITH([soapy], [AS_HELP_STRING([--with-soapy], [compile with SoapySDR driver @<:@default=check@:>@]) ], [], [with_soapy="check"])
|
||||||
|
AS_IF([test "x$with_uhd" != xno], [PKG_CHECK_MODULES(UHD, uhd >= 3.0.0, with_sdr=yes with_uhd=yes, with_uhd=no)])
|
||||||
|
AS_IF([test "x$with_soapy" != xno], [PKG_CHECK_MODULES(SOAPY, SoapySDR >= 0.6.0, with_sdr=yes with_soapy=yes, with_soapy=no)])
|
||||||
|
AM_CONDITIONAL(HAVE_UHD, test "x$with_uhd" == "xyes" )
|
||||||
|
AM_CONDITIONAL(HAVE_SOAPY, test "x$with_soapy" == "xyes" )
|
||||||
|
AM_CONDITIONAL(HAVE_SDR, test "x$with_sdr" == "xyes" )
|
||||||
|
AS_IF([test "x$with_uhd" == "xyes"],[AC_MSG_NOTICE( Compiling with UHD SDR support )], [])
|
||||||
|
AS_IF([test "x$with_soapy" == "xyes"],[AC_MSG_NOTICE( Compiling with SoapySDR support )], [])
|
||||||
|
|
||||||
AC_OUTPUT(
|
AC_OUTPUT(
|
||||||
src/common/Makefile
|
src/common/Makefile
|
||||||
|
|
|
@ -22,5 +22,6 @@ amps_LDADD = \
|
||||||
$(top_builddir)/src/common/libcommon.a \
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
$(ALSA_LIBS) \
|
$(ALSA_LIBS) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS) \
|
||||||
-lm
|
-lm
|
||||||
|
|
||||||
|
|
|
@ -14,5 +14,6 @@ anetz_LDADD = \
|
||||||
$(top_builddir)/src/common/libcommon.a \
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
$(ALSA_LIBS) \
|
$(ALSA_LIBS) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS) \
|
||||||
-lm
|
-lm
|
||||||
|
|
||||||
|
|
|
@ -16,5 +16,6 @@ bnetz_LDADD = \
|
||||||
$(top_builddir)/src/common/libcommon.a \
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
$(ALSA_LIBS) \
|
$(ALSA_LIBS) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS) \
|
||||||
-lm
|
-lm
|
||||||
|
|
||||||
|
|
|
@ -20,5 +20,6 @@ cnetz_LDADD = \
|
||||||
$(top_builddir)/src/common/libcommon.a \
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
$(ALSA_LIBS) \
|
$(ALSA_LIBS) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS) \
|
||||||
-lm
|
-lm
|
||||||
|
|
||||||
|
|
|
@ -45,3 +45,10 @@ libcommon_a_SOURCES += \
|
||||||
../common/uhd.c
|
../common/uhd.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if HAVE_SOAPY
|
||||||
|
AM_CPPFLAGS += -DHAVE_SOAPY
|
||||||
|
|
||||||
|
libcommon_a_SOURCES += \
|
||||||
|
../common/soapy.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
#include <errno.h>
|
||||||
#include "sample.h"
|
#include "sample.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
@ -61,6 +62,8 @@ const char *write_rx_wave = NULL;
|
||||||
const char *write_tx_wave = NULL;
|
const char *write_tx_wave = NULL;
|
||||||
const char *read_rx_wave = NULL;
|
const char *read_rx_wave = NULL;
|
||||||
static const char *sdr_args = "";
|
static const char *sdr_args = "";
|
||||||
|
static int sdr_uhd = 0;
|
||||||
|
static int sdr_soapy = 0;
|
||||||
double sdr_rx_gain = 0, sdr_tx_gain = 0;
|
double sdr_rx_gain = 0, sdr_tx_gain = 0;
|
||||||
const char *write_iq_rx_wave = NULL;
|
const char *write_iq_rx_wave = NULL;
|
||||||
const char *write_iq_tx_wave = NULL;
|
const char *write_iq_tx_wave = NULL;
|
||||||
|
@ -124,8 +127,17 @@ void print_help_common(const char *arg0, const char *ext_usage)
|
||||||
printf(" Replace received audio by given wave file.\n");
|
printf(" Replace received audio by given wave file.\n");
|
||||||
#ifdef HAVE_SDR
|
#ifdef HAVE_SDR
|
||||||
printf("\nSDR options:\n");
|
printf("\nSDR options:\n");
|
||||||
|
#ifdef HAVE_UHD
|
||||||
|
printf(" --sdr-uhd\n");
|
||||||
|
printf(" Force UHD driver\n");
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SOAPY
|
||||||
|
printf(" --sdr-soapy\n");
|
||||||
|
printf(" Force SoapySDR driver\n");
|
||||||
|
#endif
|
||||||
printf(" --sdr-args <args>\n");
|
printf(" --sdr-args <args>\n");
|
||||||
printf(" Optional SDR device arguments\n");
|
printf(" Optional SDR device arguments, seperated by comma\n");
|
||||||
|
printf(" e.g. --sdr-args <key>=<value>[,<key>=<value>[,...]]\n");
|
||||||
printf(" --sdr-rx-gain <gain>\n");
|
printf(" --sdr-rx-gain <gain>\n");
|
||||||
printf(" SDR device's RX gain in dB (default = %.1f)\n", sdr_rx_gain);
|
printf(" SDR device's RX gain in dB (default = %.1f)\n", sdr_rx_gain);
|
||||||
printf(" --sdr-tx-gain <gain>\n");
|
printf(" --sdr-tx-gain <gain>\n");
|
||||||
|
@ -154,12 +166,14 @@ void print_hotkeys_common(void)
|
||||||
#define OPT_READ_RX_WAVE 1003
|
#define OPT_READ_RX_WAVE 1003
|
||||||
#define OPT_CALL_SAMPLERATE 1004
|
#define OPT_CALL_SAMPLERATE 1004
|
||||||
|
|
||||||
#define OPT_SDR_ARGS 1100
|
#define OPT_SDR_UHD 1100
|
||||||
#define OPT_SDR_RX_GAIN 1101
|
#define OPT_SDR_SOAPY 1101
|
||||||
#define OPT_SDR_TX_GAIN 1102
|
#define OPT_SDR_ARGS 1102
|
||||||
#define OPT_WRITE_IQ_RX_WAVE 1103
|
#define OPT_SDR_RX_GAIN 1103
|
||||||
#define OPT_WRITE_IQ_TX_WAVE 1104
|
#define OPT_SDR_TX_GAIN 1104
|
||||||
#define OPT_READ_IQ_RX_WAVE 1105
|
#define OPT_WRITE_IQ_RX_WAVE 1105
|
||||||
|
#define OPT_WRITE_IQ_TX_WAVE 1106
|
||||||
|
#define OPT_READ_IQ_RX_WAVE 1107
|
||||||
|
|
||||||
static struct option long_options_common[] = {
|
static struct option long_options_common[] = {
|
||||||
{"help", 0, 0, 'h'},
|
{"help", 0, 0, 'h'},
|
||||||
|
@ -172,7 +186,7 @@ static struct option long_options_common[] = {
|
||||||
{"buffer", 1, 0, 'b'},
|
{"buffer", 1, 0, 'b'},
|
||||||
{"pre-emphasis", 0, 0, 'p'},
|
{"pre-emphasis", 0, 0, 'p'},
|
||||||
{"de-emphasis", 0, 0, 'd'},
|
{"de-emphasis", 0, 0, 'd'},
|
||||||
{"rx-gain", 0, 0, 'g'},
|
{"rx-gain", 1, 0, 'g'},
|
||||||
{"mncc-sock", 0, 0, 'm'},
|
{"mncc-sock", 0, 0, 'm'},
|
||||||
{"call-device", 1, 0, 'c'},
|
{"call-device", 1, 0, 'c'},
|
||||||
{"call-samplerate", 1, 0, OPT_CALL_SAMPLERATE},
|
{"call-samplerate", 1, 0, OPT_CALL_SAMPLERATE},
|
||||||
|
@ -182,6 +196,8 @@ static struct option long_options_common[] = {
|
||||||
{"write-rx-wave", 1, 0, OPT_WRITE_RX_WAVE},
|
{"write-rx-wave", 1, 0, OPT_WRITE_RX_WAVE},
|
||||||
{"write-tx-wave", 1, 0, OPT_WRITE_TX_WAVE},
|
{"write-tx-wave", 1, 0, OPT_WRITE_TX_WAVE},
|
||||||
{"read-rx-wave", 1, 0, OPT_READ_RX_WAVE},
|
{"read-rx-wave", 1, 0, OPT_READ_RX_WAVE},
|
||||||
|
{"sdr-uhd", 0, 0, OPT_SDR_UHD},
|
||||||
|
{"sdr-soapy", 0, 0, OPT_SDR_SOAPY},
|
||||||
{"sdr-args", 1, 0, OPT_SDR_ARGS},
|
{"sdr-args", 1, 0, OPT_SDR_ARGS},
|
||||||
{"sdr-rx-gain", 1, 0, OPT_SDR_RX_GAIN},
|
{"sdr-rx-gain", 1, 0, OPT_SDR_RX_GAIN},
|
||||||
{"sdr-tx-gain", 1, 0, OPT_SDR_TX_GAIN},
|
{"sdr-tx-gain", 1, 0, OPT_SDR_TX_GAIN},
|
||||||
|
@ -331,6 +347,14 @@ void opt_switch_common(int c, char *arg0, int *skip_args)
|
||||||
read_rx_wave = strdup(optarg);
|
read_rx_wave = strdup(optarg);
|
||||||
*skip_args += 2;
|
*skip_args += 2;
|
||||||
break;
|
break;
|
||||||
|
case OPT_SDR_UHD:
|
||||||
|
sdr_uhd = 1;
|
||||||
|
*skip_args += 1;
|
||||||
|
break;
|
||||||
|
case OPT_SDR_SOAPY:
|
||||||
|
sdr_soapy = 1;
|
||||||
|
*skip_args += 1;
|
||||||
|
break;
|
||||||
case OPT_SDR_ARGS:
|
case OPT_SDR_ARGS:
|
||||||
sdr_args = strdup(optarg);
|
sdr_args = strdup(optarg);
|
||||||
*skip_args += 2;
|
*skip_args += 2;
|
||||||
|
@ -413,8 +437,25 @@ int init_common(const char *station_id, int station_id_digits)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_UHD
|
||||||
|
#ifdef HAVE_SOAPY
|
||||||
|
if ((sdr_uhd == 1 && sdr_soapy == 1) || (sdr_uhd == 0 && sdr_soapy == 0)) {
|
||||||
|
fprintf(stderr, "UHD and SoapySDR drivers are compiled in. You must choose which one you want: --sdr-uhd or --sdr-soapy\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
sdr_uhd = 1;
|
||||||
|
sdr_soapy = 0;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifdef HAVE_SOAPY
|
||||||
|
sdr_uhd = 0;
|
||||||
|
sdr_soapy = 1;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SDR
|
#ifdef HAVE_SDR
|
||||||
rc = sdr_init(sdr_args, sdr_rx_gain, sdr_tx_gain, write_iq_rx_wave, write_iq_tx_wave, read_iq_rx_wave);
|
rc = sdr_init(sdr_uhd, sdr_soapy, sdr_args, sdr_rx_gain, sdr_tx_gain, write_iq_rx_wave, write_iq_tx_wave, read_iq_rx_wave);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
#ifdef HAVE_UHD
|
#ifdef HAVE_UHD
|
||||||
#include "uhd.h"
|
#include "uhd.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_SOAPY
|
||||||
|
#include "soapy.h"
|
||||||
|
#endif
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
typedef struct sdr_chan {
|
typedef struct sdr_chan {
|
||||||
|
@ -50,12 +53,15 @@ typedef struct sdr {
|
||||||
wave_play_t wave_rx_play;
|
wave_play_t wave_rx_play;
|
||||||
} sdr_t;
|
} sdr_t;
|
||||||
|
|
||||||
|
static int sdr_use_uhd, sdr_use_soapy;
|
||||||
static const char *sdr_device_args;
|
static const char *sdr_device_args;
|
||||||
static double sdr_rx_gain, sdr_tx_gain;
|
static double sdr_rx_gain, sdr_tx_gain;
|
||||||
const char *sdr_write_iq_rx_wave, *sdr_write_iq_tx_wave, *sdr_read_iq_rx_wave;
|
const char *sdr_write_iq_rx_wave, *sdr_write_iq_tx_wave, *sdr_read_iq_rx_wave;
|
||||||
|
|
||||||
int sdr_init(const char *device_args, double rx_gain, double tx_gain, const char *write_iq_rx_wave, const char *write_iq_tx_wave, const char *read_iq_rx_wave)
|
int sdr_init(int sdr_uhd, int sdr_soapy, const char *device_args, double rx_gain, double tx_gain, const char *write_iq_rx_wave, const char *write_iq_tx_wave, const char *read_iq_rx_wave)
|
||||||
{
|
{
|
||||||
|
sdr_use_uhd = sdr_uhd;
|
||||||
|
sdr_use_soapy = sdr_soapy;
|
||||||
sdr_device_args = strdup(device_args);
|
sdr_device_args = strdup(device_args);
|
||||||
sdr_rx_gain = rx_gain;
|
sdr_rx_gain = rx_gain;
|
||||||
sdr_tx_gain = tx_gain;
|
sdr_tx_gain = tx_gain;
|
||||||
|
@ -199,9 +205,19 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_UHD
|
#ifdef HAVE_UHD
|
||||||
rc = uhd_open(sdr_device_args, tx_center_frequency, rx_center_frequency, sdr->samplerate, sdr_rx_gain, sdr_tx_gain);
|
if (sdr_use_uhd) {
|
||||||
if (rc)
|
rc = uhd_open(sdr_device_args, tx_center_frequency, rx_center_frequency, sdr->samplerate, sdr_rx_gain, sdr_tx_gain);
|
||||||
goto error;
|
if (rc)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SOAPY
|
||||||
|
if (sdr_use_soapy) {
|
||||||
|
rc = soapy_open(sdr_device_args, tx_center_frequency, rx_center_frequency, sdr->samplerate, sdr_rx_gain, sdr_tx_gain);
|
||||||
|
if (rc)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return sdr;
|
return sdr;
|
||||||
|
@ -216,7 +232,13 @@ void sdr_close(void *inst)
|
||||||
sdr_t *sdr = (sdr_t *)inst;
|
sdr_t *sdr = (sdr_t *)inst;
|
||||||
|
|
||||||
#ifdef HAVE_UHD
|
#ifdef HAVE_UHD
|
||||||
uhd_close();
|
if (sdr_use_uhd)
|
||||||
|
uhd_close();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SOAPY
|
||||||
|
if (sdr_use_soapy)
|
||||||
|
soapy_close();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (sdr) {
|
if (sdr) {
|
||||||
|
@ -234,7 +256,7 @@ int sdr_write(void *inst, sample_t **samples, int num, enum paging_signal __attr
|
||||||
sdr_t *sdr = (sdr_t *)inst;
|
sdr_t *sdr = (sdr_t *)inst;
|
||||||
float buff[num * 2];
|
float buff[num * 2];
|
||||||
int c, s, ss;
|
int c, s, ss;
|
||||||
int sent;
|
int sent = 0;
|
||||||
|
|
||||||
if (channels != sdr->channels) {
|
if (channels != sdr->channels) {
|
||||||
PDEBUG(DSDR, DEBUG_ERROR, "Invalid number of channels, please fix!\n");
|
PDEBUG(DSDR, DEBUG_ERROR, "Invalid number of channels, please fix!\n");
|
||||||
|
@ -261,7 +283,12 @@ int sdr_write(void *inst, sample_t **samples, int num, enum paging_signal __attr
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_UHD
|
#ifdef HAVE_UHD
|
||||||
sent = uhd_send(buff, num);
|
if (sdr_use_uhd)
|
||||||
|
sent = uhd_send(buff, num);
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SOAPY
|
||||||
|
if (sdr_use_soapy)
|
||||||
|
sent = soapy_send(buff, num);
|
||||||
#endif
|
#endif
|
||||||
if (sent < 0)
|
if (sent < 0)
|
||||||
return sent;
|
return sent;
|
||||||
|
@ -273,11 +300,16 @@ int sdr_read(void *inst, sample_t **samples, int num, int channels)
|
||||||
{
|
{
|
||||||
sdr_t *sdr = (sdr_t *)inst;
|
sdr_t *sdr = (sdr_t *)inst;
|
||||||
float buff[num * 2];
|
float buff[num * 2];
|
||||||
int count;
|
int count = 0;
|
||||||
int c, s, ss;
|
int c, s, ss;
|
||||||
|
|
||||||
#ifdef HAVE_UHD
|
#ifdef HAVE_UHD
|
||||||
count = uhd_receive(buff, num);
|
if (sdr_use_uhd)
|
||||||
|
count = uhd_receive(buff, num);
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SOAPY
|
||||||
|
if (sdr_use_soapy)
|
||||||
|
count = soapy_receive(buff, num);
|
||||||
#endif
|
#endif
|
||||||
if (count <= 0)
|
if (count <= 0)
|
||||||
return count;
|
return count;
|
||||||
|
@ -312,10 +344,15 @@ int sdr_read(void *inst, sample_t **samples, int num, int channels)
|
||||||
int sdr_get_inbuffer(void __attribute__((__unused__)) *inst)
|
int sdr_get_inbuffer(void __attribute__((__unused__)) *inst)
|
||||||
{
|
{
|
||||||
// sdr_t *sdr = (sdr_t *)inst;
|
// sdr_t *sdr = (sdr_t *)inst;
|
||||||
int count;
|
int count = 0;
|
||||||
|
|
||||||
#ifdef HAVE_UHD
|
#ifdef HAVE_UHD
|
||||||
count = uhd_get_inbuffer();
|
if (sdr_use_uhd)
|
||||||
|
count = uhd_get_inbuffer();
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SOAPY
|
||||||
|
if (sdr_use_soapy)
|
||||||
|
count = soapy_get_inbuffer();
|
||||||
#endif
|
#endif
|
||||||
if (count < 0)
|
if (count < 0)
|
||||||
return count;
|
return count;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
int sdr_init(const char *device_args, double rx_gain, double tx_gain, const char *write_iq_rx_wave, const char *write_iq_tx_wave, const char *read_iq_rx_wave);
|
int sdr_init(int sdr_uhd, int sdr_soapy, const char *device_args, double rx_gain, double tx_gain, const char *write_iq_rx_wave, const char *write_iq_tx_wave, const char *read_iq_rx_wave);
|
||||||
void *sdr_open(const char *audiodev, double *tx_frequency, double *rx_frequency, int channels, double paging_frequency, int samplerate, double bandwidth, double sample_deviation);
|
void *sdr_open(const char *audiodev, double *tx_frequency, double *rx_frequency, int channels, double paging_frequency, int samplerate, double bandwidth, double sample_deviation);
|
||||||
void sdr_close(void *inst);
|
void sdr_close(void *inst);
|
||||||
int sdr_write(void *inst, sample_t **samples, int num, enum paging_signal *paging_signal, int *on, int channels);
|
int sdr_write(void *inst, sample_t **samples, int num, enum paging_signal *paging_signal, int *on, int channels);
|
||||||
|
|
|
@ -0,0 +1,281 @@
|
||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <SoapySDR/Device.h>
|
||||||
|
#include <SoapySDR/Formats.h>
|
||||||
|
#include "soapy.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
static SoapySDRDevice *sdr = NULL;
|
||||||
|
SoapySDRStream *rxStream = NULL;
|
||||||
|
SoapySDRStream *txStream = NULL;
|
||||||
|
static int tx_samps_per_buff, rx_samps_per_buff;
|
||||||
|
static double samplerate;
|
||||||
|
static long long rx_count = 0;
|
||||||
|
static long long tx_count = 0;
|
||||||
|
|
||||||
|
int soapy_open(const char *device_args, double tx_frequency, double rx_frequency, double rate, double rx_gain, double tx_gain)
|
||||||
|
{
|
||||||
|
double got_frequency, got_rate, got_gain;
|
||||||
|
size_t channel = 0;
|
||||||
|
char *arg_string = strdup(device_args), *key, *val;
|
||||||
|
SoapySDRKwargs args;
|
||||||
|
|
||||||
|
samplerate = rate;
|
||||||
|
|
||||||
|
/* create SoapySDR device */
|
||||||
|
PDEBUG(DUHD, DEBUG_INFO, "Creating SoapySDR with args \"%s\"...\n", arg_string);
|
||||||
|
memset(&args, 0, sizeof(args));
|
||||||
|
while (arg_string && *arg_string) {
|
||||||
|
key = arg_string;
|
||||||
|
val = strchr(key, '=');
|
||||||
|
if (!val) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Error parsing SDR args: No '=' after key\n");
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
val++;
|
||||||
|
arg_string = strchr(val, ',');
|
||||||
|
if (arg_string)
|
||||||
|
*arg_string++ = '\0';
|
||||||
|
SoapySDRKwargs_set(&args, key, val);
|
||||||
|
}
|
||||||
|
sdr = SoapySDRDevice_make(&args);
|
||||||
|
if (!sdr) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to create SoapySDR\n");
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set rate */
|
||||||
|
if (SoapySDRDevice_setSampleRate(sdr, SOAPY_SDR_TX, channel, rate) != 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX rate to %.0f Hz\n", rate);
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
if (SoapySDRDevice_setSampleRate(sdr, SOAPY_SDR_RX, channel, rate) != 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX rate to %.0f Hz\n", rate);
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see what rate actually is */
|
||||||
|
got_rate = SoapySDRDevice_getSampleRate(sdr, SOAPY_SDR_TX, channel);
|
||||||
|
if (got_rate != rate) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Given TX rate %.0f Hz is not supported, try %0.f Hz\n", rate, got_rate);
|
||||||
|
soapy_close();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
got_rate = SoapySDRDevice_getSampleRate(sdr, SOAPY_SDR_RX, channel);
|
||||||
|
if (got_rate != rate) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Given RX rate %.0f Hz is not supported, try %0.f Hz\n", rate, got_rate);
|
||||||
|
soapy_close();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set gain */
|
||||||
|
if (SoapySDRDevice_setGain(sdr, SOAPY_SDR_TX, channel, tx_gain) != 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX gain to %.0f\n", tx_gain);
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
if (SoapySDRDevice_setGain(sdr, SOAPY_SDR_RX, channel, rx_gain) != 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX gain to %.0f\n", rx_gain);
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see what gain actually is */
|
||||||
|
got_gain = SoapySDRDevice_getGain(sdr, SOAPY_SDR_TX, channel);
|
||||||
|
if (got_gain != tx_gain) {
|
||||||
|
PDEBUG(DUHD, DEBUG_NOTICE, "Given TX gain %.0f is not supported, we use %0.f\n", tx_gain, got_gain);
|
||||||
|
tx_gain = got_gain;
|
||||||
|
}
|
||||||
|
got_gain = SoapySDRDevice_getGain(sdr, SOAPY_SDR_RX, channel);
|
||||||
|
if (got_gain != rx_gain) {
|
||||||
|
PDEBUG(DUHD, DEBUG_NOTICE, "Given RX gain %.3f is not supported, we use %.3f\n", rx_gain, got_gain);
|
||||||
|
rx_gain = got_gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set frequency */
|
||||||
|
if (SoapySDRDevice_setFrequency(sdr, SOAPY_SDR_TX, channel, tx_frequency, NULL) != 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX frequency to %.0f Hz\n", tx_frequency);
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
if (SoapySDRDevice_setFrequency(sdr, SOAPY_SDR_RX, channel, rx_frequency, NULL) != 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX frequency to %.0f Hz\n", rx_frequency);
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see what frequency actually is */
|
||||||
|
got_frequency = SoapySDRDevice_getFrequency(sdr, SOAPY_SDR_TX, channel);
|
||||||
|
if (got_frequency != tx_frequency) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Given TX frequency %.0f Hz is not supported, try %0.f Hz\n", tx_frequency, got_frequency);
|
||||||
|
soapy_close();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
got_frequency = SoapySDRDevice_getFrequency(sdr, SOAPY_SDR_RX, channel);
|
||||||
|
if (got_frequency != rx_frequency) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Given RX frequency %.0f Hz is not supported, try %0.f Hz\n", rx_frequency, got_frequency);
|
||||||
|
soapy_close();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set up streamer */
|
||||||
|
if (SoapySDRDevice_setupStream(sdr, &txStream, SOAPY_SDR_TX, SOAPY_SDR_CF32, &channel, 1, NULL) != 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX streamer args\n");
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
if (SoapySDRDevice_setupStream(sdr, &rxStream, SOAPY_SDR_RX, SOAPY_SDR_CF32, &channel, 1, NULL) != 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX streamer args\n");
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get buffer sizes */
|
||||||
|
tx_samps_per_buff = SoapySDRDevice_getStreamMTU(sdr, txStream);
|
||||||
|
if (tx_samps_per_buff == 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX streamer sample buffer\n");
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
rx_samps_per_buff = SoapySDRDevice_getStreamMTU(sdr, rxStream);
|
||||||
|
if (rx_samps_per_buff == 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX streamer sample buffer\n");
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable rx stream */
|
||||||
|
if (SoapySDRDevice_activateStream(sdr, rxStream, 0, 0, 0) != 0) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to issue RX stream command\n");
|
||||||
|
soapy_close();
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void soapy_close(void)
|
||||||
|
{
|
||||||
|
PDEBUG(DUHD, DEBUG_DEBUG, "Clean up UHD\n");
|
||||||
|
if (txStream) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
/* create tx metadata */
|
||||||
|
buffs_ptr[0] = buff;
|
||||||
|
count = SoapySDRDevice_writeStream(sdr, txStream, buffs_ptr, chunk, &flags, 0, 0);
|
||||||
|
if (count <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* increment tx counter */
|
||||||
|
tx_count += count;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
PDEBUG(DUHD, DEBUG_ERROR, "SDR rx buffer overflow!\n");
|
||||||
|
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) {
|
||||||
|
/* update current rx time */
|
||||||
|
rx_count += count;
|
||||||
|
/* commit received data to buffer */
|
||||||
|
got += count;
|
||||||
|
buff += count * 2;
|
||||||
|
max -= count;
|
||||||
|
} else {
|
||||||
|
/* got nothing this time */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* estimate current unsent number of samples */
|
||||||
|
int soapy_get_inbuffer(void)
|
||||||
|
{
|
||||||
|
long long advance;
|
||||||
|
|
||||||
|
/* we need the rx time stamp to determine how much data is already sent in advance */
|
||||||
|
if (rx_count == 0)
|
||||||
|
return -EAGAIN;
|
||||||
|
|
||||||
|
/* if we have not yet sent any data, we set initial tx time stamp */
|
||||||
|
if (tx_count == 0)
|
||||||
|
tx_count = rx_count;
|
||||||
|
|
||||||
|
/* we check how advance our transmitted time stamp is */
|
||||||
|
advance = tx_count - rx_count;
|
||||||
|
/* in case of underrun: */
|
||||||
|
if (advance < 0)
|
||||||
|
advance = 0;
|
||||||
|
|
||||||
|
return advance;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
int soapy_open(const char *device_args, double tx_frequency, double rx_frequency, double rate, double rx_gain, double tx_gain);
|
||||||
|
void soapy_close(void);
|
||||||
|
int soapy_send(float *buff, int num);
|
||||||
|
int soapy_receive(float *buff, int max);
|
||||||
|
int soapy_get_inbuffer(void);
|
||||||
|
|
|
@ -166,14 +166,14 @@ int uhd_open(const char *device_args, double tx_frequency, double rx_frequency,
|
||||||
tune_request.dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO;
|
tune_request.dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO;
|
||||||
error = uhd_usrp_set_tx_freq(usrp, &tune_request, channel, &tune_result);
|
error = uhd_usrp_set_tx_freq(usrp, &tune_request, channel, &tune_result);
|
||||||
if (error) {
|
if (error) {
|
||||||
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX rate to %.0f Hz\n", tx_frequency);
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX frequeny to %.0f Hz\n", tx_frequency);
|
||||||
uhd_close();
|
uhd_close();
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
tune_request.target_freq = rx_frequency;
|
tune_request.target_freq = rx_frequency;
|
||||||
error = uhd_usrp_set_rx_freq(usrp, &tune_request, channel, &tune_result);
|
error = uhd_usrp_set_rx_freq(usrp, &tune_request, channel, &tune_result);
|
||||||
if (error) {
|
if (error) {
|
||||||
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX rate to %.0f Hz\n", rx_frequency);
|
PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX frequeny to %.0f Hz\n", rx_frequency);
|
||||||
uhd_close();
|
uhd_close();
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
@ -327,7 +327,7 @@ int uhd_receive(float *buff, int max)
|
||||||
while (1) {
|
while (1) {
|
||||||
if (max < (int)rx_samps_per_buff) {
|
if (max < (int)rx_samps_per_buff) {
|
||||||
PDEBUG(DUHD, DEBUG_ERROR, "SDR rx buffer overflow!\n");
|
PDEBUG(DUHD, DEBUG_ERROR, "SDR rx buffer overflow!\n");
|
||||||
return 0;
|
break;
|
||||||
}
|
}
|
||||||
/* read RX stream */
|
/* read RX stream */
|
||||||
buffs_ptr[0] = buff;
|
buffs_ptr[0] = buff;
|
||||||
|
@ -362,7 +362,7 @@ int uhd_receive(float *buff, int max)
|
||||||
}
|
}
|
||||||
check_rate = 0;
|
check_rate = 0;
|
||||||
rx_gap = diff_time * (double)samplerate + 0.5;
|
rx_gap = diff_time * (double)samplerate + 0.5;
|
||||||
PDEBUG(DUHD, DEBUG_ERROR, "Lost rx frame(s): A gap of %.6f secods (%d samples), \n", diff_time, rx_gap);
|
PDEBUG(DUHD, DEBUG_ERROR, "Lost rx frame(s): A gap of %.6f seconds (%d samples), \n", diff_time, rx_gap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -19,5 +19,6 @@ nmt_LDADD = \
|
||||||
$(top_builddir)/src/common/libcommon.a \
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
$(ALSA_LIBS) \
|
$(ALSA_LIBS) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS) \
|
||||||
-lm
|
-lm
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ test_filter_LDADD = \
|
||||||
$(top_builddir)/src/common/libcommon.a \
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
$(ALSA_LIBS) \
|
$(ALSA_LIBS) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS) \
|
||||||
-lm
|
-lm
|
||||||
|
|
||||||
test_compandor_SOURCES = test_compandor.c
|
test_compandor_SOURCES = test_compandor.c
|
||||||
|
@ -32,6 +33,7 @@ test_emphasis_LDADD = \
|
||||||
$(top_builddir)/src/common/libcommon.a \
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
$(ALSA_LIBS) \
|
$(ALSA_LIBS) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS) \
|
||||||
-lm
|
-lm
|
||||||
|
|
||||||
test_dms_SOURCES = \
|
test_dms_SOURCES = \
|
||||||
|
@ -44,6 +46,7 @@ test_dms_LDADD = \
|
||||||
$(top_builddir)/src/common/libcommon.a \
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
$(ALSA_LIBS) \
|
$(ALSA_LIBS) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS) \
|
||||||
-lm
|
-lm
|
||||||
|
|
||||||
test_sms_SOURCES = \
|
test_sms_SOURCES = \
|
||||||
|
@ -56,6 +59,7 @@ test_sms_LDADD = \
|
||||||
$(top_builddir)/src/common/libcommon.a \
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
$(ALSA_LIBS) \
|
$(ALSA_LIBS) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS) \
|
||||||
-lm
|
-lm
|
||||||
|
|
||||||
test_performance_SOURCES = dummy.c test_performance.c
|
test_performance_SOURCES = dummy.c test_performance.c
|
||||||
|
@ -65,5 +69,6 @@ test_performance_LDADD = \
|
||||||
$(top_builddir)/src/common/libcommon.a \
|
$(top_builddir)/src/common/libcommon.a \
|
||||||
$(ALSA_LIBS) \
|
$(ALSA_LIBS) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
|
$(SOAPY_LIBS) \
|
||||||
-lm
|
-lm
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue