2017-01-27 17:44:42 +00:00
|
|
|
// Part of dump1090, a Mode S message decoder for RTLSDR devices.
|
|
|
|
|
//
|
2017-01-30 20:15:26 +00:00
|
|
|
// sdr_bladerf.c: bladeRF support
|
2017-01-27 17:44:42 +00:00
|
|
|
//
|
2017-01-30 20:15:26 +00:00
|
|
|
// Copyright (c) 2017 FlightAware LLC
|
|
|
|
|
//
|
2017-06-15 17:16:51 +00:00
|
|
|
// This file is free software: you may copy, redistribute and/or modify it
|
2017-01-30 20:15:26 +00:00
|
|
|
// under the terms of the GNU General Public License as published by the
|
2017-06-15 17:16:51 +00:00
|
|
|
// Free Software Foundation, either version 2 of the License, or (at your
|
|
|
|
|
// option) any later version.
|
2017-01-30 20:15:26 +00:00
|
|
|
//
|
2017-06-15 17:16:51 +00:00
|
|
|
// This file is distributed in the hope that it will be useful, but
|
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
2017-01-30 20:15:26 +00:00
|
|
|
// General Public License for more details.
|
|
|
|
|
//
|
2017-06-15 17:16:51 +00:00
|
|
|
// You should have received a copy of the GNU General Public License
|
2017-01-30 20:15:26 +00:00
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2017-01-27 17:44:42 +00:00
|
|
|
|
|
|
|
|
#include "dump1090.h"
|
|
|
|
|
#include "sdr_bladerf.h"
|
|
|
|
|
|
|
|
|
|
#include <libbladeRF.h>
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
|
2019-09-04 14:46:38 +00:00
|
|
|
#if !defined(LIBBLADERF_API_VERSION) || (LIBBLADERF_API_VERSION < 0x01060100)
|
|
|
|
|
#error This libbladeRF is too old, upgrade or disable bladerf support
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-04-28 07:16:58 +00:00
|
|
|
// Polyfill for the older bladerf API
|
2019-09-04 14:46:38 +00:00
|
|
|
#if LIBBLADERF_API_VERSION < 0x02000000
|
2019-04-28 07:16:58 +00:00
|
|
|
typedef unsigned int bladerf_frequency;
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-01-27 17:44:42 +00:00
|
|
|
static struct {
|
|
|
|
|
const char *device_str;
|
|
|
|
|
const char *fpga_path;
|
|
|
|
|
unsigned decimation;
|
|
|
|
|
bladerf_lpf_mode lpf_mode;
|
|
|
|
|
unsigned lpf_bandwidth;
|
|
|
|
|
|
|
|
|
|
struct bladerf *device;
|
|
|
|
|
|
|
|
|
|
iq_convert_fn converter;
|
|
|
|
|
struct converter_state *converter_state;
|
|
|
|
|
|
|
|
|
|
unsigned block_size;
|
|
|
|
|
} BladeRF;
|
|
|
|
|
|
|
|
|
|
void bladeRFInitConfig()
|
|
|
|
|
{
|
|
|
|
|
BladeRF.device_str = NULL;
|
|
|
|
|
BladeRF.fpga_path = NULL;
|
|
|
|
|
BladeRF.decimation = 1;
|
|
|
|
|
BladeRF.lpf_mode = BLADERF_LPF_NORMAL;
|
|
|
|
|
BladeRF.lpf_bandwidth = 1750000;
|
|
|
|
|
BladeRF.device = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bladeRFHandleOption(int argc, char **argv, int *jptr)
|
|
|
|
|
{
|
|
|
|
|
int j = *jptr;
|
|
|
|
|
bool more = (j+1 < argc);
|
|
|
|
|
if (!strcmp(argv[j], "--bladerf-fpga") && more) {
|
|
|
|
|
BladeRF.fpga_path = strdup(argv[++j]);
|
|
|
|
|
} else if (!strcmp(argv[j], "--bladerf-decimation") && more) {
|
|
|
|
|
BladeRF.decimation = atoi(argv[++j]);
|
|
|
|
|
} else if (!strcmp(argv[j], "--bladerf-bandwidth") && more) {
|
|
|
|
|
++j;
|
|
|
|
|
if (!strcasecmp(argv[j], "bypass")) {
|
|
|
|
|
BladeRF.lpf_mode = BLADERF_LPF_BYPASSED;
|
|
|
|
|
} else {
|
|
|
|
|
BladeRF.lpf_mode = BLADERF_LPF_NORMAL;
|
|
|
|
|
BladeRF.lpf_bandwidth = atoi(argv[j]);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*jptr = j;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void bladeRFShowHelp()
|
|
|
|
|
{
|
|
|
|
|
printf(" bladeRF-specific options (use with --device-type bladerf)\n");
|
|
|
|
|
printf("\n");
|
|
|
|
|
printf("--device <ident> select device by bladeRF 'device identifier'\n");
|
|
|
|
|
printf("--bladerf-fpga <path> use alternative FPGA bitstream ('' to disable FPGA load)\n");
|
|
|
|
|
printf("--bladerf-decimation <N> assume FPGA decimates by a factor of N\n");
|
|
|
|
|
printf("--bladerf-bandwidth <hz> set LPF bandwidth ('bypass' to bypass the LPF)\n");
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int lna_gain_db(bladerf_lna_gain gain)
|
|
|
|
|
{
|
|
|
|
|
switch (gain) {
|
|
|
|
|
case BLADERF_LNA_GAIN_BYPASS:
|
|
|
|
|
return 0;
|
|
|
|
|
case BLADERF_LNA_GAIN_MID:
|
|
|
|
|
return BLADERF_LNA_GAIN_MID_DB;
|
|
|
|
|
case BLADERF_LNA_GAIN_MAX:
|
|
|
|
|
return BLADERF_LNA_GAIN_MAX_DB;
|
|
|
|
|
default:
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void show_config()
|
|
|
|
|
{
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
unsigned rate;
|
2019-04-28 07:16:58 +00:00
|
|
|
bladerf_frequency freq;
|
2017-01-27 17:44:42 +00:00
|
|
|
bladerf_lpf_mode lpf_mode;
|
|
|
|
|
unsigned lpf_bw;
|
|
|
|
|
bladerf_lna_gain lna_gain;
|
|
|
|
|
int rxvga1_gain;
|
|
|
|
|
int rxvga2_gain;
|
|
|
|
|
int16_t lms_dc_i, lms_dc_q;
|
|
|
|
|
int16_t fpga_phase, fpga_gain;
|
|
|
|
|
struct bladerf_lms_dc_cals dc_cals;
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_get_sample_rate(BladeRF.device, BLADERF_MODULE_RX, &rate)) < 0 ||
|
|
|
|
|
(status = bladerf_get_frequency(BladeRF.device, BLADERF_MODULE_RX, &freq)) < 0 ||
|
|
|
|
|
(status = bladerf_get_lpf_mode(BladeRF.device, BLADERF_MODULE_RX, &lpf_mode)) < 0 ||
|
|
|
|
|
(status = bladerf_get_bandwidth(BladeRF.device, BLADERF_MODULE_RX, &lpf_bw)) < 0 ||
|
|
|
|
|
(status = bladerf_get_lna_gain(BladeRF.device, &lna_gain)) < 0 ||
|
|
|
|
|
(status = bladerf_get_rxvga1(BladeRF.device, &rxvga1_gain)) < 0 ||
|
|
|
|
|
(status = bladerf_get_rxvga2(BladeRF.device, &rxvga2_gain)) < 0 ||
|
|
|
|
|
(status = bladerf_get_correction(BladeRF.device, BLADERF_MODULE_RX, BLADERF_CORR_LMS_DCOFF_I, &lms_dc_i)) < 0 ||
|
|
|
|
|
(status = bladerf_get_correction(BladeRF.device, BLADERF_MODULE_RX, BLADERF_CORR_LMS_DCOFF_Q, &lms_dc_q)) < 0 ||
|
|
|
|
|
(status = bladerf_get_correction(BladeRF.device, BLADERF_MODULE_RX, BLADERF_CORR_FPGA_PHASE, &fpga_phase)) < 0 ||
|
|
|
|
|
(status = bladerf_get_correction(BladeRF.device, BLADERF_MODULE_RX, BLADERF_CORR_FPGA_GAIN, &fpga_gain)) < 0 ||
|
|
|
|
|
(status = bladerf_lms_get_dc_cals(BladeRF.device, &dc_cals)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladeRF: couldn't read back device configuration\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "bladeRF: sampling rate: %.1f MHz\n", rate/1e6);
|
|
|
|
|
fprintf(stderr, "bladeRF: frequency: %.1f MHz\n", freq/1e6);
|
|
|
|
|
fprintf(stderr, "bladeRF: LNA gain: %ddB\n", lna_gain_db(lna_gain));
|
|
|
|
|
fprintf(stderr, "bladeRF: RXVGA1 gain: %ddB\n", rxvga1_gain);
|
|
|
|
|
fprintf(stderr, "bladeRF: RXVGA2 gain: %ddB\n", rxvga2_gain);
|
|
|
|
|
|
|
|
|
|
switch (lpf_mode) {
|
|
|
|
|
case BLADERF_LPF_NORMAL:
|
|
|
|
|
fprintf(stderr, "bladeRF: LPF bandwidth: %.2f MHz\n", lpf_bw/1e6);
|
|
|
|
|
break;
|
|
|
|
|
case BLADERF_LPF_BYPASSED:
|
|
|
|
|
fprintf(stderr, "bladeRF: LPF bypassed\n");
|
|
|
|
|
break;
|
|
|
|
|
case BLADERF_LPF_DISABLED:
|
|
|
|
|
fprintf(stderr, "bladeRF: LPF disabled\n");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "bladeRF: LPF in unknown state\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "bladeRF: calibration settings:\n");
|
|
|
|
|
fprintf(stderr, " LMS DC adjust: I=%d Q=%d\n", lms_dc_i, lms_dc_q);
|
|
|
|
|
fprintf(stderr, " FPGA phase adjust: %+.3f degrees\n", fpga_phase * 10.0 / 4096);
|
|
|
|
|
fprintf(stderr, " FPGA gain adjust: %+.3f\n", fpga_gain * 1.0 / 4096);
|
|
|
|
|
fprintf(stderr, " LMS LPF tuning: %d\n", dc_cals.lpf_tuning);
|
|
|
|
|
fprintf(stderr, " LMS RX LPF filter: I=%d Q=%d\n", dc_cals.rx_lpf_i, dc_cals.rx_lpf_q);
|
|
|
|
|
fprintf(stderr, " LMS RXVGA2 DC ref: %d\n", dc_cals.dc_ref);
|
|
|
|
|
fprintf(stderr, " LMS RXVGA2A: I=%d Q=%d\n", dc_cals.rxvga2a_i, dc_cals.rxvga2a_q);
|
|
|
|
|
fprintf(stderr, " LMS RXVGA2B: I=%d Q=%d\n", dc_cals.rxvga2b_i, dc_cals.rxvga2b_q);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bladeRFOpen()
|
|
|
|
|
{
|
|
|
|
|
if (BladeRF.device) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
bladerf_set_usb_reset_on_open(true);
|
|
|
|
|
if ((status = bladerf_open(&BladeRF.device, Modes.dev_name)) < 0) {
|
|
|
|
|
fprintf(stderr, "Failed to open bladeRF: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *fpga_path;
|
|
|
|
|
if (BladeRF.fpga_path) {
|
|
|
|
|
fpga_path = BladeRF.fpga_path;
|
|
|
|
|
} else {
|
|
|
|
|
bladerf_fpga_size size;
|
|
|
|
|
if ((status = bladerf_get_fpga_size(BladeRF.device, &size)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_get_fpga_size failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (size) {
|
|
|
|
|
case BLADERF_FPGA_40KLE:
|
|
|
|
|
fpga_path = "/usr/share/Nuand/bladeRF/hostedx40.rbf";
|
|
|
|
|
break;
|
|
|
|
|
case BLADERF_FPGA_115KLE:
|
|
|
|
|
fpga_path = "/usr/share/Nuand/bladeRF/hostedx115.rbf";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "bladeRF: unknown FPGA size, skipping FPGA load");
|
|
|
|
|
fpga_path = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fpga_path && fpga_path[0]) {
|
|
|
|
|
fprintf(stderr, "bladeRF: loading FPGA bitstream from %s\n", fpga_path);
|
|
|
|
|
if ((status = bladerf_load_fpga(BladeRF.device, fpga_path)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_load_fpga() failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (bladerf_device_speed(BladeRF.device)) {
|
|
|
|
|
case BLADERF_DEVICE_SPEED_HIGH:
|
|
|
|
|
BladeRF.block_size = 1024;
|
|
|
|
|
break;
|
|
|
|
|
case BLADERF_DEVICE_SPEED_SUPER:
|
|
|
|
|
BladeRF.block_size = 2048;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "couldn't determine bladerf device speed\n");
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_set_sample_rate(BladeRF.device, BLADERF_MODULE_RX, Modes.sample_rate * BladeRF.decimation, NULL)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_set_sample_rate failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_set_frequency(BladeRF.device, BLADERF_MODULE_RX, Modes.freq)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_set_frequency failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_set_lpf_mode(BladeRF.device, BLADERF_MODULE_RX, BladeRF.lpf_mode)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_set_lpf_mode failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_set_bandwidth(BladeRF.device, BLADERF_MODULE_RX, BladeRF.lpf_bandwidth, NULL)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_set_lpf_bandwidth failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* turn the tx gain right off, just in case */
|
|
|
|
|
if ((status = bladerf_set_gain(BladeRF.device, BLADERF_MODULE_TX, -100)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_set_gain(TX) failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-29 12:11:13 +00:00
|
|
|
if ((status = bladerf_set_gain(BladeRF.device, BLADERF_MODULE_RX, Modes.gain)) < 0) {
|
2017-01-27 17:44:42 +00:00
|
|
|
fprintf(stderr, "bladerf_set_gain(RX) failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_set_loopback(BladeRF.device, BLADERF_LB_NONE)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_set_loopback() failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_calibrate_dc(BladeRF.device, BLADERF_DC_CAL_LPF_TUNING)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_calibrate_dc(LPF_TUNING) failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_calibrate_dc(BladeRF.device, BLADERF_DC_CAL_RX_LPF)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_calibrate_dc(RX_LPF) failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_calibrate_dc(BladeRF.device, BLADERF_DC_CAL_RXVGA2)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_calibrate_dc(RXVGA2) failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
show_config();
|
|
|
|
|
|
|
|
|
|
BladeRF.converter = init_converter(INPUT_SC16Q11,
|
|
|
|
|
Modes.sample_rate,
|
|
|
|
|
Modes.dc_filter,
|
|
|
|
|
&BladeRF.converter_state);
|
|
|
|
|
if (!BladeRF.converter) {
|
|
|
|
|
fprintf(stderr, "can't initialize sample converter\n");
|
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
if (BladeRF.device) {
|
|
|
|
|
bladerf_close(BladeRF.device);
|
|
|
|
|
BladeRF.device = NULL;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned timeouts = 0;
|
|
|
|
|
|
|
|
|
|
static void *handle_bladerf_samples(struct bladerf *dev,
|
|
|
|
|
struct bladerf_stream *stream,
|
|
|
|
|
struct bladerf_metadata *meta,
|
|
|
|
|
void *samples,
|
|
|
|
|
size_t num_samples,
|
|
|
|
|
void *user_data)
|
|
|
|
|
{
|
2020-08-05 11:32:44 +00:00
|
|
|
static uint64_t nextTimestamp = 0; // what's the next timestamp we expect to see?
|
|
|
|
|
static bool overrun = false; // do we have a pending overrun to report?
|
|
|
|
|
static bool dropped = 0; // do we have some dropped samples to report?
|
|
|
|
|
static bool first_buffer = true; // is this the very first callback?
|
2017-01-27 17:44:42 +00:00
|
|
|
|
|
|
|
|
MODES_NOTUSED(dev);
|
|
|
|
|
MODES_NOTUSED(stream);
|
|
|
|
|
MODES_NOTUSED(meta);
|
|
|
|
|
MODES_NOTUSED(user_data);
|
|
|
|
|
|
|
|
|
|
// record initial time for later sys timestamp calculation
|
2017-12-02 17:38:33 +00:00
|
|
|
uint64_t entryTimestamp = mstime();
|
2017-01-27 17:44:42 +00:00
|
|
|
|
2020-08-05 04:00:50 +00:00
|
|
|
sdrMonitor();
|
|
|
|
|
|
2017-01-27 17:44:42 +00:00
|
|
|
if (Modes.exit) {
|
2020-08-05 11:32:44 +00:00
|
|
|
// ask our caller to return
|
2017-01-27 17:44:42 +00:00
|
|
|
return BLADERF_STREAM_SHUTDOWN;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-05 11:32:44 +00:00
|
|
|
timeouts = 0;
|
2017-01-27 17:44:42 +00:00
|
|
|
|
|
|
|
|
// start handling metadata blocks
|
|
|
|
|
unsigned samples_per_block = (BladeRF.block_size - 16) / 4;
|
2020-08-05 11:32:44 +00:00
|
|
|
struct mag_buf *outbuf = NULL;
|
2017-01-27 17:44:42 +00:00
|
|
|
|
2020-08-05 11:32:44 +00:00
|
|
|
for (unsigned offset = 0; offset < num_samples * 4; offset += BladeRF.block_size) {
|
2017-01-27 17:44:42 +00:00
|
|
|
// read the next metadata header
|
|
|
|
|
uint8_t *header = ((uint8_t*)samples) + offset;
|
|
|
|
|
uint64_t metadata_magic = le32toh(*(uint32_t*)(header));
|
|
|
|
|
uint64_t metadata_timestamp = le64toh(*(uint64_t*)(header + 4));
|
|
|
|
|
uint32_t metadata_flags = le32toh(*(uint32_t*)(header + 12));
|
|
|
|
|
void *sample_data = header + 16;
|
|
|
|
|
|
|
|
|
|
if (metadata_magic != 0x12344321) {
|
|
|
|
|
// first buffer is often in the wrong mode
|
|
|
|
|
if (!first_buffer) {
|
|
|
|
|
fprintf(stderr, "bladeRF: wrong metadata header magic value, skipping rest of buffer\n");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-05 11:32:44 +00:00
|
|
|
if (metadata_flags & BLADERF_META_STATUS_OVERRUN)
|
2017-01-27 17:44:42 +00:00
|
|
|
overrun = true;
|
|
|
|
|
|
|
|
|
|
if (nextTimestamp && nextTimestamp != metadata_timestamp) {
|
2020-08-05 11:32:44 +00:00
|
|
|
// dropped data or lost sync
|
|
|
|
|
overrun = true;
|
2017-01-27 17:44:42 +00:00
|
|
|
if (metadata_timestamp > nextTimestamp)
|
2020-08-05 11:32:44 +00:00
|
|
|
dropped = (metadata_timestamp - nextTimestamp);
|
|
|
|
|
else
|
|
|
|
|
dropped = 0;
|
2017-01-27 17:44:42 +00:00
|
|
|
}
|
|
|
|
|
|
2020-08-05 11:32:44 +00:00
|
|
|
if (outbuf && (overrun || (outbuf->validLength + samples_per_block > outbuf->totalLength))) {
|
|
|
|
|
// discontinuity or buffer is full. Push the current buffer and get a new one
|
|
|
|
|
fifo_enqueue(outbuf);
|
|
|
|
|
outbuf = NULL;
|
2017-01-27 17:44:42 +00:00
|
|
|
}
|
|
|
|
|
|
2020-08-05 11:32:44 +00:00
|
|
|
if (!outbuf) {
|
|
|
|
|
// need a new buffer
|
|
|
|
|
outbuf = fifo_acquire(/* don't wait */ 0);
|
|
|
|
|
if (!outbuf) {
|
|
|
|
|
// we have nowhere to put this data, drop it. nb: don't update nextTimestamp
|
|
|
|
|
overrun = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set metadata on the new buffer
|
|
|
|
|
outbuf->flags = 0;
|
|
|
|
|
if (overrun) {
|
|
|
|
|
outbuf->flags |= MAGBUF_DISCONTINUOUS;
|
|
|
|
|
}
|
|
|
|
|
outbuf->dropped = dropped;
|
|
|
|
|
outbuf->validLength = outbuf->overlap;
|
|
|
|
|
outbuf->sampleTimestamp = metadata_timestamp * 12e6 / Modes.sample_rate / BladeRF.decimation;
|
|
|
|
|
outbuf->sysTimestamp = entryTimestamp + (num_samples - offset / 4) * 1000 / Modes.sample_rate / BladeRF.decimation;
|
|
|
|
|
outbuf->mean_level = 0;
|
|
|
|
|
outbuf->mean_power = 0;
|
|
|
|
|
|
|
|
|
|
overrun = false;
|
|
|
|
|
dropped = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert one block of sample data
|
2017-01-27 17:44:42 +00:00
|
|
|
double mean_level, mean_power;
|
2020-08-05 08:18:59 +00:00
|
|
|
BladeRF.converter(sample_data, &outbuf->data[outbuf->validLength], samples_per_block, BladeRF.converter_state, &mean_level, &mean_power);
|
|
|
|
|
outbuf->validLength += samples_per_block;
|
2017-01-27 17:44:42 +00:00
|
|
|
outbuf->mean_level += mean_level;
|
|
|
|
|
outbuf->mean_power += mean_power;
|
2020-08-05 11:32:44 +00:00
|
|
|
nextTimestamp = metadata_timestamp + samples_per_block * BladeRF.decimation;
|
2017-01-27 17:44:42 +00:00
|
|
|
}
|
|
|
|
|
|
2020-08-05 11:32:44 +00:00
|
|
|
// push the final buffer, if any
|
|
|
|
|
if (outbuf) {
|
2020-08-05 08:18:59 +00:00
|
|
|
fifo_enqueue(outbuf);
|
2017-01-27 17:44:42 +00:00
|
|
|
}
|
|
|
|
|
|
2020-08-05 11:32:44 +00:00
|
|
|
first_buffer = false;
|
2017-01-27 17:44:42 +00:00
|
|
|
return samples;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void bladeRFRun()
|
|
|
|
|
{
|
|
|
|
|
if (!BladeRF.device) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned transfers = 7;
|
|
|
|
|
|
|
|
|
|
int status;
|
|
|
|
|
struct bladerf_stream *stream = NULL;
|
|
|
|
|
void **buffers = NULL;
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_init_stream(&stream,
|
|
|
|
|
BladeRF.device,
|
|
|
|
|
handle_bladerf_samples,
|
|
|
|
|
&buffers,
|
|
|
|
|
/* num_buffers */ transfers,
|
|
|
|
|
BLADERF_FORMAT_SC16_Q11_META,
|
|
|
|
|
/* samples_per_buffer */ MODES_MAG_BUF_SAMPLES,
|
|
|
|
|
/* num_transfers */ transfers,
|
|
|
|
|
/* user_data */ NULL)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_init_stream() failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned ms_per_transfer = 1000 * MODES_MAG_BUF_SAMPLES / Modes.sample_rate;
|
|
|
|
|
if ((status = bladerf_set_stream_timeout(BladeRF.device, BLADERF_MODULE_RX, ms_per_transfer * (transfers + 2))) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_set_stream_timeout() failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((status = bladerf_enable_module(BladeRF.device, BLADERF_MODULE_RX, true) < 0)) {
|
|
|
|
|
fprintf(stderr, "bladerf_enable_module(RX, true) failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
timeouts = 0; // reset to zero when we get a callback with some data
|
|
|
|
|
retry:
|
|
|
|
|
if ((status = bladerf_stream(stream, BLADERF_MODULE_RX)) < 0) {
|
|
|
|
|
fprintf(stderr, "bladerf_stream() failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
if (status == BLADERF_ERR_TIMEOUT) {
|
|
|
|
|
if (++timeouts < 5)
|
|
|
|
|
goto retry;
|
|
|
|
|
fprintf(stderr, "bladerf is wedged, giving up.\n");
|
|
|
|
|
}
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if ((status = bladerf_enable_module(BladeRF.device, BLADERF_MODULE_RX, false) < 0)) {
|
|
|
|
|
fprintf(stderr, "bladerf_enable_module(RX, false) failed: %s\n", bladerf_strerror(status));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stream) {
|
|
|
|
|
bladerf_deinit_stream(stream);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void bladeRFClose()
|
|
|
|
|
{
|
|
|
|
|
if (BladeRF.converter) {
|
|
|
|
|
cleanup_converter(BladeRF.converter_state);
|
|
|
|
|
BladeRF.converter = NULL;
|
|
|
|
|
BladeRF.converter_state = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (BladeRF.device) {
|
|
|
|
|
bladerf_close(BladeRF.device);
|
|
|
|
|
BladeRF.device = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|