Use libhackrf directly (not tested yet)
This commit is contained in:
parent
7995f5bef0
commit
2b741acd8a
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required (VERSION 3.2)
|
cmake_minimum_required (VERSION 3.20)
|
||||||
project (hamnet70 VERSION 0.1 LANGUAGES C)
|
project (hamnet70 VERSION 0.1 LANGUAGES C)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
@ -53,7 +53,7 @@ target_link_libraries(
|
||||||
rt
|
rt
|
||||||
fftw3f
|
fftw3f
|
||||||
fec
|
fec
|
||||||
SoapySDR
|
hackrf
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
|
|
|
@ -2,12 +2,8 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#include <SoapySDR/Logger.h>
|
|
||||||
|
|
||||||
#include <liquid/liquid.h>
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
@ -195,8 +191,6 @@ int main(void)
|
||||||
|
|
||||||
sdr_ctx_t sdr;
|
sdr_ctx_t sdr;
|
||||||
|
|
||||||
SoapySDR_setLogLevel(SOAPY_SDR_DEBUG);
|
|
||||||
|
|
||||||
bool on_air = true;
|
bool on_air = true;
|
||||||
|
|
||||||
debug_fd = open("/tmp/dump.cf32", O_CREAT | O_WRONLY | O_TRUNC);
|
debug_fd = open("/tmp/dump.cf32", O_CREAT | O_WRONLY | O_TRUNC);
|
||||||
|
|
|
@ -9,6 +9,7 @@ typedef enum {
|
||||||
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.
|
||||||
|
ERR_SDR, // an error occurred in the SDR interface.
|
||||||
} result_t;
|
} result_t;
|
||||||
|
|
||||||
#ifdef DEBUG_LIQUID
|
#ifdef DEBUG_LIQUID
|
||||||
|
|
|
@ -1,43 +1,152 @@
|
||||||
#include <SoapySDR/Device.h>
|
#include <libhackrf/hackrf.h>
|
||||||
#include <SoapySDR/Formats.h>
|
|
||||||
#include <SoapySDR/Logger.h>
|
|
||||||
#include <liquid/liquid.h>
|
#include <liquid/liquid.h>
|
||||||
|
#include <semaphore.h>
|
||||||
#include <stdio.h> //printf
|
#include <stdio.h> //printf
|
||||||
#include <stdlib.h> //free
|
#include <stdlib.h> //free
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "sdr.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void close_streams(sdr_ctx_t *ctx)
|
for(size_t i = 0; i < nsamples; i++) {
|
||||||
{
|
liquid_float_complex sample =
|
||||||
if(ctx->rx_stream) {
|
0.0078125f * (transfer->buffer[2*i + 0] + I * transfer->buffer[2*i + 1]);
|
||||||
SoapySDRDevice_deactivateStream(ctx->sdr, ctx->rx_stream, 0, 0);
|
|
||||||
SoapySDRDevice_closeStream(ctx->sdr, ctx->rx_stream);
|
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(ctx->tx_stream) {
|
if(sem_post(&sdr_ctx->buf_sem) < 0) {
|
||||||
SoapySDRDevice_deactivateStream(ctx->sdr, ctx->tx_stream, 0, 0);
|
perror("sem_post");
|
||||||
SoapySDRDevice_closeStream(ctx->sdr, ctx->tx_stream);
|
return HACKRF_ERROR_OTHER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int8_t clamp_float2int8(float s)
|
||||||
|
{
|
||||||
|
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(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)
|
result_t sdr_init(sdr_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
size_t length;
|
int result;
|
||||||
|
|
||||||
ctx->sdr = NULL;
|
result = sem_init(&ctx->buf_sem, 0, 1);
|
||||||
ctx->tx_stream = NULL;
|
if(result < 0) {
|
||||||
ctx->rx_stream = NULL;
|
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->interp = firinterp_crcf_create_kaiser(SDR_OVERSAMPLING, 9, 60.0f);
|
||||||
ctx->decim = firdecim_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->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);
|
nco_crcf_set_frequency(ctx->rx_nco, 2 * M_PI * SDR_RX_IF_SHIFT / SDR_RX_SAMPLING_RATE);
|
||||||
|
|
||||||
// set up logging
|
ctx->rx_buf = cbuffercf_create(SDR_BUFFER_SIZE_SAMPLES);
|
||||||
SoapySDR_registerLogHandler(soapy_log_handler);
|
ctx->tx_buf = cbuffercf_create(SDR_BUFFER_SIZE_SAMPLES);
|
||||||
|
|
||||||
//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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -137,7 +166,8 @@ result_t sdr_init(sdr_ctx_t *ctx)
|
||||||
|
|
||||||
result_t sdr_destroy(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->rx_nco);
|
||||||
nco_crcf_destroy(ctx->tx_nco);
|
nco_crcf_destroy(ctx->tx_nco);
|
||||||
|
@ -145,9 +175,10 @@ result_t sdr_destroy(sdr_ctx_t *ctx)
|
||||||
firdecim_crcf_destroy(ctx->decim);
|
firdecim_crcf_destroy(ctx->decim);
|
||||||
firinterp_crcf_destroy(ctx->interp);
|
firinterp_crcf_destroy(ctx->interp);
|
||||||
|
|
||||||
if(ctx->sdr) {
|
cbuffercf_destroy(ctx->rx_buf);
|
||||||
SoapySDRDevice_unmake(ctx->sdr);
|
cbuffercf_destroy(ctx->tx_buf);
|
||||||
}
|
|
||||||
|
sem_close(&ctx->buf_sem);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -155,26 +186,22 @@ result_t sdr_destroy(sdr_ctx_t *ctx)
|
||||||
|
|
||||||
result_t sdr_start_rx(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) {
|
int result;
|
||||||
fprintf(stderr, "sdr: activateStream fail: %s\n", SoapySDRDevice_lastError());
|
|
||||||
return ERR_SOAPY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set gains
|
result = hackrf_set_freq(ctx->hackrf, SDR_RX_FREQ - SDR_RX_IF_SHIFT);
|
||||||
if(SoapySDRDevice_setGainElement(ctx->sdr, SOAPY_SDR_RX, 0, "AMP", SDR_GAIN_RX_AMP) != 0) {
|
CHECK_HACKRF_RESULT(result, "hackrf_set_freq");
|
||||||
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) {
|
result = hackrf_set_lna_gain(ctx->hackrf, SDR_GAIN_RX_LNA);
|
||||||
fprintf(stderr, "sdr: setGainElement fail: %s\n", SoapySDRDevice_lastError());
|
CHECK_HACKRF_RESULT(result, "hackrf_set_lna_gain");
|
||||||
return ERR_SOAPY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(SoapySDRDevice_setGainElement(ctx->sdr, SOAPY_SDR_RX, 0, "VGA", SDR_GAIN_RX_VGA) != 0) {
|
result = hackrf_set_vga_gain(ctx->hackrf, SDR_GAIN_RX_VGA);
|
||||||
fprintf(stderr, "sdr: setGainElement fail: %s\n", SoapySDRDevice_lastError());
|
CHECK_HACKRF_RESULT(result, "hackrf_set_vga_gain");
|
||||||
return ERR_SOAPY;
|
|
||||||
}
|
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;
|
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)
|
result_t sdr_start_tx(sdr_ctx_t *ctx, size_t burst_size)
|
||||||
{
|
{
|
||||||
// set gain
|
(void)burst_size;
|
||||||
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) {
|
int result;
|
||||||
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) {
|
result = hackrf_set_freq(ctx->hackrf, SDR_TX_FREQ);
|
||||||
fprintf(stderr, "sdr: activateStream fail: %s\n", SoapySDRDevice_lastError());
|
CHECK_HACKRF_RESULT(result, "hackrf_set_freq");
|
||||||
return ERR_SOAPY;
|
|
||||||
}
|
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;
|
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)
|
result_t sdr_stop_rx(sdr_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
if(SoapySDRDevice_deactivateStream(ctx->sdr, ctx->rx_stream, 0, 0) != 0) {
|
return stop_streaming(ctx);
|
||||||
fprintf(stderr, "sdr: activateStream fail: %s\n", SoapySDRDevice_lastError());
|
|
||||||
return ERR_SOAPY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
result_t sdr_stop_tx(sdr_ctx_t *ctx)
|
result_t sdr_stop_tx(sdr_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
if(SoapySDRDevice_deactivateStream(ctx->sdr, ctx->tx_stream, 0, 0) != 0) {
|
return stop_streaming(ctx);
|
||||||
fprintf(stderr, "sdr: activateStream fail: %s\n", SoapySDRDevice_lastError());
|
|
||||||
return ERR_SOAPY;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
if(ctx->tx_stream == NULL) {
|
(void)timeout_us; // not implemented yet
|
||||||
return ERR_INVALID_STATE;
|
|
||||||
|
if(sem_wait(&ctx->buf_sem) < 0) {
|
||||||
|
perror("sem_wait failed. Samples lost. Error:");
|
||||||
|
return ERR_SDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *buffs[] = {(void*)samples};
|
int result = cbuffercf_write(ctx->tx_buf, (float complex*)samples, nsamples);
|
||||||
|
if(result != LIQUID_OK) {
|
||||||
int ret = SoapySDRDevice_writeStream(ctx->sdr, ctx->tx_stream, (const void* const*)buffs, nsamples, 0, 0, timeout_us);
|
fprintf(stderr, "cbuffercf_push failed: %d. Samples are lost.\n", result);
|
||||||
|
sem_post(&ctx->buf_sem);
|
||||||
if(ret <= 0) {
|
return ERR_LIQUID;
|
||||||
fprintf(stderr, "sdr: writeStream fail: %s\n", SoapySDRDevice_lastError());
|
|
||||||
return ERR_SOAPY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(sem_post(&ctx->buf_sem) < 0) {
|
||||||
|
perror("sem_post");
|
||||||
|
return ERR_SDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
if(ctx->rx_stream == NULL) {
|
(void)timeout_us; // not implemented yet
|
||||||
return ERR_INVALID_STATE;
|
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if(sem_wait(&ctx->buf_sem) < 0) {
|
||||||
|
perror("sem_wait");
|
||||||
|
return ERR_SDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *buffs[] = {(void*)samples};
|
unsigned int samples_read;
|
||||||
|
float complex *buf_samples;
|
||||||
int ret = SoapySDRDevice_readStream(ctx->sdr, ctx->rx_stream, (void* const*)buffs, *nsamples, 0, 0, timeout_us);
|
result = cbuffercf_read(ctx->tx_buf, *nsamples, &buf_samples, &samples_read);
|
||||||
|
if(result != LIQUID_OK) {
|
||||||
if(ret <= 0) {
|
fprintf(stderr, "cbuffercf_read failed: %d. Samples are lost.\n", result);
|
||||||
fprintf(stderr, "sdr: readStream fail: %s\n", SoapySDRDevice_lastError());
|
sem_post(&ctx->buf_sem);
|
||||||
return ERR_SOAPY;
|
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;
|
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)
|
result_t sdr_flush_tx_buffer(sdr_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
size_t mtu = SoapySDRDevice_getStreamMTU(ctx->sdr, ctx->tx_stream);
|
// currently not implemented
|
||||||
|
(void)ctx;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,34 @@
|
||||||
#define SDR_SDR_H
|
#define SDR_SDR_H
|
||||||
|
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
#include <SoapySDR/Device.h>
|
#include <libhackrf/hackrf.h>
|
||||||
|
|
||||||
#include <liquid/liquid.h>
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
#include "results.h"
|
#include "results.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef enum {
|
||||||
SoapySDRDevice *sdr;
|
SDR_STATUS_IDLE,
|
||||||
|
SDR_STATUS_TX,
|
||||||
|
SDR_STATUS_RX
|
||||||
|
} sdr_status_t;
|
||||||
|
|
||||||
SoapySDRStream *rx_stream;
|
typedef struct {
|
||||||
SoapySDRStream *tx_stream;
|
hackrf_device *hackrf;
|
||||||
|
|
||||||
firinterp_crcf interp;
|
firinterp_crcf interp;
|
||||||
nco_crcf tx_nco;
|
nco_crcf tx_nco;
|
||||||
|
cbuffercf tx_buf;
|
||||||
|
|
||||||
firdecim_crcf decim;
|
firdecim_crcf decim;
|
||||||
nco_crcf rx_nco;
|
nco_crcf rx_nco;
|
||||||
|
cbuffercf rx_buf;
|
||||||
|
|
||||||
|
sem_t buf_sem;
|
||||||
|
|
||||||
|
sdr_status_t status;
|
||||||
} sdr_ctx_t;
|
} sdr_ctx_t;
|
||||||
|
|
||||||
result_t sdr_init(sdr_ctx_t *ctx);
|
result_t sdr_init(sdr_ctx_t *ctx);
|
||||||
|
|
Loading…
Reference in a new issue