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.
This commit is contained in:
Thomas Kolb 2024-04-27 20:08:04 +02:00
parent a3928d0ad0
commit c6ea578808
4 changed files with 34 additions and 5 deletions

View file

@ -330,7 +330,7 @@ int main(int argc, char **argv)
size_t n_rf_samples = CHUNKSIZE_RF; size_t n_rf_samples = CHUNKSIZE_RF;
size_t n_bb_samples = CHUNKSIZE_BB; 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++; rx_retries++;
fprintf(stderr, "sdr_receive() failed %d times.\n", rx_retries); fprintf(stderr, "sdr_receive() failed %d times.\n", rx_retries);
if(rx_retries >= 3) { if(rx_retries >= 3) {

View file

@ -4,8 +4,9 @@
typedef enum { typedef enum {
OK, OK,
ERR_INVALID_STATE, ERR_INVALID_STATE,
ERR_NO_MEM, ERR_INVALID_PARAM, // invalid / nonsense parameters given
ERR_SIZE, 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_LIQUID, // an error occurred in the LiquidDSP library.
ERR_SYSCALL, // a syscall failed. Use errno to determine the cause. ERR_SYSCALL, // a syscall failed. Use errno to determine the cause.
ERR_SOAPY, // an error occurred in the SoapySDR library. ERR_SOAPY, // an error occurred in the SoapySDR library.

View file

@ -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 (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; 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; unsigned int samples_read;
float complex *buf_samples; float complex *buf_samples;
result = cbuffercf_read(ctx->rx_buf, *nsamples, &buf_samples, &samples_read); 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, const float complex *rf_samples, size_t nrf,
float complex *bb_samples, size_t *nbb) 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) { 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); fprintf(stderr, "sdr_rf_to_baseband: result would not fit in output: %zd * %d < %zd\n", *nbb, SDR_OVERSAMPLING, nrf);
return ERR_SIZE; return ERR_SIZE;

View file

@ -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_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_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); result_t sdr_flush_tx_buffer(sdr_ctx_t *ctx);
size_t sdr_get_tx_buffer_free_space(sdr_ctx_t *ctx); size_t sdr_get_tx_buffer_free_space(sdr_ctx_t *ctx);