Hi,
I'm trying to capture DJI Ocusync 2 packets (Ocusync 2 are not encrypted btw) using an E200 with Libiio to interface in between, but I struggle to capture and store the raw packets in the right format. Below is the used code (modified ad9361-iiostream.c):
#include <complex.h>
#include <iio.h>
#include <math.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define MHZ(x) ((long long)(x * 1000000.0 + 0.5))
#define GHZ(x) ((long long)(x * 1000000000.0 + 0.5))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define SAMPLE_RATE MHZ(61.44)
#define SAMPLES_PER_BUFFER (8192 * 1024)
#define MIN_SIGNAL_DB -60.0f
#define BANDWIDTH MHZ(50)
typedef struct {
double freq;
float dwell;
} freq_entry_t;
const freq_entry_t freq_table[] = {{2444.5e6, 1.3f}};
enum iodev { RX };
struct stream_cfg {
long long bw_hz;
long long fs_hz;
long long lo_hz;
long long gain_db;
const char *rfport;
const char *gain_mode;
};
static char tmpstr[64];
static struct iio_context *ctx = NULL;
static struct iio_channel *rx0_i = NULL;
static struct iio_channel *rx0_q = NULL;
static struct iio_buffer *rxbuf = NULL;
static struct iio_device *rx = NULL;
static bool stop = false;
static void shutdown(void) {
printf("* Destroying buffers\n");
if (rxbuf)
iio_buffer_destroy(rxbuf);
printf("* Disabling channels\n");
if (rx0_i)
iio_channel_disable(rx0_i);
if (rx0_q)
iio_channel_disable(rx0_q);
printf("* Destroying context\n");
if (ctx)
iio_context_destroy(ctx);
exit(0);
}
static void handle_sig() {
printf("\nCaptured interrupt signal. Stopping...\n");
stop = true;
}
static void errchk(int v, const char *what) {
if (v < 0) {
fprintf(stderr, "Error %d writing to %s: %s\n", v, what, strerror(-v));
shutdown();
}
}
static void wr_ch_lli(struct iio_channel *chn, const char *what,
long long val) {
errchk(iio_channel_attr_write_longlong(chn, what, val), what);
}
static void wr_ch_str(struct iio_channel *chn, const char *what,
const char *str) {
errchk(iio_channel_attr_write(chn, what, str), what);
}
static char *get_ch_name(const char *type, int id) {
snprintf(tmpstr, sizeof(tmpstr), "%s%d", type, id);
return tmpstr;
}
static struct iio_device *get_ad9361_phy(void) {
struct iio_device *dev = iio_context_find_device(ctx, "ad9361-phy");
if (!dev)
fprintf(stderr, "No ad9361-phy found\n");
return dev;
}
static bool get_ad9361_stream_dev(struct iio_device **dev) {
*dev = iio_context_find_device(ctx, "cf-ad9361-lpc");
return *dev != NULL;
}
static bool get_ad9361_stream_ch(struct iio_device *dev, int chid,
struct iio_channel **chn) {
*chn = iio_device_find_channel(dev, get_ch_name("voltage", chid), false);
return *chn != NULL;
}
static bool get_phy_chan(int chid, struct iio_channel **chn) {
*chn = iio_device_find_channel(get_ad9361_phy(), get_ch_name("voltage", chid),
false);
return *chn != NULL;
}
static bool get_lo_chan(struct iio_channel **chn) {
*chn = iio_device_find_channel(get_ad9361_phy(), get_ch_name("altvoltage", 0),
true);
return *chn != NULL;
}
static float calculate_power(float complex *buffer, size_t size) {
float power = 0.0f, mag_squared = 0;
for (size_t i = 0; i < size; i++) {
mag_squared = crealf(buffer[i]) * crealf(buffer[i]) +
cimagf(buffer[i]) * cimagf(buffer[i]);
power += mag_squared;
}
return 10 * log10f((power / size) + 1e-10);
}
static bool cfg_ad9361_streaming_ch(struct stream_cfg *cfg, int chid) {
struct iio_channel *chn = NULL;
if (!get_phy_chan(chid, &chn))
return false;
wr_ch_str(chn, "rf_port_select", cfg->rfport);
wr_ch_str(chn, "gain_control_mode", cfg->gain_mode);
if (strcmp(cfg->gain_mode, "manual") == 0) {
wr_ch_lli(chn, "hardwaregain", cfg->gain_db);
}
wr_ch_lli(chn, "rf_bandwidth", cfg->bw_hz);
wr_ch_lli(chn, "sampling_frequency", cfg->fs_hz);
if (!get_lo_chan(&chn))
return false;
wr_ch_lli(chn, "frequency", cfg->lo_hz);
return true;
}
static void capture_frequency(double freq, float dwell, FILE *fp_complex) {
struct iio_channel *lo_chn;
if (!get_lo_chan(&lo_chn)) {
fprintf(stderr, "Failed to find LO channel\n");
return;
}
printf("Tuning to %.3f MHz... ", freq / 1e6);
fflush(stdout);
wr_ch_lli(lo_chn, "frequency", (long long)freq);
size_t total_samples = 0;
float avg_power = -120.0f;
bool signal_detected = false;
printf("Dwelling for %.1f seconds\n", dwell);
static float scale = 1.0f / 2048.0f;
char *p_dat, *p_end;
ptrdiff_t p_inc;
int buffer_index = 0;
size_t num_samps = 0, buffer_data_type_complex = sizeof(float complex),
buffer_size_complex = 0;
ssize_t nbytes = 0;
float power_db = 0;
const struct iio_data_format *fmt = iio_channel_get_data_format(rx0_i);
printf("Data length: %d\nData bits: %d\nData shift: %d\nData with scale: "
"%d\n16-bit data: %d\n",
fmt->length, fmt->bits, fmt->shift, fmt->with_scale,
fmt->length / 8 == sizeof(int16_t));
while (!stop) {
nbytes = iio_buffer_refill(rxbuf);
if (nbytes < 0) {
fprintf(stderr, "Buffer refill error: %zd\n", nbytes);
break;
} else {
printf("Number of bytes refilled: %zd\n", nbytes);
}
num_samps = buffer_index = 0;
buffer_size_complex = nbytes * buffer_data_type_complex;
float complex *buffer_complex = malloc(buffer_size_complex);
if (!buffer_complex) {
fprintf(stderr, "Memory allocation failed\n");
break;
}
p_inc = iio_buffer_step(rxbuf);
p_end = iio_buffer_end(rxbuf);
p_dat = (char *)iio_buffer_first(rxbuf, rx0_i);
printf("Buffer start: %p, end: %p, step: %td\n", (void *)p_dat,
(void *)p_end, p_inc);
for (; p_dat < p_end; p_dat += p_inc) {
buffer_complex[buffer_index] =
(((int16_t *)p_dat)[0] * scale) + I * (((int16_t *)p_dat)[1] * scale);
num_samps++;
buffer_index++;
}
power_db = calculate_power(buffer_complex, num_samps);
avg_power = avg_power * 0.7f + power_db * 0.3f;
if (avg_power > MIN_SIGNAL_DB) {
if (!signal_detected) {
printf(" Signal detected! Power: %.1f dB\n", avg_power);
signal_detected = true;
}
fwrite(buffer_complex, buffer_data_type_complex, num_samps, fp_complex);
} else if (signal_detected && (total_samples % (SAMPLE_RATE / 2) == 0)) {
printf(" Signal faded. Power: %.1f dB\n", avg_power);
signal_detected = false;
}
free(buffer_complex);
total_samples += num_samps;
}
printf("\nFinished with %.3f MHz\n", freq / 1e6);
}
FILE *create_file(char *filename) {
printf("* Opening output file: %s\n", filename);
FILE *fp = fopen(filename, "wb");
if (!fp) {
perror("File open failed");
shutdown();
return NULL;
} else {
return fp;
}
}
int main(int argc, char **argv) {
struct stream_cfg rxcfg = {.bw_hz = BANDWIDTH,
.fs_hz = SAMPLE_RATE,
.lo_hz = GHZ(2.4),
.gain_db = 62,
.rfport = "A_BALANCED",
.gain_mode = "fast_attack"};
signal(SIGINT, handle_sig);
if (argc == 1) {
ctx = iio_create_local_context();
} else if (argc == 2) {
ctx = iio_create_context_from_uri(argv[1]);
}
if (!ctx) {
fprintf(stderr, "Local context creation failed\n");
return 1;
}
printf("* Connected to local context: %s\n",
iio_context_get_attr_value(ctx, "local,kernel"));
if (!get_ad9361_stream_dev(&rx)) {
fprintf(stderr, "RX device not found\n");
shutdown();
}
printf("* Configuring SDR...\n");
if (!cfg_ad9361_streaming_ch(&rxcfg, 0)) {
fprintf(stderr, "RX config failed\n");
shutdown();
}
if (!get_ad9361_stream_ch(rx, 0, &rx0_i) ||
!get_ad9361_stream_ch(rx, 1, &rx0_q)) {
fprintf(stderr, "RX channels not found\n");
shutdown();
}
iio_channel_enable(rx0_i);
iio_channel_enable(rx0_q);
printf("* Creating buffer (%d samples per buffer)\n", SAMPLES_PER_BUFFER);
rxbuf = iio_device_create_buffer(rx, SAMPLES_PER_BUFFER, false);
if (!rxbuf) {
perror("Buffer creation failed");
shutdown();
}
iio_device_set_kernel_buffers_count(rx, 1);
char filename_complex[] = "droneid_complex.raw";
FILE *fp_complex = create_file(filename_complex);
capture_frequency(freq_table[0].freq, freq_table[0].dwell, fp_complex);
printf("\n* Capture complete. Closing file.\n");
fclose(fp_complex);
printf("* Saved to %s\n", filename_complex);
shutdown();
return 0;
}
What I expect sampled (when viewed in Inspectrum) is smth similar to this (sampled from https://github.com/RUB-SysSec/DroneSecurity/tree/public_squash/samples):
While this is what I'm able to capture (at a higher sample frequency, but at the same sampling frequency I get similar results)
What I'm curious about is if there's some apparent flaw with my approach with capturing the raw packets and do the conversion manually from the hardware format?