Skip to content

Commit 0ebf6bf

Browse files
author
Deepak Shandilya
committed
Add support for IIO/AD9361 based devices (e.g. ADALM PlutoSDR, LibreSDR etc.)
1 parent a80ba8f commit 0ebf6bf

File tree

5 files changed

+369
-2
lines changed

5 files changed

+369
-2
lines changed

Makefile

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,19 @@ ifeq ($(PKGCONFIG), yes)
3535
ifndef SOAPYSDR
3636
SOAPYSDR := $(shell pkg-config --exists SoapySDR && echo "yes" || echo "no")
3737
endif
38+
39+
ifndef AD9361SDR
40+
AD9361SDR := $(shell pkg-config --exists libiio libad9361 && echo "yes" || echo "no")
41+
endif
42+
3843
else
3944
# pkg-config not available. Only use explicitly enabled libraries.
4045
RTLSDR ?= no
4146
BLADERF ?= no
4247
HACKRF ?= no
4348
LIMESDR ?= no
4449
SOAPYSDR ?= no
50+
AD9361SDR ?= no
4551
endif
4652

4753
BUILD_UNAME := $(shell uname)
@@ -54,7 +60,7 @@ ifeq ($(UNAME), Linux)
5460
DUMP1090_CPPFLAGS += -D_DEFAULT_SOURCE
5561
LIBS += -lrt
5662
LIBS_USB += -lusb-1.0
57-
LIBS_CURSES := -lncurses
63+
LIBS_CURSES := -lncurses -ltinfo
5864
CPUFEATURES ?= yes
5965
endif
6066

@@ -167,6 +173,13 @@ ifeq ($(SOAPYSDR), yes)
167173
LIBS_SDR += $(shell pkg-config --libs SoapySDR)
168174
endif
169175

176+
ifeq ($(AD9361SDR), yes)
177+
SDR_OBJ += sdr_ad9361.o
178+
DUMP1090_CPPFLAGS += -DENABLE_LIBIIO -DENABLE_AD9361
179+
DUMP1090_CFLAGS += -DENABLE_LIBIIO -DENABLE_AD9361
180+
DUMP1090_CFLAGS += $(shell pkg-config --cflags libiio libad9361)
181+
LIBS_SDR += $(shell pkg-config --libs libiio libad9361)
182+
endif
170183

171184
##
172185
## starch (runtime DSP code selection) mix, architecture-specific
@@ -218,6 +231,7 @@ showconfig:
218231
@echo " HackRF support: $(HACKRF)" >&2
219232
@echo " LimeSDR support: $(LIMESDR)" >&2
220233
@echo " SoapySDR support: $(SOAPYSDR)" >&2
234+
@echo " AD9361 support: $(AD9361SDR)" >&2
221235

222236
%.o: %.c *.h
223237
$(CC) $(ALL_CCFLAGS) -c $< -o $@

