From 2b741acd8a3f60e43696c117db0b41d4cc567ec6 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Tue, 2 Jan 2024 22:49:16 +0100 Subject: [PATCH] Use libhackrf directly (not tested yet) --- impl/CMakeLists.txt | 4 +- impl/src/main.c | 6 - impl/src/results.h | 1 + impl/src/sdr/sdr.c | 379 +++++++++++++++++++++++--------------------- impl/src/sdr/sdr.h | 20 ++- 5 files changed, 219 insertions(+), 191 deletions(-) diff --git a/impl/CMakeLists.txt b/impl/CMakeLists.txt index 7faddce..fa46a75 100644 --- a/impl/CMakeLists.txt +++ b/impl/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.2) +cmake_minimum_required (VERSION 3.20) project (hamnet70 VERSION 0.1 LANGUAGES C) set(CMAKE_C_STANDARD 99) @@ -53,7 +53,7 @@ target_link_libraries( rt fftw3f fec - SoapySDR + hackrf ) add_subdirectory(test) diff --git a/impl/src/main.c b/impl/src/main.c index e509665..a6b5fda 100644 --- a/impl/src/main.c +++ b/impl/src/main.c @@ -2,12 +2,8 @@ #include #include #include -#include -#include #include -#include - #include #include @@ -195,8 +191,6 @@ int main(void) sdr_ctx_t sdr; - SoapySDR_setLogLevel(SOAPY_SDR_DEBUG); - bool on_air = true; debug_fd = open("/tmp/dump.cf32", O_CREAT | O_WRONLY | O_TRUNC); diff --git a/impl/src/results.h b/impl/src/results.h index fb1f821..55a5359 100644 --- a/impl/src/results.h +++ b/impl/src/results.h @@ -9,6 +9,7 @@ typedef enum { ERR_LIQUID, // an error occurred in the LiquidDSP library. ERR_SYSCALL, // a syscall failed. Use errno to determine the cause. ERR_SOAPY, // an error occurred in the SoapySDR library. + ERR_SDR, // an error occurred in the SDR interface. } result_t; #ifdef DEBUG_LIQUID diff --git a/impl/src/sdr/sdr.c b/impl/src/sdr/sdr.c index c23ec9e..c17ed81 100644 --- a/impl/src/sdr/sdr.c +++ b/impl/src/sdr/sdr.c @@ -1,43 +1,152 @@ -#include -#include -#include +#include #include +#include #include //printf #include //free #include #include #include +#include #include "config.h" #include "sdr.h" -void soapy_log_handler(const SoapySDRLogLevel logLevel, const char *message) +#define SDR_BUFFER_SIZE_SAMPLES 2048000 + +#define CHECK_HACKRF_RESULT(result, call) do { \ + if(result != HACKRF_SUCCESS) { \ + fprintf(stderr, call "() failed: %s (%d)", hackrf_error_name(result), result); \ + return ERR_SDR; \ + } \ +} while(0); + + +static int rx_callback(hackrf_transfer *transfer) { - fprintf(stderr, "soapy [%d]: %s\n", logLevel, message); + sdr_ctx_t *sdr_ctx = (sdr_ctx_t*)transfer->rx_ctx; + + size_t nsamples = transfer->valid_length / 2; + + if(sem_wait(&sdr_ctx->buf_sem) < 0) { + perror("sem_wait failed. Samples lost. Error:"); + return HACKRF_ERROR_OTHER; + } + + for(size_t i = 0; i < nsamples; i++) { + liquid_float_complex sample = + 0.0078125f * (transfer->buffer[2*i + 0] + I * transfer->buffer[2*i + 1]); + + int result = cbuffercf_push(sdr_ctx->rx_buf, sample); + if(result != LIQUID_OK) { + fprintf(stderr, "cbuffercf_push failed: %d. Samples are lost.\n", result); + break; + } + } + + if(sem_post(&sdr_ctx->buf_sem) < 0) { + perror("sem_post"); + return HACKRF_ERROR_OTHER; + } + + return 0; } -static void close_streams(sdr_ctx_t *ctx) +static inline int8_t clamp_float2int8(float s) { - if(ctx->rx_stream) { - SoapySDRDevice_deactivateStream(ctx->sdr, ctx->rx_stream, 0, 0); - SoapySDRDevice_closeStream(ctx->sdr, ctx->rx_stream); + if(s >= 127.0f) { + return 127; + } else if(s <= -128.0f) { + return -128; + } else { + return (int8_t)(s + 0.5f); + } +} + +static int tx_callback(hackrf_transfer *transfer) +{ + int result; + + sdr_ctx_t *sdr_ctx = (sdr_ctx_t*)transfer->tx_ctx; + + if(transfer->valid_length % 2 != 0) { + fprintf(stderr, "WARNING! Buffer unaligned!\n"); } - if(ctx->tx_stream) { - SoapySDRDevice_deactivateStream(ctx->sdr, ctx->tx_stream, 0, 0); - SoapySDRDevice_closeStream(ctx->sdr, ctx->tx_stream); + if(sem_wait(&sdr_ctx->buf_sem) < 0) { + perror("sem_wait"); + return HACKRF_ERROR_OTHER; } + + unsigned int samples_requested = transfer->valid_length / 2; + unsigned int samples_read; + liquid_float_complex *samples; + + result = cbuffercf_read(sdr_ctx->tx_buf, samples_requested, &samples, &samples_read); + if(result != LIQUID_OK) { + fprintf(stderr, "cbuffercf_read failed: %d. Samples are lost.\n", result); + return HACKRF_ERROR_OTHER; + } + + for(size_t i = 0; i < samples_read; i++) { + transfer->buffer[2*i + 0] = clamp_float2int8(creal(samples[i])); + transfer->buffer[2*i + 1] = clamp_float2int8(cimag(samples[i])); + } + + if(sem_post(&sdr_ctx->buf_sem) < 0) { + perror("sem_post"); + return HACKRF_ERROR_OTHER; + } + + return 0; +} + +static result_t stop_streaming(sdr_ctx_t *ctx) +{ + int result; + + switch(ctx->status) { + case SDR_STATUS_RX: + result = hackrf_stop_rx(ctx->hackrf); + CHECK_HACKRF_RESULT(result, "hackrf_stop_rx"); + break; + + case SDR_STATUS_TX: + result = hackrf_stop_tx(ctx->hackrf); + CHECK_HACKRF_RESULT(result, "hackrf_stop_tx"); + break; + + default: + // no action needed + return OK; + } + + struct timespec ts_1ms = {0, 1000000}; + while(hackrf_is_streaming(ctx->hackrf) == HACKRF_TRUE) { + puts("wr"); + nanosleep(&ts_1ms, NULL); + } + + return OK; } result_t sdr_init(sdr_ctx_t *ctx) { - size_t length; + int result; - ctx->sdr = NULL; - ctx->tx_stream = NULL; - ctx->rx_stream = NULL; + result = sem_init(&ctx->buf_sem, 0, 1); + if(result < 0) { + perror("sem_init failed"); + return ERR_SDR; + } + + hackrf_init(); + + result = hackrf_open(&ctx->hackrf); + CHECK_HACKRF_RESULT(result, "hackrf_open"); + + ctx->status = SDR_STATUS_IDLE; ctx->interp = firinterp_crcf_create_kaiser(SDR_OVERSAMPLING, 9, 60.0f); ctx->decim = firdecim_crcf_create_kaiser(SDR_OVERSAMPLING, 9, 60.0f); @@ -48,88 +157,8 @@ result_t sdr_init(sdr_ctx_t *ctx) 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", "hackrf"); - 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); - - //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; - } + ctx->rx_buf = cbuffercf_create(SDR_BUFFER_SIZE_SAMPLES); + ctx->tx_buf = cbuffercf_create(SDR_BUFFER_SIZE_SAMPLES); return OK; } @@ -137,7 +166,8 @@ result_t sdr_init(sdr_ctx_t *ctx) result_t sdr_destroy(sdr_ctx_t *ctx) { - close_streams(ctx); + stop_streaming(ctx); + hackrf_close(ctx->hackrf); nco_crcf_destroy(ctx->rx_nco); nco_crcf_destroy(ctx->tx_nco); @@ -145,9 +175,10 @@ result_t sdr_destroy(sdr_ctx_t *ctx) firdecim_crcf_destroy(ctx->decim); firinterp_crcf_destroy(ctx->interp); - if(ctx->sdr) { - SoapySDRDevice_unmake(ctx->sdr); - } + cbuffercf_destroy(ctx->rx_buf); + cbuffercf_destroy(ctx->tx_buf); + + sem_close(&ctx->buf_sem); return OK; } @@ -155,26 +186,22 @@ result_t sdr_destroy(sdr_ctx_t *ctx) result_t sdr_start_rx(sdr_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; - } + int result; - // 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; - } + result = hackrf_set_freq(ctx->hackrf, SDR_RX_FREQ - SDR_RX_IF_SHIFT); + CHECK_HACKRF_RESULT(result, "hackrf_set_freq"); - 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; - } + result = hackrf_set_lna_gain(ctx->hackrf, SDR_GAIN_RX_LNA); + CHECK_HACKRF_RESULT(result, "hackrf_set_lna_gain"); - 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; - } + result = hackrf_set_vga_gain(ctx->hackrf, SDR_GAIN_RX_VGA); + CHECK_HACKRF_RESULT(result, "hackrf_set_vga_gain"); + + result = hackrf_set_amp_enable(ctx->hackrf, SDR_GAIN_RX_AMP > 0); + CHECK_HACKRF_RESULT(result, "hackrf_set_amp_enable"); + + result = hackrf_start_rx(ctx->hackrf, rx_callback, ctx); + CHECK_HACKRF_RESULT(result, "hackrf_start_rx"); return OK; } @@ -182,21 +209,21 @@ result_t sdr_start_rx(sdr_ctx_t *ctx) result_t sdr_start_tx(sdr_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; - } + (void)burst_size; - 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; - } + int result; - 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; - } + result = hackrf_set_freq(ctx->hackrf, SDR_TX_FREQ); + CHECK_HACKRF_RESULT(result, "hackrf_set_freq"); + + result = hackrf_set_amp_enable(ctx->hackrf, SDR_GAIN_TX_AMP > 0); + CHECK_HACKRF_RESULT(result, "hackrf_set_amp_enable"); + + result = hackrf_set_txvga_gain(ctx->hackrf, SDR_GAIN_TX_AMP); + CHECK_HACKRF_RESULT(result, "hackrf_set_txvga_gain"); + + result = hackrf_start_tx(ctx->hackrf, tx_callback, ctx); + CHECK_HACKRF_RESULT(result, "hackrf_start_tx"); return OK; } @@ -204,61 +231,70 @@ result_t sdr_start_tx(sdr_ctx_t *ctx, size_t burst_size) result_t sdr_stop_rx(sdr_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; + return stop_streaming(ctx); } result_t sdr_stop_tx(sdr_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; + return stop_streaming(ctx); } result_t sdr_transmit(sdr_ctx_t *ctx, const float complex *samples, size_t nsamples, long timeout_us) { - if(ctx->tx_stream == NULL) { - return ERR_INVALID_STATE; + (void)timeout_us; // not implemented yet + + if(sem_wait(&ctx->buf_sem) < 0) { + perror("sem_wait failed. Samples lost. Error:"); + return ERR_SDR; } - 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; + int result = cbuffercf_write(ctx->tx_buf, (float complex*)samples, nsamples); + if(result != LIQUID_OK) { + fprintf(stderr, "cbuffercf_push failed: %d. Samples are lost.\n", result); + sem_post(&ctx->buf_sem); + return ERR_LIQUID; } + if(sem_post(&ctx->buf_sem) < 0) { + perror("sem_post"); + return ERR_SDR; + } + + return OK; } result_t sdr_receive(sdr_ctx_t *ctx, float complex *samples, size_t *nsamples, long timeout_us) { - if(ctx->rx_stream == NULL) { - return ERR_INVALID_STATE; + (void)timeout_us; // not implemented yet + + int result; + + if(sem_wait(&ctx->buf_sem) < 0) { + perror("sem_wait"); + return ERR_SDR; } - 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; + unsigned int samples_read; + float complex *buf_samples; + result = cbuffercf_read(ctx->tx_buf, *nsamples, &buf_samples, &samples_read); + if(result != LIQUID_OK) { + fprintf(stderr, "cbuffercf_read failed: %d. Samples are lost.\n", result); + sem_post(&ctx->buf_sem); + return ERR_LIQUID; } - *nsamples = ret; + memcpy(samples, buf_samples, samples_read * sizeof(samples[0])); + + if(sem_post(&ctx->buf_sem) < 0) { + perror("sem_post"); + return ERR_SDR; + } + + *nsamples = samples_read; return OK; } @@ -266,21 +302,8 @@ result_t sdr_receive(sdr_ctx_t *ctx, float complex *samples, size_t *nsamples, l result_t sdr_flush_tx_buffer(sdr_ctx_t *ctx) { - size_t mtu = SoapySDRDevice_getStreamMTU(ctx->sdr, ctx->tx_stream); - - float complex zeros[mtu]; - memset(zeros, 0, sizeof(zeros)); - - for(int i = 0; i < 4; i++) { - fprintf(stderr, "z"); - - unsigned tries = 0; - int ret; - - do { - ret = sdr_transmit(ctx, zeros, sizeof(zeros)/sizeof(zeros[0]), 100000); - } while(ret <= 0 && tries++ < 3); - } + // currently not implemented + (void)ctx; return OK; } diff --git a/impl/src/sdr/sdr.h b/impl/src/sdr/sdr.h index 4dabc98..6cb37de 100644 --- a/impl/src/sdr/sdr.h +++ b/impl/src/sdr/sdr.h @@ -2,24 +2,34 @@ #define SDR_SDR_H #include +#include -#include +#include #include #include "results.h" -typedef struct { - SoapySDRDevice *sdr; +typedef enum { + SDR_STATUS_IDLE, + SDR_STATUS_TX, + SDR_STATUS_RX +} sdr_status_t; - SoapySDRStream *rx_stream; - SoapySDRStream *tx_stream; +typedef struct { + hackrf_device *hackrf; firinterp_crcf interp; nco_crcf tx_nco; + cbuffercf tx_buf; firdecim_crcf decim; nco_crcf rx_nco; + cbuffercf rx_buf; + + sem_t buf_sem; + + sdr_status_t status; } sdr_ctx_t; result_t sdr_init(sdr_ctx_t *ctx);