From 53183c0b2a26006e5ac8d97fe6a09d771638f7a7 Mon Sep 17 00:00:00 2001 From: Gluttton Date: Sun, 26 Jul 2020 16:52:48 +0300 Subject: [PATCH] limesdr: basic implementation of LimeSDR support The commit provides a basic implementation of support for LimeSDR USB receivers based on LMS7002 chip. The solution has several limitations: - passing parameters for tune LimeSDR receiver via command-line options is not implemented; - only hardcoded configuration is used (channel 0 of lower band LNA, LMS_FMT_I16 format, gain, bandwidth and timeout); - only one device is supported and it is not possible to select a desired one in case if several devices are connected to the host. Test: compare the output of the program for RTL and LimeSDR receivers. Environment: - RTL2832SDR dongle; - LMS7002M based USB LimeSDR board; - 800MHz-2200MHz omnidirectional antenna with SMA connector. Procedure: - connect RTL dongle to the host and start the program with the following parameters: $ ./dump1090 --device-type rtlsdr --interactive - wait until several planes will be detected; - stop the program, connect the LimeSDR board to the host and restart the program with the following parameters: $ ./dump1090 --device-type limesdr --interactive - ensure that the same planes are detected. Acceptance criteria: the same planes are detected using both receivers and track information matches with information from the FlightRadar24 application. Signed-off-by: Gluttton --- Makefile | 8 ++ README.md | 12 +- dump1090.c | 3 + dump1090.h | 2 +- sdr.c | 7 + sdr_limesdr.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++ sdr_limesdr.h | 30 +++++ 7 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 sdr_limesdr.c create mode 100644 sdr_limesdr.h diff --git a/Makefile b/Makefile index 6343ffb..9ec046f 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ PROGNAME=dump1090 RTLSDR ?= yes BLADERF ?= yes +LIMESDR ?= 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 ($(LIMESDR), yes) + SDR_OBJ += sdr_limesdr.o + CPPFLAGS += -DENABLE_LIMESDR + CFLAGS += $(shell pkg-config --cflags LimeSuite) + LIBS_SDR += $(shell pkg-config --libs LimeSuite) +endif + all: dump1090 view1090 %.o: %.c *.h diff --git a/README.md b/README.md index 31864c4..392b17b 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,11 @@ see https://flightaware.com/adsb/piaware/install This is packaged with jessie. `sudo apt-get install librtlsdr-dev` +### Dependencies - LimeSDR + +You will need a build of [LimeSuite](https://github.com/myriadrf/LimeSuite). +See detailed instruction on [the official Wiki](https://wiki.myriadrf.org/Lime_Suite) how to build and install it. + ### Actually building it Nothing special, just build it (`dpkg-buildpackage -b`) @@ -45,7 +50,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 and LimeSDR support. ## Building manually @@ -56,5 +61,8 @@ install them (and a method for starting them) yourself. ``make BLADERF=no`` will disable bladeRF support and remove the dependency on libbladeRF. -``make RTLSDR=no`` will disable rtl-sdr support and remove the dependency on +``make RTLSDR=no`` will disable rtl-sdr support and remove the dependency on librtlsdr. + +``make LIMESDR=no`` will disable LimeSDR support and remove the dependency on +libLimeSuite. diff --git a/dump1090.c b/dump1090.c index 2506e8c..1274a0f 100644 --- a/dump1090.c +++ b/dump1090.c @@ -267,6 +267,9 @@ void showHelp(void) { #ifdef ENABLE_BLADERF "ENABLE_BLADERF " #endif +#ifdef ENABLE_LIMESDR + "ENABLE_LIMESDR " +#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 5c7cf73..645c9c8 100644 --- a/dump1090.h +++ b/dump1090.h @@ -280,7 +280,7 @@ typedef enum { //======================== structure declarations ========================= typedef enum { - SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF + SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF, SDR_LIMESDR } sdr_type_t; // Structure representing one magnitude buffer diff --git a/sdr.c b/sdr.c index 89fe8f4..84cfff7 100644 --- a/sdr.c +++ b/sdr.c @@ -27,6 +27,9 @@ #ifdef ENABLE_BLADERF # include "sdr_bladerf.h" #endif +#ifdef ENABLE_LIMESDR +# include "sdr_limesdr.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_LIMESDR + { "limesdr", SDR_LIMESDR, limesdrInitConfig, limesdrShowHelp, limesdrHandleOption, limesdrOpen, limesdrRun, limesdrClose }, +#endif + { "ifile", SDR_IFILE, ifileInitConfig, ifileShowHelp, ifileHandleOption, ifileOpen, ifileRun, ifileClose }, { "none", SDR_NONE, noInitConfig, noShowHelp, noHandleOption, noOpen, noRun, noClose }, diff --git a/sdr_limesdr.c b/sdr_limesdr.c new file mode 100644 index 0000000..17bb81a --- /dev/null +++ b/sdr_limesdr.c @@ -0,0 +1,345 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// sdr_limesdr.c: LimeSDR dongle support +// +// Copyright (c) 2020 Gluttton +// +// 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 . + +// This file incorporates work covered by the following copyright and +// permission notice: +// +// Copyright (C) 2012 by Salvatore Sanfilippo +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "dump1090.h" +#include "sdr_limesdr.h" + +#include + +static struct { + lms_device_t *dev; + lms_stream_t stream; + bool is_stream_opened; + bool is_stop; + int bytes_in_sample; + iq_convert_fn converter; + struct converter_state *converter_state; +} LimeSDR; + +void limesdrLogHandler(int lvl, const char *msg) +{ + FILE *out = NULL; + switch (lvl) { + default: + case LMS_LOG_DEBUG: + return; + case LMS_LOG_INFO: + out = stdout; + break; + case LMS_LOG_WARNING: + case LMS_LOG_ERROR: + case LMS_LOG_CRITICAL: + out = stderr; + break; + } + + fprintf(out, "limesdr: %s\n", msg); +} + +void limesdrInitConfig() +{ + LimeSDR.dev = NULL; + LimeSDR.stream.channel = 0; + LimeSDR.stream.fifoSize = 1024 * 1024; + LimeSDR.stream.throughputVsLatency = 1.0; // best throughput + LimeSDR.stream.isTx = false; + LimeSDR.stream.dataFmt = LMS_FMT_I16; // should be matched with conveter + LimeSDR.is_stream_opened = false; + LimeSDR.is_stop = false; + LimeSDR.bytes_in_sample = 2 * sizeof(int16_t); // hardcoded for LMS_FMT_I16 + + LMS_RegisterLogHandler(limesdrLogHandler); +} + +void limesdrShowHelp() +{ + printf(" limesdr-specific options (use with --device-type limesdr)\n"); + printf("\n"); + printf("so far there is no any LimeSDR specific option...\n"); + printf("\n"); +} + +bool limesdrHandleOption(int argc, char **argv, int *jptr) +{ + MODES_NOTUSED(argc); + MODES_NOTUSED(argv); + MODES_NOTUSED(jptr); + + return false; +} + +bool limesdrOpen(void) +{ + const size_t devCountMax = 8; + lms_info_str_t list[devCountMax]; + const int devCount = LMS_GetDeviceList(list); + if (devCount < 0) { + fprintf(stderr, "limesdr: unable to get a number of connected devices\n"); + goto error; + } + + if (devCount < 1) { + fprintf(stderr, "limesdr: no connected devices\n"); + goto error; + } + + if (LMS_Open(&LimeSDR.dev, list[0], NULL) ) { + fprintf(stderr, "limesdr: unable to open device\n"); + goto error; + } + + if (LMS_Init(LimeSDR.dev)) { + fprintf(stderr, "limesdr: unable to initialize device\n"); + goto error; + } + + if (LMS_EnableChannel(LimeSDR.dev, LMS_CH_RX, LimeSDR.stream.channel, true)) { + fprintf(stderr, "limesdr: unable to enable RX channel\n"); + goto error; + } + + if (LMS_SetLOFrequency(LimeSDR.dev, LMS_CH_RX, LimeSDR.stream.channel, Modes.freq)) { + fprintf(stderr, "limesdr: unable to set frequency\n"); + goto error; + } + + if (LMS_SetAntenna(LimeSDR.dev, LMS_CH_RX, LimeSDR.stream.channel, LMS_PATH_LNAL)) { + fprintf(stderr, "limesdr: unable to set RF port\n"); + goto error; + } + + if (LMS_SetSampleRate(LimeSDR.dev, Modes.sample_rate, 0/*default oversample*/)) { + fprintf(stderr, "limesdr: unable to set sampling rate\n"); + goto error; + } + + if (LMS_SetNormalizedGain(LimeSDR.dev, LMS_CH_RX, LimeSDR.stream.channel, 0.85)) { + fprintf(stderr, "limesdr: unable to set gain\n"); + goto error; + } + + if (LMS_SetLPFBW(LimeSDR.dev, LMS_CH_RX, LimeSDR.stream.channel, Modes.sample_rate)) { + fprintf(stderr, "limesdr: unable to set LP filter\n"); + goto error; + } + + LimeSDR.is_stream_opened = true; + if (LMS_SetupStream(LimeSDR.dev, &LimeSDR.stream)) { + fprintf(stderr, "limesdr: unable to setup stream\n"); + LimeSDR.is_stream_opened = false; + goto error; + } + + if (LMS_Calibrate(LimeSDR.dev, LMS_CH_RX, LimeSDR.stream.channel, 2.5e6/*0.5e5*/, 0)) { + fprintf(stderr, "limesdr: unable to calibrate device\n"); + goto error; + } + + LimeSDR.converter = init_converter(INPUT_SC16, + Modes.sample_rate, + Modes.dc_filter, + &LimeSDR.converter_state); + if (!LimeSDR.converter) { + fprintf(stderr, "limesdr: can't initialize sample converter.\n"); + goto error; + } + + return true; + + error: + if (LimeSDR.is_stream_opened) { + LMS_DestroyStream(LimeSDR.dev, &LimeSDR.stream); + LimeSDR.is_stream_opened = false; + } + + if (LimeSDR.dev) { + LMS_Close(LimeSDR.dev); + LimeSDR.dev = NULL; + } + + return false; +} + +static struct timespec limesdr_thread_cpu; + +void limesdrCallback(unsigned char *buf, uint32_t len, void *ctx) +{ + struct mag_buf *outbuf; + struct mag_buf *lastbuf; + uint32_t slen; + unsigned next_free_buffer; + unsigned free_bufs; + unsigned block_duration; + + static int dropping = 0; + static uint64_t sampleCounter = 0; + + MODES_NOTUSED(ctx); + + // Lock the data buffer variables before accessing them + pthread_mutex_lock(&Modes.data_mutex); + if (Modes.exit) { + LimeSDR.is_stop = true; // ask our caller to exit + } + + 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; + + // Paranoia! Unlikely, but let's go for belt and suspenders here + + if (len != MODES_RTL_BUF_SIZE) { + fprintf(stderr, "weirdness: limesdr gave us a block with an unusual size (got %u bytes, expected %u bytes)\n", + (unsigned)len, (unsigned)MODES_RTL_BUF_SIZE); + + if (len > MODES_RTL_BUF_SIZE) { + // wat?! Discard the start. + unsigned discard = (len - MODES_RTL_BUF_SIZE + 1) / LimeSDR.bytes_in_sample; + outbuf->dropped += discard; + buf += discard * LimeSDR.bytes_in_sample; + len -= discard * LimeSDR.bytes_in_sample; + } + } + + slen = len / LimeSDR.bytes_in_sample; // 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; + } + + 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; + LimeSDR.converter(buf, &outbuf->data[Modes.trailing_samples], slen, LimeSDR.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(&limesdr_thread_cpu, &Modes.reader_cpu_accumulator); + start_cpu_timing(&limesdr_thread_cpu); + + pthread_cond_signal(&Modes.data_cond); + pthread_mutex_unlock(&Modes.data_mutex); +} + +void limesdrRun() +{ + if (!LimeSDR.dev) { + return; + } + + int16_t *buffer = malloc(MODES_RTL_BUF_SIZE); + + LMS_StartStream(&LimeSDR.stream); + + start_cpu_timing(&limesdr_thread_cpu); + + while (!LimeSDR.is_stop) { + int sampleCnt = LMS_RecvStream(&LimeSDR.stream, buffer, MODES_RTL_BUF_SIZE / LimeSDR.bytes_in_sample, NULL, 1000); + if (sampleCnt) { + limesdrCallback((unsigned char *)buffer, sampleCnt * LimeSDR.bytes_in_sample, NULL); + } + } + + if (!Modes.exit) { + fprintf(stderr, "limesdr: async read returned unexpectedly.\n"); + } + + free(buffer); + LMS_StopStream(&LimeSDR.stream); +} + +void limesdrClose() +{ + if (LimeSDR.converter) { + cleanup_converter(LimeSDR.converter_state); + LimeSDR.converter = NULL; + LimeSDR.converter_state = NULL; + } + + LMS_StopStream(&LimeSDR.stream); + + if (LimeSDR.is_stream_opened) { + LMS_DestroyStream(LimeSDR.dev, &LimeSDR.stream); + LimeSDR.is_stream_opened = false; + } + + if (LimeSDR.dev) { + LMS_Close(LimeSDR.dev); + LimeSDR.dev = NULL; + } +} diff --git a/sdr_limesdr.h b/sdr_limesdr.h new file mode 100644 index 0000000..49a2f2d --- /dev/null +++ b/sdr_limesdr.h @@ -0,0 +1,30 @@ +// Part of dump1090, a Mode S message decoder for RTLSDR devices. +// +// sdr_limesdr.h: LimeSDR dongle support (header) +// +// Copyright (c) 2020 Gluttton +// +// 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 SDR_LIMESDR_H +#define SDR_LIMESDR_H + +void limesdrInitConfig(); +void limesdrShowHelp(); +bool limesdrOpen(); +void limesdrRun(); +void limesdrClose(); +bool limesdrHandleOption(int argc, char **argv, int *jptr); + +#endif