dump1090.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ typedef enum {
299299
//======================== structure declarations =========================
300300

301301
typedef enum {
302-
SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF, SDR_HACKRF, SDR_LIMESDR, SDR_SOAPYSDR
302+
SDR_NONE, SDR_IFILE, SDR_RTLSDR, SDR_BLADERF, SDR_HACKRF, SDR_LIMESDR, SDR_SOAPYSDR, SDR_AD9361
303303
} sdr_type_t;
304304

305305
// Program global state

sdr.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
#ifdef ENABLE_SOAPYSDR
3737
# include "sdr_soapy.h"
3838
#endif
39+
#ifdef ENABLE_AD9361
40+
# include "sdr_ad9361.h"
41+
#endif
3942

4043
typedef struct {
4144
const char *name;
@@ -134,6 +137,9 @@ static sdr_handler sdr_handlers[] = {
134137
#ifdef ENABLE_SOAPYSDR
135138
{ "soapy", SDR_SOAPYSDR, soapyInitConfig, soapyShowHelp, soapyHandleOption, soapyOpen, soapyRun, noStop, soapyClose, soapyGetGain, soapyGetMaxGain, soapyGetGainDb, soapySetGain },
136139
#endif
140+
#ifdef ENABLE_AD9361
141+
{ "ad9361", SDR_AD9361, ad9361InitConfig, ad9361ShowHelp, ad9361HandleOption, ad9361Open, ad9361Run, noStop, ad9361Close, noGetGain, noGetMaxGain, noGetGainDb, noSetGain },
142+
#endif
137143

138144
{ "none", SDR_NONE, noInitConfig, noShowHelp, noHandleOption, noOpen, noRun, noStop, noClose, noGetGain, noGetMaxGain, noGetGainDb, noSetGain },
139145
{ "ifile", SDR_IFILE, ifileInitConfig, ifileShowHelp, ifileHandleOption, ifileOpen, ifileRun, noStop, ifileClose, noGetGain, noGetMaxGain, noGetGainDb, noSetGain },

sdr_ad9361.c

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
// Part of dump1090, a Mode S message decoder
2+
//
3+
// sdr_ad9361.c: AD9361 One support
4+
//
5+
// Copyright (c) 2019 FlightAware LLC
6+
// Copyright (c) 2025 Deepak Shandilya ([email protected])
7+
//
8+
// This file is free software: you may copy, redistribute and/or modify it
9+
// under the terms of the GNU General Public License as published by the
10+
// Free Software Foundation, either version 2 of the License, or (at your
11+
// option) any later version.
12+
//
13+
// This file is distributed in the hope that it will be useful, but
14+
// WITHOUT ANY WARRANTY; without even the implied warranty of
15+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
// General Public License for more details.
17+
//
18+
// You should have received a copy of the GNU General Public License
19+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
21+
#include "dump1090.h"
22+
#include "sdr_ad9361.h"
23+
24+
#include <iio.h>
25+
#include <ad9361.h>
26+
#include <inttypes.h>
27+
28+
#define AD9361_DEFAULT_RATE 2400000
29+
#define AD9361_DEFAULT_FREQ 1090000000
30+
#define AD9361_DEFAULT_WIDTH 1000
31+
#define AD9361_DEFAULT_HEIGHT 700
32+
#define AD9361_ASYNC_BUF_NUMBER 12
33+
#define AD9361_DATA_LEN (32*16384) /* 512k */
34+
#define AD9361_AUTO_GAIN -100 /* Use automatic gain. */
35+
#define AD9361_MAX_GAIN 70 /* Use max available gain. */
36+
37+
38+
static struct
39+
{
40+
struct iio_device *device;
41+
struct iio_context *ctx;
42+
int dev_index;
43+
uint64_t freq;
44+
int gain;
45+
int enable_agc;
46+
uint32_t samplerate;
47+
struct iio_channel *rx0_i;
48+
struct iio_channel *rx0_q;
49+
struct iio_buffer *rxbuf;
50+
51+
iq_convert_fn converter;
52+
struct converter_state *converter_state;
53+
} AD9361;
54+
55+
void ad9361InitConfig()
56+
{
57+
AD9361.device = NULL;
58+
AD9361.ctx = NULL;
59+
AD9361.freq = Modes.freq;
60+
AD9361.enable_agc = 1;
61+
AD9361.gain = AD9361_AUTO_GAIN;
62+
AD9361.samplerate = AD9361_DEFAULT_RATE;
63+
AD9361.converter = NULL;
64+
AD9361.converter_state = NULL;
65+
}
66+
67+
bool ad9361HandleOption(int argc, char **argv, int *jptr)
68+
{
69+
int j = *jptr;
70+
bool more = (j + 1 < argc);
71+
72+
if (!strcmp (argv[j], "--host") && more) {
73+
Modes.dev_name = strdup (argv[++j]);
74+
}
75+
else if (!strcmp (argv[j], "--samplerate") && more) {
76+
AD9361.samplerate = atoi (argv[++j]);
77+
}
78+
else if (!strcmp (argv[j], "--set-gain") && more) {
79+
AD9361.gain = atoi (argv[++j]);
80+
}
81+
else if (!strcmp (argv[j], "--9361freq") && more) {
82+
AD9361.freq = atoi (argv[++j]);
83+
}
84+
else {
85+
return false;
86+
}
87+
88+
*jptr = j;
89+
return true;
90+
}
91+
92+
void ad9361ShowHelp()
93+
{
94+
printf(" AD9361-specific options (use with --device-type ad9361)\n");
95+
printf("\n");
96+
printf("--host device string : e.g ip:pluto.local or ip:<addr>\n");
97+
printf("--samplerate set sample rate (default: 2400000Hz)\n");
98+
printf("--set-gain set gain : -100 for auto or upto 70 for manual gain\n");
99+
printf("--9361freq set frequency (default : 1090000000 Hz)\n");
100+
printf("\n");
101+
}
102+
103+
static void show_config()
104+
{
105+
fprintf (stderr, "* Frequency : %" PRIu64 "\n", AD9361.freq);
106+
fprintf (stderr, "* Sample Rate : %u \n", AD9361.samplerate);
107+
fprintf (stderr, "* Gain (-100 = AGC) : %d \n", AD9361.gain);
108+
if (NULL != Modes.dev_name) {
109+
fprintf (stderr, "* Device : %s\n", Modes.dev_name);
110+
}
111+
else
112+
fprintf (stderr, "* Device : Local\n");
113+
}
114+
115+
bool ad9361Open()
116+
{
117+
if (AD9361.device) {
118+
return true;
119+
}
120+
121+
int device_count;
122+
123+
AD9361.ctx = NULL;
124+
fprintf (stderr, "* Acquiring IIO context\n");
125+
if (NULL != Modes.dev_name) {
126+
AD9361.ctx = iio_create_network_context (Modes.dev_name);
127+
}
128+
if (AD9361.ctx == NULL) {
129+
AD9361.ctx = iio_create_default_context ();
130+
}
131+
if (AD9361.ctx == NULL) {
132+
AD9361.ctx = iio_create_network_context ("pluto.local");
133+
}
134+
if (NULL == AD9361.ctx) return false;
135+
136+
device_count = iio_context_get_devices_count (AD9361.ctx);
137+
138+
if (!device_count) {
139+
fprintf (stderr, "No supported AD9361 devices found.\n");
140+
return false;
141+
}
142+
143+
fprintf (stderr, "Found %d device(s):\n", device_count);
144+
145+
fprintf (stderr, "* Acquiring AD9361 streaming devices\n");
146+
147+
AD9361.device = iio_context_find_device (AD9361.ctx, "cf-ad9361-lpc");
148+
149+
if (AD9361.device == NULL) {
150+
fprintf (stderr, "Error opening the PLUTOSDR device: %s\n", strerror (errno));
151+
return false;
152+
}
153+
154+
fprintf (stderr, "* Acquiring AD9361 phy channel 0\n");
155+
156+
struct iio_channel *phy_chn =
157+
iio_device_find_channel (iio_context_find_device
158+
(AD9361.ctx, "ad9361-phy"), "voltage0", false);
159+
160+
iio_channel_attr_write (phy_chn, "rf_port_select", "A_BALANCED");
161+
iio_channel_attr_write_longlong (phy_chn, "rf_bandwidth",
162+
AD9361.samplerate);
163+
iio_channel_attr_write_longlong (phy_chn, "sampling_frequency",
164+
AD9361.samplerate);
165+
166+
struct iio_channel *lo_chn =
167+
iio_device_find_channel (iio_context_find_device
168+
(AD9361.ctx, "ad9361-phy"), "altvoltage0", true);
169+
170+
iio_channel_attr_write_longlong (lo_chn, "frequency", AD9361.freq);
171+
172+
fprintf (stderr, "* Initializing AD9361 IIO streaming channels\n");
173+
174+
AD9361.rx0_i = iio_device_find_channel (AD9361.device, "voltage0", false);
175+
176+
if (!AD9361.rx0_i) AD9361.rx0_i = iio_device_find_channel (AD9361.device, "altvoltage0", false);
177+
178+
AD9361.rx0_q = iio_device_find_channel (AD9361.device, "voltage1", false);
179+
180+
if (!AD9361.rx0_q) AD9361.rx0_q = iio_device_find_channel (AD9361.device, "altvoltage1", false);
181+
182+
ad9361_set_bb_rate (iio_context_find_device (AD9361.ctx, "ad9361-phy"), AD9361.samplerate);
183+
184+
fprintf (stderr, "* Enabling IIO streaming channels\n");
185+
186+
iio_channel_enable (AD9361.rx0_i);
187+
iio_channel_enable (AD9361.rx0_q);
188+
189+
fprintf (stderr, "* Creating non-cyclic IIO buffers \n");
190+
191+
AD9361.rxbuf =
192+
iio_device_create_buffer (AD9361.device, AD9361_DATA_LEN / 4, false);
193+
194+
if (!AD9361.rxbuf) {
195+
fprintf (stderr, "Could not create RX buffer");
196+
return false;
197+
}
198+
199+
if (AD9361.gain == AD9361_AUTO_GAIN) {
200+
iio_channel_attr_write (phy_chn, "gain_control_mode", "slow_attack");
201+
fprintf (stderr, "* Using AGC\r\n");
202+
}
203+
else {
204+
if (AD9361.gain > AD9361_MAX_GAIN) AD9361.gain = AD9361_MAX_GAIN;
205+
iio_channel_attr_write (phy_chn, "gain_control_mode", "manual");
206+
iio_channel_attr_write_longlong (phy_chn, "hardwaregain", AD9361.gain);
207+
}
208+
209+
show_config();
210+
211+
AD9361.converter = init_converter (INPUT_SC16,
212+
Modes.sample_rate,
213+
Modes.dc_filter,
214+
&AD9361.converter_state);
215+
216+
if (!AD9361.converter) {
217+
fprintf (stderr, "AD9361: can't initialize sample converter\n");
218+
return false;
219+
}
220+
221+
return true;
222+
}
223+
224+
void ad9361Run()
225+
{
226+
void *p_dat;
227+
static unsigned dropped = 0;
228+
static unsigned sampleCounter = 0;
229+
size_t sample_size = iio_device_get_sample_size (AD9361.device);
230+
231+
if (!AD9361.device) {
232+
return;
233+
}
234+
235+
while (!Modes.exit) {
236+
ssize_t bytes_read = iio_buffer_refill (AD9361.rxbuf);
237+
p_dat = iio_buffer_first (AD9361.rxbuf, AD9361.rx0_i);
238+
int samples_read = bytes_read / sample_size;
239+
struct mag_buf *outbuf = NULL;
240+
outbuf = fifo_acquire ( /* don't wait */ 0);
241+
242+
if (!outbuf) {
243+
// FIFO is full. Drop this block.
244+
fprintf (stderr, "FIFO is full \r\n");
245+
dropped += samples_read;
246+
sampleCounter += samples_read;
247+
return;
248+
}
249+
250+
outbuf->flags = 0;
251+
if (dropped) {
252+
// We previously dropped some samples due to no buffers being available
253+
fprintf (stderr, "Was previously dropped \r\n");
254+
outbuf->flags |= MAGBUF_DISCONTINUOUS;
255+
outbuf->dropped = dropped;
256+
}
257+
258+
dropped = 0;
259+
260+
// Compute the sample timestamp and system timestamp for the start of the block
261+
outbuf->sampleTimestamp = sampleCounter * 12e6 / Modes.sample_rate;
262+
sampleCounter += samples_read;
263+
264+
// Get the approx system time for the start of this block
265+
uint64_t block_duration = 1e3 * samples_read / Modes.sample_rate;
266+
outbuf->sysTimestamp = mstime () - block_duration;
267+
268+
// Convert the new data
269+
unsigned int to_convert = samples_read;
270+
if (to_convert + outbuf->overlap > outbuf->totalLength) {
271+
// how did that happen?
272+
fprintf (stderr, "Overlap overflow \r\n");
273+
to_convert = outbuf->totalLength - outbuf->overlap;
274+
dropped = samples_read - to_convert;
275+
}
276+
277+
AD9361.converter (p_dat, &outbuf->data[outbuf->overlap], to_convert,
278+
AD9361.converter_state, &outbuf->mean_level,
279+
&outbuf->mean_power);
280+
281+
outbuf->validLength = outbuf->overlap + to_convert;
282+
283+
// Push to the demodulation thread
284+
fifo_enqueue (outbuf);
285+
}
286+
}
287+
288+
void ad9361Close()
289+
{
290+
if (AD9361.device) {
291+
fprintf (stderr, "* Destroying buffers\n");
292+
293+
if (AD9361.rxbuf) {
294+
iio_buffer_destroy (AD9361.rxbuf);
295+
}
296+
297+
fprintf (stderr, "* Disabling streaming channels\n");
298+
299+
if (AD9361.rx0_i) {
300+
iio_channel_disable (AD9361.rx0_i);
301+
}
302+
303+
if (AD9361.rx0_q) {
304+
iio_channel_disable (AD9361.rx0_q);
305+
}
306+
307+
fprintf (stderr, "* Destroying context\n");
308+
309+
if (AD9361.ctx) {
310+
iio_context_destroy (AD9361.ctx);
311+
}
312+
313+
AD9361.device = NULL;
314+
}
315+
}

0 commit comments

Comments
 (0)