From eb03e6a6612e57fa60f0b6d86f5e96619044fa4b Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Tue, 1 Oct 2024 20:51:39 +0200 Subject: [PATCH] Add basic sxceiver sdr module --- impl/src/sdr/sdr_sxceiver.c | 276 ++++++++++++++++++++++++++++++++++++ impl/src/sdr/sdr_sxceiver.h | 34 +++++ 2 files changed, 310 insertions(+) create mode 100644 impl/src/sdr/sdr_sxceiver.c create mode 100644 impl/src/sdr/sdr_sxceiver.h diff --git a/impl/src/sdr/sdr_sxceiver.c b/impl/src/sdr/sdr_sxceiver.c new file mode 100644 index 0000000..7d7e617 --- /dev/null +++ b/impl/src/sdr/sdr_sxceiver.c @@ -0,0 +1,276 @@ +#include +#include +#include +#include +#include //printf +#include //free +#include +#include +#include + +#include "config.h" + +#include "sdr.h" + +void soapy_log_handler(const SoapySDRLogLevel logLevel, const char *message) +{ + fprintf(stderr, "soapy [%d]: %s\n", logLevel, message); +} + +static void close_streams(sdr_sxceiver_ctx_t *ctx) +{ + if(ctx->rx_stream) { + SoapySDRDevice_deactivateStream(ctx->sdr, ctx->rx_stream, 0, 0); + SoapySDRDevice_closeStream(ctx->sdr, ctx->rx_stream); + } + + if(ctx->tx_stream) { + SoapySDRDevice_deactivateStream(ctx->sdr, ctx->tx_stream, 0, 0); + SoapySDRDevice_closeStream(ctx->sdr, ctx->tx_stream); + } +} + + +result_t sdr_sxceiver_init(sdr_sxceiver_ctx_t *ctx) +{ + size_t length; + + ctx->sdr = NULL; + ctx->tx_stream = NULL; + ctx->rx_stream = NULL; + + ctx->tx_nco = nco_crcf_create(LIQUID_NCO); + ctx->rx_nco = nco_crcf_create(LIQUID_NCO); + + nco_crcf_set_frequency(ctx->tx_nco, 2 * M_PI * SDR_TX_IF_SHIFT / SDR_TX_SAMPLING_RATE); + nco_crcf_set_frequency(ctx->rx_nco, 2 * M_PI * SDR_RX_IF_SHIFT / SDR_RX_SAMPLING_RATE); + + // set up logging + SoapySDR_registerLogHandler(soapy_log_handler); + + //enumerate devices + SoapySDRKwargs *results = SoapySDRDevice_enumerate(NULL, &length); + for (size_t i = 0; i < length; i++) + { + fprintf(stderr, "sdr: Found device #%d:\n", (int)i); + for (size_t j = 0; j < results[i].size; j++) + { + fprintf(stderr, "sdr: - %s=%s\n", results[i].keys[j], results[i].vals[j]); + } + fprintf(stderr, "sdr: \n"); + } + SoapySDRKwargsList_clear(results, length); + + //create device instance + //args can be user defined or from the enumeration result + SoapySDRKwargs args; + memset(&args, 0, sizeof(args)); + SoapySDRKwargs_set(&args, "driver", "sx"); + ctx->sdr = SoapySDRDevice_make(&args); + SoapySDRKwargs_clear(&args); + + if (ctx->sdr == NULL) + { + fprintf(stderr, "sdr: SoapySDRDevice_make fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + //query device info + char** names = SoapySDRDevice_listAntennas(ctx->sdr, SOAPY_SDR_RX, 0, &length); + fprintf(stderr, "sdr: Rx antennas: "); + for (size_t i = 0; i < length; i++) fprintf(stderr, "%s, ", names[i]); + fprintf(stderr, "\n"); + SoapySDRStrings_clear(&names, length); + + names = SoapySDRDevice_listGains(ctx->sdr, SOAPY_SDR_RX, 0, &length); + fprintf(stderr, "sdr: Rx gains: "); + for (size_t i = 0; i < length; i++) fprintf(stderr, "%s, ", names[i]); + fprintf(stderr, "\n"); + SoapySDRStrings_clear(&names, length); + + SoapySDRRange *ranges = SoapySDRDevice_getFrequencyRange(ctx->sdr, SOAPY_SDR_RX, 0, &length); + fprintf(stderr, "sdr: Rx freq ranges: "); + for (size_t i = 0; i < length; i++) fprintf(stderr, "[%g Hz -> %g Hz], ", ranges[i].minimum, ranges[i].maximum); + fprintf(stderr, "\n"); + free(ranges); + + char** names = SoapySDRDevice_listAntennas(ctx->sdr, SOAPY_SDR_TX, 0, &length); + fprintf(stderr, "sdr: Tx antennas: "); + for (size_t i = 0; i < length; i++) fprintf(stderr, "%s, ", names[i]); + fprintf(stderr, "\n"); + SoapySDRStrings_clear(&names, length); + + names = SoapySDRDevice_listGains(ctx->sdr, SOAPY_SDR_TX, 0, &length); + fprintf(stderr, "sdr: Tx gains: "); + for (size_t i = 0; i < length; i++) fprintf(stderr, "%s, ", names[i]); + fprintf(stderr, "\n"); + SoapySDRStrings_clear(&names, length); + + SoapySDRRange *ranges = SoapySDRDevice_getFrequencyRange(ctx->sdr, SOAPY_SDR_TX, 0, &length); + fprintf(stderr, "sdr: Tx freq ranges: "); + for (size_t i = 0; i < length; i++) fprintf(stderr, "[%g Hz -> %g Hz], ", ranges[i].minimum, ranges[i].maximum); + fprintf(stderr, "\n"); + free(ranges); + + //setup streams + ctx->rx_stream = SoapySDRDevice_setupStream(ctx->sdr, SOAPY_SDR_RX, SOAPY_SDR_CF32, NULL, 0, NULL); + + if(ctx->rx_stream == NULL) { + fprintf(stderr, "sdr: setupStream fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + ctx->tx_stream = SoapySDRDevice_setupStream(ctx->sdr, SOAPY_SDR_TX, SOAPY_SDR_CF32, NULL, 0, NULL); + + if(ctx->tx_stream == NULL) { + fprintf(stderr, "sdr: setupStream fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + //apply settings + if (SoapySDRDevice_setSampleRate(ctx->sdr, SOAPY_SDR_RX, 0, SDR_RX_SAMPLING_RATE) != 0) { + fprintf(stderr, "sdr: setSampleRate fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + if (SoapySDRDevice_setFrequency(ctx->sdr, SOAPY_SDR_RX, 0, SDR_RX_FREQ - SDR_RX_IF_SHIFT, NULL) != 0) { + fprintf(stderr, "sdr: setFrequency fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + if (SoapySDRDevice_setSampleRate(ctx->sdr, SOAPY_SDR_TX, 0, SDR_TX_SAMPLING_RATE) != 0) { + fprintf(stderr, "sdr: setSampleRate fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + if (SoapySDRDevice_setFrequency(ctx->sdr, SOAPY_SDR_TX, 0, SDR_TX_FREQ, NULL) != 0) { + fprintf(stderr, "sdr: setFrequency fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + return OK; +} + + +result_t sdr_sxceiver_destroy(sdr_sxceiver_ctx_t *ctx) +{ + close_streams(ctx); + + nco_crcf_destroy(ctx->rx_nco); + nco_crcf_destroy(ctx->tx_nco); + + if(ctx->sdr) { + SoapySDRDevice_unmake(ctx->sdr); + } + + return OK; +} + + +result_t sdr_sxceiver_start_rx(sdr_sxceiver_ctx_t *ctx) +{ + if(SoapySDRDevice_activateStream(ctx->sdr, ctx->rx_stream, 0, 0, 0) != 0) { + fprintf(stderr, "sdr: activateStream fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + // set gains + if(SoapySDRDevice_setGainElement(ctx->sdr, SOAPY_SDR_RX, 0, "AMP", SDR_GAIN_RX_AMP) != 0) { + fprintf(stderr, "sdr: setGainElement fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + if(SoapySDRDevice_setGainElement(ctx->sdr, SOAPY_SDR_RX, 0, "LNA", SDR_GAIN_RX_LNA) != 0) { + fprintf(stderr, "sdr: setGainElement fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + if(SoapySDRDevice_setGainElement(ctx->sdr, SOAPY_SDR_RX, 0, "VGA", SDR_GAIN_RX_VGA) != 0) { + fprintf(stderr, "sdr: setGainElement fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + return OK; +} + + +result_t sdr_sxceiver_start_tx(sdr_sxceiver_ctx_t *ctx, size_t burst_size) +{ + // set gain + if(SoapySDRDevice_setGainElement(ctx->sdr, SOAPY_SDR_TX, 0, "AMP", SDR_GAIN_TX_AMP) != 0) { + fprintf(stderr, "sdr: setGainElement fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + if(SoapySDRDevice_setGainElement(ctx->sdr, SOAPY_SDR_TX, 0, "LNA", SDR_GAIN_TX_LNA) != 0) { + fprintf(stderr, "sdr: setGainElement fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + if(SoapySDRDevice_activateStream(ctx->sdr, ctx->tx_stream, 0, 0, burst_size) != 0) { + fprintf(stderr, "sdr: activateStream fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + return OK; +} + + +result_t sdr_sxceiver_stop_rx(sdr_sxceiver_ctx_t *ctx) +{ + if(SoapySDRDevice_deactivateStream(ctx->sdr, ctx->rx_stream, 0, 0) != 0) { + fprintf(stderr, "sdr: activateStream fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + return OK; +} + + +result_t sdr_sxceiver_stop_tx(sdr_sxceiver_ctx_t *ctx) +{ + if(SoapySDRDevice_deactivateStream(ctx->sdr, ctx->tx_stream, 0, 0) != 0) { + fprintf(stderr, "sdr: activateStream fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + return OK; +} + + +result_t sdr_sxceiver_transmit(sdr_sxceiver_ctx_t *ctx, const float complex *samples, size_t nsamples, long timeout_us) +{ + if(ctx->tx_stream == NULL) { + return ERR_INVALID_STATE; + } + + void *buffs[] = {(void*)samples}; + + int ret = SoapySDRDevice_writeStream(ctx->sdr, ctx->tx_stream, (const void* const*)buffs, nsamples, 0, 0, timeout_us); + + if(ret <= 0) { + fprintf(stderr, "sdr: writeStream fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + return OK; +} + + +result_t sdr_sxceiver_receive(sdr_sxceiver_ctx_t *ctx, float complex *samples, size_t *nsamples, long timeout_us) +{ + if(ctx->rx_stream == NULL) { + return ERR_INVALID_STATE; + } + + void *buffs[] = {(void*)samples}; + + int ret = SoapySDRDevice_readStream(ctx->sdr, ctx->rx_stream, (void* const*)buffs, *nsamples, 0, 0, timeout_us); + + if(ret <= 0) { + fprintf(stderr, "sdr: readStream fail: %s\n", SoapySDRDevice_lastError()); + return ERR_SOAPY; + } + + *nsamples = ret; + + return OK; +} diff --git a/impl/src/sdr/sdr_sxceiver.h b/impl/src/sdr/sdr_sxceiver.h new file mode 100644 index 0000000..91ac994 --- /dev/null +++ b/impl/src/sdr/sdr_sxceiver.h @@ -0,0 +1,34 @@ +#ifndef SDR_SDR_H +#define SDR_SDR_H + +#include + +#include + +#include + +#include "results.h" + +typedef struct { + SoapySDRDevice *sdr; + + SoapySDRStream *rx_stream; + SoapySDRStream *tx_stream; + + nco_crcf tx_nco; + nco_crcf rx_nco; +} sdr_ctx_t; + +result_t sdr_init(sdr_ctx_t *ctx); +result_t sdr_destroy(sdr_ctx_t *ctx); + +result_t sdr_start_rx(sdr_ctx_t *ctx); +result_t sdr_start_tx(sdr_ctx_t *ctx, size_t burst_size); + +result_t sdr_stop_rx(sdr_ctx_t *ctx); +result_t sdr_stop_tx(sdr_ctx_t *ctx); + +result_t sdr_transmit(sdr_ctx_t *ctx, const float complex *samples, size_t nsamples, long timeout_us); +result_t sdr_receive(sdr_ctx_t *ctx, float complex *samples, size_t *nsamples, long timeout_us); + +#endif // SDR_SDR_H