Post Go back to editing

Incorrect capturing of raw drone packets

Category: Software
Product Number: e200
Software Version: Libiio: v0.25, E200 firmware: v0.39

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):

Expected sample

While this is what I'm able to capture (at a higher sample frequency, but at the same sampling frequency I get similar results)


Captured sample

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?

Thread Notes

Parents Reply Children
No Data