diff --git a/Makefile b/Makefile index 6343ffb..46d0c3c 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ PROGNAME=dump1090 RTLSDR ?= yes BLADERF ?= yes +HACKRF ?= yes CPPFLAGS += -DMODES_DUMP1090_VERSION=\"$(DUMP1090_VERSION)\" -DMODES_DUMP1090_VARIANT=\"dump1090-fa\" @@ -39,6 +40,13 @@ ifeq ($(BLADERF), yes) LIBS_SDR += $(shell pkg-config --libs libbladeRF) endif +ifeq ($(HACKRF), yes) + SDR_OBJ += sdr_hackrf.o + CPPFLAGS += -DENABLE_HACKRF + CFLAGS += $(shell pkg-config --cflags libhackrf) + LIBS_SDR += $(shell pkg-config --libs libhackrf) +endif + all: dump1090 view1090 %.o: %.c *.h diff --git a/README.md b/README.md index 31864c4..b578de8 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,10 @@ see https://flightaware.com/adsb/piaware/install This is packaged with jessie. `sudo apt-get install librtlsdr-dev` +### Dependencies - HackRF + +This is packaged with jessie. `sudo apt-get install libhackrf-dev` + ### Actually building it Nothing special, just build it (`dpkg-buildpackage -b`) @@ -45,7 +49,7 @@ Nothing special, just build it (`dpkg-buildpackage -b`) First run `prepare-wheezy-tree.sh`. This will create a package tree in package-wheezy/. Build in there (`dpkg-buildpackage -b`) -The wheezy build does not include bladeRF support. +The wheezy build does not include bladeRF nor HackRF support. ## Building manually @@ -58,3 +62,6 @@ libbladeRF. ``make RTLSDR=no`` will disable rtl-sdr support and remove the dependency on librtlsdr. + +``make HACKRF=no`` will disable HackRF support and remove the dependency on +libhackrf. \ No newline at end of file diff --git a/dump1090.c b/dump1090.c index b654c01..88e8194 100644 --- a/dump1090.c +++ b/dump1090.c @@ -267,6 +267,9 @@ void showHelp(void) { #ifdef ENABLE_BLADERF "ENABLE_BLADERF " #endif +#ifdef ENABLE_HACKRF + "ENABLE_HACKRF " +#endif #ifdef SC16Q11_TABLE_BITS // This is a little silly, but that's how the preprocessor works.. #define _stringize(x) #x diff --git a/dump1090.h b/dump1090.h index 90700ce..6bf2f62 100644 --- a/dump1090.h +++ b/dump1090.h @@ -279,7 +279,7 @@ typedef enum { //======================== structure declarations ========================= typedef enum { - SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF + SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF, SDR_HACKRF } sdr_type_t; // Structure representing one magnitude buffer diff --git a/sdr.c b/sdr.c index 89fe8f4..569bc9a 100644 --- a/sdr.c +++ b/sdr.c @@ -27,6 +27,9 @@ #ifdef ENABLE_BLADERF # include "sdr_bladerf.h" #endif +#ifdef ENABLE_HACKRF +# include "sdr_hackrf.h" +#endif typedef struct { const char *name; @@ -85,6 +88,10 @@ static sdr_handler sdr_handlers[] = { { "bladerf", SDR_BLADERF, bladeRFInitConfig, bladeRFShowHelp, bladeRFHandleOption, bladeRFOpen, bladeRFRun, bladeRFClose }, #endif +#ifdef ENABLE_HACKRF + { "hackrf", SDR_HACKRF, hackRFInitConfig, hackRFShowHelp, hackRFHandleOption, hackRFOpen, hackRFRun, hackRFClose }, +#endif + { "ifile", SDR_IFILE, ifileInitConfig, ifileShowHelp, ifileHandleOption, ifileOpen, ifileRun, ifileClose }, { "none", SDR_NONE, noInitConfig, noShowHelp, noHandleOption, noOpen, noRun, noClose }, diff --git a/sdr_hackrf.c b/sdr_hackrf.c new file mode 100644 index 0000000..3a1ad98 --- /dev/null +++ b/sdr_hackrf.c @@ -0,0 +1,322 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// sdr_hackrf.h: HackRF One support (header) +// +// Copyright (c) 2019 FlightAware LLC +// +// This file is free software: you may copy, redistribute and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 2 of the License, or (at your +// option) any later version. +// +// 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "dump1090.h" +#include "sdr_hackrf.h" + +#include +#include + +static struct { + hackrf_device *device; + uint64_t freq; + int enable_amp; + int lna_gain; + int vga_gain; + int rate; + int ppm; + + iq_convert_fn converter; + struct converter_state *converter_state; +} HackRF; + +void hackRFInitConfig() +{ + HackRF.device = NULL; + HackRF.freq = 1090000000; + HackRF.enable_amp = 0; + HackRF.lna_gain = 32; + HackRF.vga_gain = 50; + HackRF.rate = 2400000; + HackRF.ppm = 0; + HackRF.converter = NULL; + HackRF.converter_state = NULL; +} + +bool hackRFHandleOption(int argc, char **argv, int *jptr) +{ + int j = *jptr; + bool more = (j+1 < argc); + if (!strcmp(argv[j], "--lna-gain") && more) { + HackRF.lna_gain = atoi(argv[++j]); + + if (HackRF.lna_gain % 8 != 0) { + printf("Error: --lna-gain must be multiple of 8\n"); + return false; + } + + if (HackRF.lna_gain > 40 || HackRF.lna_gain < 0) { + printf("Error: --lna-gain range is 0 - 42\n"); + return false; + } + } else if (!strcmp(argv[j], "--vga-gain") && more) { + HackRF.vga_gain = atoi(argv[++j]); + + if (HackRF.vga_gain % 2 != 0) { + printf("Error: --vga-gain must be multiple of 2\n"); + return false; + } + + if (HackRF.vga_gain > 62 || HackRF.vga_gain < 0) { + printf("Error: --vga-gain range is 0 - 62\n"); + return false; + } + + } else if (!strcmp(argv[j], "--ppm") && more) { + HackRF.ppm = atoi(argv[++j]); + } else if (!strcmp(argv[j], "--samplerate") && more) { + HackRF.rate = atoi(argv[++j]); + } else if (!strcmp(argv[j], "--enable-amp") && more) { + HackRF.enable_amp = 1; + } else { + return false; + } + + *jptr = j; + + return true; +} + +void hackRFShowHelp() +{ + printf(" HackRF-specific options (use with --device-type hackrf)\n"); + printf("\n"); + printf("--enable-amp enable amplifier)\n"); + printf("--lna-gain set LNA gain (Range 0-40 in 8dB steps))\n"); + printf("--vga-gain set VGA gain (Range 0-62 in 2dB steps))\n"); + printf("--samplerate set sample rate)\n"); + printf("--ppm ppm correction)\n"); + printf("\n"); +} + +static void show_config() +{ + printf("freq : %ld\n", HackRF.freq); + printf("lna_gain : %d\n", HackRF.lna_gain); + printf("vga_gain : %d\n", HackRF.vga_gain); + printf("samplerate : %d\n", HackRF.rate); + printf("ppm : %d\n", HackRF.ppm); +} + +bool hackRFOpen() +{ + if (HackRF.device) { + return true; + } + + // Calculate sample rate and frequency deviation if ppm is specified + if (HackRF.ppm != 0) { + HackRF.rate = (uint32_t)((double)HackRF.rate * (1000000 - HackRF.ppm)/1000000+0.5); + HackRF.freq = HackRF.freq * (1000000 - HackRF.ppm)/1000000; + } + + int status; + + status = hackrf_init(); + if (status != 0) { + printf("HackRF: hackrf_init failed with code %d", status); + hackrf_close(HackRF.device); + hackrf_exit(); + return false; + } + + status = hackrf_open(&HackRF.device); + if (status != 0) { + printf("HackRF: hackrf_open failed with code %d", status); + hackrf_close(HackRF.device); + hackrf_exit(); + return false; + } + + status = hackrf_set_freq(HackRF.device, HackRF.freq); + if (status != 0) { + printf("HackRF: hackrf_set_freq failed with code %d", status); + hackrf_close(HackRF.device); + hackrf_exit(); + return false; + } + + status = hackrf_set_sample_rate(HackRF.device, HackRF.rate); + if (status != 0) { + printf("HackRF: hackrf_set_sample_rate failed with code %d", status); + hackrf_close(HackRF.device); + hackrf_exit(); + return false; + } + + status = hackrf_set_amp_enable(HackRF.device, HackRF.enable_amp); + if (status != 0) { + printf("HackRF: hackrf_set_amp_enable failed with code %d", status); + hackrf_close(HackRF.device); + hackrf_exit(); + return false; + } + + status = hackrf_set_lna_gain(HackRF.device, HackRF.lna_gain); + if (status != 0) { + printf("HackRF: hackrf_set_lna_gain failed with code %d", status); + hackrf_close(HackRF.device); + hackrf_exit(); + return false; + } + + status = hackrf_set_vga_gain(HackRF.device, HackRF.vga_gain); + if (status != 0) { + printf("HackRF: hackrf_set_vga_gain failed with code %d", status); + hackrf_close(HackRF.device); + hackrf_exit(); + return false; + } + + show_config(); + + HackRF.converter = init_converter(INPUT_UC8, + Modes.sample_rate, + Modes.dc_filter, + &HackRF.converter_state); + if (!HackRF.converter) { + printf("HackRF: can't initialize sample converter\n"); + return false; + } + + return true; +} + +struct timespec thread_cpu; + +int handle_hackrf_samples(hackrf_transfer *transfer) +{ + struct mag_buf *outbuf; + struct mag_buf *lastbuf; + unsigned char *buf; + int32_t len; + uint32_t slen; + unsigned next_free_buffer; + unsigned free_bufs; + unsigned block_duration; + + static int dropping = 0; + static uint64_t sampleCounter = 0; + + // Lock the data buffer variables before accessing them + pthread_mutex_lock(&Modes.data_mutex); + if (Modes.exit) { + pthread_mutex_unlock(&Modes.data_mutex); + return -1; + } + + next_free_buffer = (Modes.first_free_buffer + 1) % MODES_MAG_BUFFERS; + outbuf = &Modes.mag_buffers[Modes.first_free_buffer]; + lastbuf = &Modes.mag_buffers[(Modes.first_free_buffer + MODES_MAG_BUFFERS - 1) % MODES_MAG_BUFFERS]; + free_bufs = (Modes.first_filled_buffer - next_free_buffer + MODES_MAG_BUFFERS) % MODES_MAG_BUFFERS; + + buf = transfer->buffer; + len = transfer->buffer_length; + + // Values returned by HackRF need conversion from signed to unsigned + for (int32_t i = 0; i < len; i++) { + buf[i] ^= (uint8_t)0x80; + } + + slen = len/2; // Drops any trailing odd sample, that's OK + + if (free_bufs == 0 || (dropping && free_bufs < MODES_MAG_BUFFERS/2)) { + // FIFO is full. Drop this block. + dropping = 1; + outbuf->dropped += slen; + sampleCounter += slen; + pthread_mutex_unlock(&Modes.data_mutex); + return -1; + } + + dropping = 0; + pthread_mutex_unlock(&Modes.data_mutex); + + // Compute the sample timestamp and system timestamp for the start of the block + outbuf->sampleTimestamp = sampleCounter * 12e6 / Modes.sample_rate; + sampleCounter += slen; + + // Get the approx system time for the start of this block + block_duration = 1e3 * slen / Modes.sample_rate; + outbuf->sysTimestamp = mstime() - block_duration; + + // Copy trailing data from last block (or reset if not valid) + if (outbuf->dropped == 0) { + memcpy(outbuf->data, lastbuf->data + lastbuf->length, Modes.trailing_samples * sizeof(uint16_t)); + } else { + memset(outbuf->data, 0, Modes.trailing_samples * sizeof(uint16_t)); + } + + // Convert the new data + outbuf->length = slen; + HackRF.converter(buf, &outbuf->data[Modes.trailing_samples], slen, HackRF.converter_state, &outbuf->mean_level, &outbuf->mean_power); + + // Push the new data to the demodulation thread + pthread_mutex_lock(&Modes.data_mutex); + + Modes.mag_buffers[next_free_buffer].dropped = 0; + Modes.mag_buffers[next_free_buffer].length = 0; // just in case + Modes.first_free_buffer = next_free_buffer; + + // accumulate CPU while holding the mutex, and restart measurement + end_cpu_timing(&thread_cpu, &Modes.reader_cpu_accumulator); + start_cpu_timing(&thread_cpu); + + pthread_cond_signal(&Modes.data_cond); + pthread_mutex_unlock(&Modes.data_mutex); + + return 0; +} + + +void hackRFRun() +{ + if (!HackRF.device) { + printf("hackRFRun: HackRF.device = NULL\n"); + return; + } + + start_cpu_timing(&thread_cpu); + + int status = hackrf_start_rx(HackRF.device, &handle_hackrf_samples, NULL); + + if (status != 0) { + printf("hackrf_start_rx failed"); + hackrf_close(HackRF.device); + hackrf_exit(); + exit (1); + } + + // hackrf_start_rx does not block so we need to wait until the streaming is finished + // before returning from the hackRFRun function + while (hackrf_is_streaming(HackRF.device) == HACKRF_TRUE) { + usleep(10000); + } + + printf("HackRF stopped streaming %d\n", hackrf_is_streaming(HackRF.device)); +} + +void hackRFClose() +{ + if (HackRF.device) { + hackrf_close(HackRF.device); + hackrf_exit(); + HackRF.device = NULL; + } +} diff --git a/sdr_hackrf.h b/sdr_hackrf.h new file mode 100644 index 0000000..df77634 --- /dev/null +++ b/sdr_hackrf.h @@ -0,0 +1,30 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// sdr_hackrf.h: HackRF One support (header) +// +// Copyright (c) 2019 FlightAware LLC +// +// This file is free software: you may copy, redistribute and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation, either version 2 of the License, or (at your +// option) any later version. +// +// 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef HACKRF_H +#define HACKRF_H + +void hackRFInitConfig(); +void hackRFShowHelp(); +bool hackRFHandleOption(int argc, char **argv, int *jptr); +bool hackRFOpen(); +void hackRFRun(); +void hackRFClose(); + +#endif