From c6ea57880865850154173a19d3813c3c0aeec646 Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Sat, 27 Apr 2024 20:08:04 +0200 Subject: [PATCH] sdr: fix loss of samples due to unaligned buffer reads sdr_rf_to_baseband() processes samples in blocks of size SDR_OVERSAMPLING. If the total number of samples does not align with this block size, the leftover samples are lost and phase and timing glitches result. To mitigate this, sdr_receive() now has an additional parameter that specifies the alignment of the returned data. The number of samples returned is always a multiple of this alignment factor. This feature is used to ensure that the number of returned samples is a multiple of SDR_OVERSAMPLING and therefore no samples are lost in sdr_rf_to_baseband(). sdr_rf_to_baseband() now has an additional check that makes the function fail if the alignment is incorrect. --- impl/src/main.c | 2 +- impl/src/results.h | 5 +++-- impl/src/sdr/sdr.c | 17 ++++++++++++++++- impl/src/sdr/sdr.h | 15 ++++++++++++++- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/impl/src/main.c b/impl/src/main.c index ea8708a..d2d27ad 100644 --- a/impl/src/main.c +++ b/impl/src/main.c @@ -330,7 +330,7 @@ int main(int argc, char **argv) size_t n_rf_samples = CHUNKSIZE_RF; size_t n_bb_samples = CHUNKSIZE_BB; - if(sdr_receive(&sdr, rf_samples, &n_rf_samples, 100000) != OK) { + if(sdr_receive(&sdr, rf_samples, &n_rf_samples, 100000, SDR_OVERSAMPLING) != OK) { rx_retries++; fprintf(stderr, "sdr_receive() failed %d times.\n", rx_retries); if(rx_retries >= 3) { diff --git a/impl/src/results.h b/impl/src/results.h index 55a5359..34373ca 100644 --- a/impl/src/results.h +++ b/impl/src/results.h @@ -4,8 +4,9 @@ typedef enum { OK, ERR_INVALID_STATE, - ERR_NO_MEM, - ERR_SIZE, + ERR_INVALID_PARAM, // invalid / nonsense parameters given + ERR_NO_MEM, // not enough memory or allocation error + ERR_SIZE, // a given size is invalid 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. diff --git a/impl/src/sdr/sdr.c b/impl/src/sdr/sdr.c index 949a511..011f6e4 100644 --- a/impl/src/sdr/sdr.c +++ b/impl/src/sdr/sdr.c @@ -313,7 +313,7 @@ result_t sdr_transmit(sdr_ctx_t *ctx, const float complex *samples, size_t nsamp } -result_t sdr_receive(sdr_ctx_t *ctx, 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, size_t alignment) { (void)timeout_us; // not implemented yet @@ -324,6 +324,16 @@ result_t sdr_receive(sdr_ctx_t *ctx, float complex *samples, size_t *nsamples, l return ERR_SDR; } + // ensure that an aligned number of bytes is read from the buffer in any case. + if(alignment > 1) { + unsigned int samples_avail = cbuffercf_size(ctx->rx_buf); + if(samples_avail < *nsamples) { + *nsamples = samples_avail - (samples_avail % alignment); + } else { + *nsamples -= *nsamples % alignment; + } + } + unsigned int samples_read; float complex *buf_samples; result = cbuffercf_read(ctx->rx_buf, *nsamples, &buf_samples, &samples_read); @@ -425,6 +435,11 @@ result_t sdr_rf_to_baseband(sdr_ctx_t *ctx, const float complex *rf_samples, size_t nrf, float complex *bb_samples, size_t *nbb) { + if((nrf % SDR_OVERSAMPLING) != 0) { + fprintf(stderr, "sdr_rf_to_baseband: the given number of RF samples (%zd) is not a multiple of SDR_OVERSAMPLING (%d).\n", nrf, SDR_OVERSAMPLING); + return ERR_SIZE; + } + if((*nbb * SDR_OVERSAMPLING) < nrf) { fprintf(stderr, "sdr_rf_to_baseband: result would not fit in output: %zd * %d < %zd\n", *nbb, SDR_OVERSAMPLING, nrf); return ERR_SIZE; diff --git a/impl/src/sdr/sdr.h b/impl/src/sdr/sdr.h index 4c42744..0469eac 100644 --- a/impl/src/sdr/sdr.h +++ b/impl/src/sdr/sdr.h @@ -45,7 +45,20 @@ 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); + +/*! + * \brief Read samples from the SDR device. + * + * \param ctx The SDR device context to use. + * \param samples Pointer to the output array. + * \param nsamples Pointer to the number of samples requested. Will be + * adjusted to the number of samples actually read. + * \param timeout_us Read timeout in microseconds (currently unused). + * \param alignment If greater than 1, the number of returned samples will be + * adjusted such that it is divisable by this number. + * \returns The result code of the read operation. + */ +result_t sdr_receive(sdr_ctx_t *ctx, float complex *samples, size_t *nsamples, long timeout_us, size_t alignment); result_t sdr_flush_tx_buffer(sdr_ctx_t *ctx); size_t sdr_get_tx_buffer_free_space(sdr_ctx_t *ctx);