Added SDR abstraction layer
This commit is contained in:
parent
88145d74ba
commit
85663d5304
|
@ -33,6 +33,8 @@ set(sources
|
||||||
src/layer1/modcod.h
|
src/layer1/modcod.h
|
||||||
src/layer2/tundev.c
|
src/layer2/tundev.c
|
||||||
src/layer2/tundev.h
|
src/layer2/tundev.h
|
||||||
|
src/sdr/sdr.c
|
||||||
|
src/sdr/sdr.h
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
|
|
@ -3,24 +3,6 @@
|
||||||
|
|
||||||
#include <liquid/liquid.h>
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
/*** SDR CONFIG ***/
|
|
||||||
|
|
||||||
#define SDR_IS_FULL_DUPLEX 0
|
|
||||||
|
|
||||||
#define SDR_RX_SAMPLING_RATE 1e6f
|
|
||||||
#define SDR_TX_SAMPLING_RATE RX_SAMPLING_RATE
|
|
||||||
|
|
||||||
// actually transmitted or received signal frequencies, NOT the SDR center frequency.
|
|
||||||
#define SDR_TX_FREQ 434.100e6f
|
|
||||||
#define SDR_RX_FREQ 434.100e6f
|
|
||||||
|
|
||||||
// shift applied in the baseband, to get rid of SDR DC peak. If the value here
|
|
||||||
// is not 0, software mixing will be done on the received signal.
|
|
||||||
#define SDR_TX_IF_SHIFT 0.000e6f
|
|
||||||
#define SDR_RX_IF_SHIFT 0.150e6f
|
|
||||||
|
|
||||||
// NOTE: the SDR center frequency will be SDR_RX_FREQ - SDR_RX_IF_SHIFT.
|
|
||||||
|
|
||||||
/*** LAYER 1 CONFIG ***/
|
/*** LAYER 1 CONFIG ***/
|
||||||
|
|
||||||
#define SYMBOL_RATE 100e3f
|
#define SYMBOL_RATE 100e3f
|
||||||
|
@ -44,4 +26,24 @@
|
||||||
#define TRANSMISSION_RAMP_UP_LEN 64 // symbols
|
#define TRANSMISSION_RAMP_UP_LEN 64 // symbols
|
||||||
#define TRANSMISSION_RAMP_DOWN_LEN 32 // symbols
|
#define TRANSMISSION_RAMP_DOWN_LEN 32 // symbols
|
||||||
|
|
||||||
|
/*** SDR CONFIG ***/
|
||||||
|
|
||||||
|
#define SDR_IS_FULL_DUPLEX 0
|
||||||
|
|
||||||
|
#define SDR_OVERSAMPLING 3 // integer factor!
|
||||||
|
|
||||||
|
#define SDR_RX_SAMPLING_RATE (SYMBOL_RATE * RRC_SPS * SDR_OVERSAMPLING)
|
||||||
|
#define SDR_TX_SAMPLING_RATE SDR_RX_SAMPLING_RATE
|
||||||
|
|
||||||
|
// actually transmitted or received signal frequencies, NOT the SDR center frequency.
|
||||||
|
#define SDR_TX_FREQ 434.100e6f
|
||||||
|
#define SDR_RX_FREQ 434.100e6f
|
||||||
|
|
||||||
|
// shift applied in the baseband, to get rid of SDR DC peak. If the value here
|
||||||
|
// is not 0, software mixing will be done on the received signal.
|
||||||
|
#define SDR_TX_IF_SHIFT 0.000e6f
|
||||||
|
#define SDR_RX_IF_SHIFT 0.150e6f
|
||||||
|
|
||||||
|
// NOTE: the SDR center frequency will be SDR_RX_FREQ - SDR_RX_IF_SHIFT.
|
||||||
|
|
||||||
#endif // CONFIG_H
|
#endif // CONFIG_H
|
||||||
|
|
|
@ -6,8 +6,9 @@ typedef enum {
|
||||||
ERR_INVALID_STATE,
|
ERR_INVALID_STATE,
|
||||||
ERR_NO_MEM,
|
ERR_NO_MEM,
|
||||||
ERR_SIZE,
|
ERR_SIZE,
|
||||||
ERR_LIQUID,
|
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.
|
||||||
} result_t;
|
} result_t;
|
||||||
|
|
||||||
#ifdef DEBUG_LIQUID
|
#ifdef DEBUG_LIQUID
|
||||||
|
|
249
impl/src/sdr/sdr.c
Normal file
249
impl/src/sdr/sdr.c
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
#include <SoapySDR/Device.h>
|
||||||
|
#include <SoapySDR/Formats.h>
|
||||||
|
#include <liquid/liquid.h>
|
||||||
|
#include <stdio.h> //printf
|
||||||
|
#include <stdlib.h> //free
|
||||||
|
#include <complex.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "sdr.h"
|
||||||
|
|
||||||
|
static void shutdown_streams(sdr_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_init(sdr_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
ctx->sdr = NULL;
|
||||||
|
ctx->tx_stream = NULL;
|
||||||
|
ctx->rx_stream = NULL;
|
||||||
|
|
||||||
|
ctx->interp = firinterp_crcf_create_prototype(LIQUID_FIRFILT_PM, SDR_OVERSAMPLING, 10, 0.2, 0);
|
||||||
|
ctx->decim = firdecim_crcf_create_prototype(LIQUID_FIRFILT_PM, SDR_OVERSAMPLING, 10, 0.2, 0);
|
||||||
|
|
||||||
|
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_TX_SAMPLING_RATE);
|
||||||
|
|
||||||
|
//enumerate devices
|
||||||
|
SoapySDRKwargs *results = SoapySDRDevice_enumerate(NULL, &length);
|
||||||
|
for (size_t i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "sdr: Found device #%d: ", (int)i);
|
||||||
|
for (size_t j = 0; j < results[i].size; j++)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "sdr: %s=%s, ", 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;
|
||||||
|
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, "sdr: %s, ", names[i]);
|
||||||
|
fprintf(stderr, "sdr: \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, "sdr: %s, ", names[i]);
|
||||||
|
fprintf(stderr, "sdr: \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, "sdr: [%g Hz -> %g Hz], ", ranges[i].minimum, ranges[i].maximum);
|
||||||
|
fprintf(stderr, "sdr: \n");
|
||||||
|
free(ranges);
|
||||||
|
|
||||||
|
//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_destroy(sdr_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
shutdown_streams(ctx);
|
||||||
|
|
||||||
|
nco_crcf_destroy(ctx->rx_nco);
|
||||||
|
nco_crcf_destroy(ctx->tx_nco);
|
||||||
|
|
||||||
|
firdecim_crcf_destroy(ctx->decim);
|
||||||
|
firinterp_crcf_destroy(ctx->interp);
|
||||||
|
|
||||||
|
if(ctx->sdr) {
|
||||||
|
SoapySDRDevice_unmake(ctx->sdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t sdr_start_rx(sdr_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
shutdown_streams(ctx);
|
||||||
|
|
||||||
|
//setup a stream (complex floats)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
SoapySDRDevice_activateStream(ctx->sdr, ctx->rx_stream, 0, 0, 0); //start streaming
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t sdr_start_tx(sdr_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
shutdown_streams(ctx);
|
||||||
|
|
||||||
|
//setup a stream (complex floats)
|
||||||
|
ctx->tx_stream = SoapySDRDevice_setupStream(ctx->sdr, SOAPY_SDR_RX, SOAPY_SDR_CF32, NULL, 0, NULL);
|
||||||
|
|
||||||
|
if(ctx->tx_stream == NULL) {
|
||||||
|
fprintf(stderr, "sdr: setupStream fail: %s\n", SoapySDRDevice_lastError());
|
||||||
|
return ERR_SOAPY;
|
||||||
|
}
|
||||||
|
|
||||||
|
SoapySDRDevice_activateStream(ctx->sdr, ctx->tx_stream, 0, 0, 0); //start streaming
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 *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_receive(sdr_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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((nbb * SDR_OVERSAMPLING) > nrf) {
|
||||||
|
return ERR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < nbb; i++) {
|
||||||
|
float complex tmp[SDR_OVERSAMPLING];
|
||||||
|
|
||||||
|
nco_crcf_mix_block_down(ctx->rx_nco,
|
||||||
|
(complex float*)(rf_samples + i * SDR_OVERSAMPLING),
|
||||||
|
tmp,
|
||||||
|
SDR_OVERSAMPLING);
|
||||||
|
|
||||||
|
firdecim_crcf_execute(ctx->decim, tmp, bb_samples + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t sdr_baseband_to_rf(sdr_ctx_t *ctx,
|
||||||
|
const float complex *bb_samples, size_t nbb,
|
||||||
|
float complex *rf_samples, size_t nrf)
|
||||||
|
{
|
||||||
|
if((nbb * SDR_OVERSAMPLING) > nrf) {
|
||||||
|
return ERR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < nbb; i++) {
|
||||||
|
float complex tmp[SDR_OVERSAMPLING];
|
||||||
|
|
||||||
|
firinterp_crcf_execute(ctx->interp, bb_samples[i], tmp);
|
||||||
|
|
||||||
|
nco_crcf_mix_block_up(ctx->tx_nco,
|
||||||
|
tmp,
|
||||||
|
rf_samples + i * SDR_OVERSAMPLING,
|
||||||
|
SDR_OVERSAMPLING);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
48
impl/src/sdr/sdr.h
Normal file
48
impl/src/sdr/sdr.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef SDR_SDR_H
|
||||||
|
#define SDR_SDR_H
|
||||||
|
|
||||||
|
#include <complex.h>
|
||||||
|
|
||||||
|
#include <SoapySDR/Device.h>
|
||||||
|
|
||||||
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
|
#include "results.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SoapySDRDevice *sdr;
|
||||||
|
|
||||||
|
SoapySDRStream *rx_stream;
|
||||||
|
SoapySDRStream *tx_stream;
|
||||||
|
|
||||||
|
firinterp_crcf interp;
|
||||||
|
nco_crcf tx_nco;
|
||||||
|
|
||||||
|
firdecim_crcf decim;
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 Convert and resample a received signal to baseband.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Convert and resample a baseband signal for transmission.
|
||||||
|
*/
|
||||||
|
result_t sdr_baseband_to_rf(sdr_ctx_t *ctx,
|
||||||
|
const float complex *bb_samples, size_t nbb,
|
||||||
|
float complex *rf_samples, size_t nrf);
|
||||||
|
|
||||||
|
#endif // SDR_SDR_H
|
Loading…
Reference in a new issue