]> sigrok.org Git - libsigrok.git/blobdiff - src/input/trace32_ad.c
output/csv: use intermediate time_t var, silence compiler warning
[libsigrok.git] / src / input / trace32_ad.c
index 4ac06cf565c1792b2df0c0b7437e166d8e8f6d80..ba89ad2c41b7479586ae7204762cb1a86a5f7f6b 100644 (file)
 
 #define LOG_PREFIX "input/trace32_ad"
 
-#define MAX_CHUNK_SIZE    4096
-#define OUTBUF_FLUSH_SIZE 10240
+#define CHUNK_SIZE        (4 * 1024 * 1024)
 #define MAX_POD_COUNT     12
-#define HEADER_SIZE       80
 
-#define TIMESTAMP_RESOLUTION  ((double)0.000000000078125) /* 0.078125 ns */
+#define SPACE             ' '
+#define CTRLZ             '\x1a'
+#define TRACE32           "trace32"
+
+#define TIMESTAMP_RESOLUTION ((double)0.000000000078125) /* 0.078125 ns */
 
 /*
  * The resolution equals a sampling freq of 12.8 GHz. That's a bit high
  * for inter-record sample generation, so we scale it down to 200 MHz
  * for now. That way, the scaling factor becomes 32.
  */
-#define SAMPLING_FREQ 200000000
-#define TIMESTAMP_SCALE ((1 / TIMESTAMP_RESOLUTION) / SAMPLING_FREQ)
+#define DEFAULT_SAMPLERATE 200
 
-enum {
-       AD_FORMAT_BINHDR = 1, /* Binary header, binary data, textual setup info */
-       AD_FORMAT_TXTHDR      /* Textual header, binary data */
+enum ad_format {
+       AD_FORMAT_UNKNOWN,
+       AD_FORMAT_BINHDR1,      /* Binary header, binary data, textual setup info, v1 */
+       AD_FORMAT_BINHDR2,      /* Binary header, binary data, textual setup info, v2 */
+       AD_FORMAT_TXTHDR,       /* Textual header, binary data */
 };
 
-enum {
+enum ad_device {
        AD_DEVICE_PI = 1, /* Data recorded by LA-7940 PowerIntegrator or */
                           /* LA-394x PowerIntegrator II. */
        AD_DEVICE_IPROBE  /* Data recorded by LA-769x PowerTrace II IProbe. */
@@ -73,31 +76,59 @@ enum {
        /* Missing file format info for LA-4530 uTrace analog probe */
 };
 
-enum {
+enum ad_mode {
        AD_MODE_250MHZ = 0,
        AD_MODE_500MHZ = 1
 };
 
-enum {
+enum ad_compr {
        AD_COMPR_NONE  = 0, /* File created with /NOCOMPRESS */
-       AD_COMPR_QCOMP = 6  /* File created with /COMPRESS or /QUICKCOMPRESS */
+       AD_COMPR_QCOMP = 6, /* File created with /COMPRESS or /QUICKCOMPRESS */
 };
 
 struct context {
        gboolean meta_sent;
        gboolean header_read, records_read, trigger_sent;
-       char format, device, record_mode, compression;
+       enum ad_format format;
+       enum ad_device device;
+       enum ad_mode record_mode;
+       enum ad_compr compression;
        char pod_status[MAX_POD_COUNT];
        struct sr_channel *channels[MAX_POD_COUNT][17]; /* 16 + CLK */
        uint64_t trigger_timestamp;
-       uint32_t record_size, record_count, cur_record;
+       uint32_t header_size, record_size, record_count, cur_record;
        int32_t last_record;
+       uint64_t samplerate;
+       double timestamp_scale;
        GString *out_buf;
 };
 
 static int process_header(GString *buf, struct context *inc);
 static void create_channels(struct sr_input *in);
 
+/* Transform non-printable chars to '\xNN' presentation. */
+static char *printable_name(const char *name)
+{
+       size_t l, i;
+       char *s, *p;
+
+       if (!name)
+               return NULL;
+       l = strlen(name);
+       s = g_malloc0(l * strlen("\\x00") + 1);
+       for (p = s, i = 0; i < l; i++) {
+               if (g_ascii_isprint(name[i])) {
+                       *p++ = name[i];
+               } else {
+                       snprintf(p, 5, "\\x%02x", name[i]);
+                       p += strlen("\\x00");
+               }
+       }
+       *p = '\0';
+
+       return s;
+}
+
 static char get_pod_name_from_id(int id)
 {
        switch (id) {
@@ -130,6 +161,12 @@ static int init(struct sr_input *in, GHashTable *options)
 
        inc = in->priv;
 
+       /* Calculate the desired timestamp scaling factor. */
+       inc->samplerate = 1000000 *
+               g_variant_get_uint64(g_hash_table_lookup(options, "samplerate"));
+
+       inc->timestamp_scale = ((1 / TIMESTAMP_RESOLUTION) / (double)inc->samplerate);
+
        /* Enable the pods the user chose to see. */
        for (pod = 0; pod < MAX_POD_COUNT; pod++) {
                g_snprintf(id, sizeof(id), "pod%c", get_pod_name_from_id(pod));
@@ -145,80 +182,143 @@ static int init(struct sr_input *in, GHashTable *options)
                return SR_ERR;
        }
 
-       inc->out_buf = g_string_sized_new(OUTBUF_FLUSH_SIZE);
+       inc->out_buf = g_string_sized_new(CHUNK_SIZE);
 
        return SR_OK;
 }
 
-static int format_match(GHashTable *metadata)
+static int format_match(GHashTable *metadata, unsigned int *confidence)
 {
        GString *buf;
+       int rc;
 
        buf = g_hash_table_lookup(metadata, GINT_TO_POINTER(SR_INPUT_META_HEADER));
+       rc = process_header(buf, NULL);
+
+       if (rc != SR_OK)
+               return rc;
+       *confidence = 10;
 
-       return process_header(buf, NULL);
+       return SR_OK;
 }
 
 static int process_header(GString *buf, struct context *inc)
 {
        char *format_name, *format_name_sig;
-       int i, record_size, device_id;
+       char *p;
+       int has_trace32;
+       size_t record_size;
+       enum ad_device device_id;
+       enum ad_format format;
 
        /*
-        * 00-31 (0x00-1F) file format name
-        * 32-39 (0x20-27) trigger timestamp       u64
-        * 40-47 (0x28-2F) unused
-        * 48    (0x30)    compression
-        * 49-53 (0x31-35) ??
-        *       50 (0x32) 0x00 (PI), 0x01 (iprobe)
-        * 54    (0x36)    0x08 (PI 250/500), 0x0A (iprobe 250)
-        * 55    (0x37)    0x00 (250), 0x01 (500)
-        * 56    (0x38)    record size             u8
-        * 57-59 (0x39-3B) const 0x00
-        * 60-63 (0x3C-3F) number of records       u32
-        * 64-67 (0x40-43) id of last record       s32
-        * 68-77 (0x44-4D) ??
-        *       71 (0x47) const 0x80=128
-        *       72 (0x48) const 0x01
-        * 78-79 (0x4E-4F) ??
+        * First-level file header:
+        * 0x00-1F  file format name
+        * 0x20 u64 trigger timestamp
+        * 0x28-2F  unused
+        * 0x30 u8  compression
+        * 0x31-35 ??
+        *  0x32 u8 0x00 (PI), 0x01 (iprobe)
+        * 0x36 u8  device id: 0x08 (PI 250/500), 0x0A (iprobe 250)
         */
 
-       /* Note: inc is off-limits until we check whether it's a valid pointer. */
+       /*
+        * Second-level file header, version 1:
+        * 0x37 u8  capture speed: 0x00 (250), 0x01 (500)
+        * 0x38 u8  record size
+        * 0x39-3B  const 0x00
+        * 0x3C u32 number of records
+        * 0x40 s32 id of last record
+        * 0x44-4D ??
+        *  0x47 u8 const 0x80=128
+        *  0x48 u8 const 0x01
+        * 0x4E-4F ??
+        */
 
-       format_name = g_strndup(buf->str, 32);
+       /*
+        * Second-level file header, version 2:
+        * 0x37 u8  ??
+        * 0x38 u64 ??
+        * 0x40 u64 ??
+        * 0x48 u8  record size
+        * 0x49-4F  ??
+        * 0x50 u64 ??
+        * 0x58 u64 number of records
+        * 0x60 u64 ??
+        * 0x68 u64 ??
+        * 0x70 u64 ??
+        * 0x78 u64 ??
+        * 0x80 u64 ??
+        * 0x88 u64 ?? (timestamp of some kind?)
+        * 0x90 u64 ??
+        * 0x98-9E  ??
+        * 0x9F u8 capture speed: 0x00 (250), 0x01 (500)
+        * 0xA0 u64 ??
+        * 0xA8 u64 ??
+        * 0xB0 u64 ??
+        * 0xB8-CF  version string? (e.g. '93173--96069', same for all tested .ad files)
+        * 0xC8 u16 ??
+        */
 
-       /* File format name ends on 0x20/0x1A, let's remove both. */
-       for (i = 1; i < 31; i++) {
-               if (format_name[i] == 0x1A) {
-                       format_name[i - 1] = 0;
-                       format_name[i] = 0;
-               }
-       }
-       g_strchomp(format_name); /* This is for additional padding spaces. */
+       /*
+        * Note: The routine is called from different contexts. Either
+        * to auto-detect the file format (format_match(), 'inc' is NULL),
+        * or to process the data during acquisition (receive(), 'inc'
+        * is a valid pointer). This header parse routine shall gracefully
+        * deal with unexpected or incorrect input data.
+        */
 
-       format_name_sig = g_strndup(format_name, 5);
+       /*
+        * Get up to the first 32 bytes of the file content. File format
+        * names end on SPACE or CTRL-Z (or NUL). Trim trailing SPACE
+        * before further processing.
+        */
+       format_name = g_strndup(buf->str, 32);
+       p = strchr(format_name, CTRLZ);
+       if (p)
+               *p = '\0';
+       g_strchomp(format_name);
 
-       /* Desired file formats either start with digit+space or "trace32". */
-       if (g_strcmp0(format_name_sig, "trace32")) {
-               if (inc)
-                       inc->format = AD_FORMAT_BINHDR;
-       } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == 0x20)) {
-               if (inc)
-                       inc->format = AD_FORMAT_TXTHDR;
-               g_free(format_name_sig);
+       /*
+        * File format names either start with the "trace32" literal,
+        * or with a digit and SPACE.
+        */
+       format_name_sig = g_strndup(format_name, strlen(TRACE32));
+       has_trace32 = g_strcmp0(format_name_sig, TRACE32) == 0;
+       g_free(format_name_sig);
+
+       format = AD_FORMAT_UNKNOWN;
+       if (has_trace32) {
+               /* Literal "trace32" leader, binary header follows. */
+               format = AD_FORMAT_BINHDR1;
+       } else if (g_ascii_isdigit(format_name[0]) && (format_name[1] == SPACE)) {
+               /* Digit and SPACE leader, currently unsupported text header. */
+               format = AD_FORMAT_TXTHDR;
                g_free(format_name);
-               sr_err("This format isn't implemented yet, aborting.");
+               if (inc)
+                       sr_err("This format isn't implemented yet, aborting.");
                return SR_ERR;
        } else {
-               g_free(format_name_sig);
+               /* Unknown kind of format name. Unsupported. */
                g_free(format_name);
-               sr_err("Don't know this file format, aborting.");
+               if (inc)
+                       sr_err("Don't know this file format, aborting.");
                return SR_ERR;
        }
+       if (!format)
+               return SR_ERR;
+
+       /* If the device id is 0x00, we have a v2 format file. */
+       if (R8(buf->str + 0x36) == 0x00)
+               format = AD_FORMAT_BINHDR2;
 
-       sr_dbg("File says it's \"%s\"", format_name);
+       p = printable_name(format_name);
+       if (inc)
+               sr_dbg("File says it's \"%s\" -> format type %u.", p, format);
+       g_free(p);
 
-       record_size = R8(buf->str + 56);
+       record_size = (format == AD_FORMAT_BINHDR1) ?
+               R8(buf->str + 0x38) : R8(buf->str + 0x48);
        device_id = 0;
 
        if (g_strcmp0(format_name, "trace32 power integrator data") == 0) {
@@ -230,27 +330,35 @@ static int process_header(GString *buf, struct context *inc)
        }
 
        if (!device_id) {
-               g_free(format_name_sig);
                g_free(format_name);
-               sr_err("Don't know how to handle this file with record size %d.",
-                       record_size);
+               if (inc)
+                       sr_err("Cannot handle file with record size %zu.",
+                               record_size);
                return SR_ERR;
        }
 
-       g_free(format_name_sig);
        g_free(format_name);
 
        /* Stop processing the header if we just want to identify the file. */
        if (!inc)
                return SR_OK;
 
+       inc->format       = format;
        inc->device       = device_id;
-       inc->trigger_timestamp = RL64(buf->str + 32);
-       inc->compression  = R8(buf->str + 48);        /* Maps to the enum. */
-       inc->record_mode  = R8(buf->str + 55);        /* Maps to the enum. */
+       inc->trigger_timestamp = RL64(buf->str + 0x20);
+       inc->compression  = R8(buf->str + 0x30); /* Maps to the enum. */
+       inc->header_size  = (format == AD_FORMAT_BINHDR1) ? 0x50 : 0xCA;
        inc->record_size  = record_size;
-       inc->record_count = RL32(buf->str + 60);
-       inc->last_record  = RL32S(buf->str + 64);
+
+       if (format == AD_FORMAT_BINHDR1) {
+               inc->record_mode  = R8(buf->str + 0x37); /* Maps to the enum. */
+               inc->record_count = RL32(buf->str + 0x3C);
+               inc->last_record  = RL32S(buf->str + 0x40);
+       } else {
+               inc->record_mode  = R8(buf->str + 0x9F); /* Maps to the enum. */
+               inc->record_count = RL32(buf->str + 0x58);
+               inc->last_record  = inc->record_count;
+       }
 
        sr_dbg("Trigger occured at %lf s.",
                inc->trigger_timestamp * TIMESTAMP_RESOLUTION);
@@ -259,7 +367,7 @@ static int process_header(GString *buf, struct context *inc)
                inc->last_record);
 
        /* Check if we can work with this compression. */
-       if (inc->compression != AD_COMPR_NONE) {
+       if (inc->compression) {
                sr_err("File uses unsupported compression (0x%02X), can't continue.",
                        inc->compression);
                return SR_ERR;
@@ -284,13 +392,13 @@ static void create_channels(struct sr_input *in)
                        continue;
 
                for (channel = 0; channel < 16; channel++) {
-                       snprintf(name, 8, "%c%d", get_pod_name_from_id(pod), channel);
+                       snprintf(name, sizeof(name), "%c%d", get_pod_name_from_id(pod), channel);
                        inc->channels[pod][channel] =
                                sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
                        chan_id++;
                }
 
-               snprintf(name, 8, "CLK%c", get_pod_name_from_id(pod));
+               snprintf(name, sizeof(name), "CLK%c", get_pod_name_from_id(pod));
                inc->channels[pod][16] =
                        sr_channel_new(in->sdi, chan_id, SR_CHANNEL_LOGIC, TRUE, name);
                chan_id++;
@@ -299,20 +407,11 @@ static void create_channels(struct sr_input *in)
 
 static void send_metadata(struct sr_input *in)
 {
-       struct sr_datafeed_packet packet;
-       struct sr_datafeed_meta meta;
-       struct sr_config *src;
        struct context *inc;
 
-       packet.type = SR_DF_META;
-       packet.payload = &meta;
-       src = sr_config_new(SR_CONF_SAMPLERATE, g_variant_new_uint64(SAMPLING_FREQ));
-       meta.config = g_slist_append(NULL, src);
-       sr_session_send(in->sdi, &packet);
-       g_slist_free(meta.config);
-       sr_config_free(src);
-
        inc = in->priv;
+       (void)sr_session_send_meta(in->sdi, SR_CONF_SAMPLERATE,
+               g_variant_new_uint64(inc->samplerate));
        inc->meta_sent = TRUE;
 }
 
@@ -338,7 +437,6 @@ static void flush_output_buffer(struct sr_input *in)
 
 static void process_record_pi(struct sr_input *in, gsize start)
 {
-       struct sr_datafeed_packet packet;
        struct context *inc;
        uint64_t timestamp, next_timestamp;
        uint32_t pod_data;
@@ -351,36 +449,36 @@ static void process_record_pi(struct sr_input *in, gsize start)
        buf = in->buf;
 
        /*
-        * 00-07 timestamp
-        * 08-09 A15..0
-        * 10-11 B15..0
-        * 12-13 C15..0
-        * 14-15 D15..0
-        * 16-17 E15..0
-        * 18-19 F15..0
-        * 20-23 ??
-        * 24-25 J15..0                         Not present in 500MHz mode
-        * 26-27 K15..0                         Not present in 500MHz mode
-        * 28-29 L15..0                         Not present in 500MHz mode
-        * 30-31 M15..0                         Not present in 500MHz mode
-        * 32-33 N15..0                         Not present in 500MHz mode
-        * 34-35 O15..0                         Not present in 500MHz mode
-        * 36-39 ??                             Not present in 500MHz mode
-        * 40/24 CLKF..A (32=CLKF, .., 1=CLKA)
-        * 41    CLKO..J (32=CLKO, .., 1=CLKJ)  Not present in 500MHz mode
-        * 42/25    ??
-        * 43/26    ??
-        * 44/27    ??
+        * 0x00 u8  timestamp
+        * 0x08 u16 A15..0
+        * 0x0A u16 B15..0
+        * 0x0C u16 C15..0
+        * 0x0E u16 D15..0
+        * 0x10 u16 E15..0
+        * 0x12 u16 F15..0
+        * 0x14 u32 ??
+        * 0x18 u16 J15..0                          Not present in 500MHz mode
+        * 0x1A u16 K15..0                          Not present in 500MHz mode
+        * 0x1C u16 L15..0                          Not present in 500MHz mode
+        * 0x1E u16 M15..0                          Not present in 500MHz mode
+        * 0x20 u16 N15..0                          Not present in 500MHz mode
+        * 0x22 u16 O15..0                          Not present in 500MHz mode
+        * 0x24 u32 ??                              Not present in 500MHz mode
+        * 0x28/18 u8 CLKF..A (32=CLKF, .., 1=CLKA)
+        * 0x29/1A u8 CLKO..J (32=CLKO, .., 1=CLKJ) Not present in 500MHz mode
+        * 0x2A/19 u8 ??
+        * 0x2B/1A u8 ??
+        * 0x2C/1B u8 ??
         */
 
        timestamp = RL64(buf->str + start);
 
        if (inc->record_mode == AD_MODE_500MHZ) {
                pod_count = 6;
-               clk_offset = 24;
+               clk_offset = 0x18;
        } else {
                pod_count = 12;
-               clk_offset = 40;
+               clk_offset = 0x28;
        }
 
        payload_bit = 0;
@@ -393,54 +491,55 @@ static void process_record_pi(struct sr_input *in, gsize start)
 
                switch (pod) {
                case 0: /* A */
-                       pod_data = RL16(buf->str + start + 8);
+                       pod_data = RL16(buf->str + start + 0x08);
                        pod_data |= (RL16(buf->str + start + clk_offset) & 1) << 16;
                        break;
                case 1: /* B */
-                       pod_data = RL16(buf->str + start + 10);
+                       pod_data = RL16(buf->str + start + 0x0A);
                        pod_data |= (RL16(buf->str + start + clk_offset) & 2) << 15;
                        break;
                case 2: /* C */
-                       pod_data = RL16(buf->str + start + 12);
+                       pod_data = RL16(buf->str + start + 0x0C);
                        pod_data |= (RL16(buf->str + start + clk_offset) & 4) << 14;
                        break;
                case 3: /* D */
-                       pod_data = RL16(buf->str + start + 14);
+                       pod_data = RL16(buf->str + start + 0x0E);
                        pod_data |= (RL16(buf->str + start + clk_offset) & 8) << 13;
                        break;
                case 4: /* E */
-                       pod_data = RL16(buf->str + start + 16);
+                       pod_data = RL16(buf->str + start + 0x10);
                        pod_data |= (RL16(buf->str + start + clk_offset) & 16) << 12;
                        break;
                case 5: /* F */
-                       pod_data = RL16(buf->str + start + 18);
+                       pod_data = RL16(buf->str + start + 0x12);
                        pod_data |= (RL16(buf->str + start + clk_offset) & 32) << 11;
                        break;
                case 6: /* J */
-                       pod_data = RL16(buf->str + start + 24);
-                       pod_data |= (RL16(buf->str + start + 41) & 1) << 16;
+                       pod_data = RL16(buf->str + start + 0x18);
+                       pod_data |= (RL16(buf->str + start + 0x29) & 1) << 16;
                        break;
                case 7: /* K */
-                       pod_data = RL16(buf->str + start + 26);
-                       pod_data |= (RL16(buf->str + start + 41) & 2) << 15;
+                       pod_data = RL16(buf->str + start + 0x1A);
+                       pod_data |= (RL16(buf->str + start + 0x29) & 2) << 15;
                        break;
                case 8: /* L */
-                       pod_data = RL16(buf->str + start + 28);
-                       pod_data |= (RL16(buf->str + start + 41) & 4) << 14;
+                       pod_data = RL16(buf->str + start + 0x1C);
+                       pod_data |= (RL16(buf->str + start + 0x29) & 4) << 14;
                        break;
                case 9: /* M */
-                       pod_data = RL16(buf->str + start + 30);
-                       pod_data |= (RL16(buf->str + start + 41) & 8) << 13;
+                       pod_data = RL16(buf->str + start + 0x1E);
+                       pod_data |= (RL16(buf->str + start + 0x29) & 8) << 13;
                        break;
                case 10: /* N */
-                       pod_data = RL16(buf->str + start + 32);
-                       pod_data |= (RL16(buf->str + start + 41) & 16) << 12;
+                       pod_data = RL16(buf->str + start + 0x20);
+                       pod_data |= (RL16(buf->str + start + 0x29) & 16) << 12;
                        break;
                case 11: /* O */
-                       pod_data = RL16(buf->str + start + 34);
-                       pod_data |= (RL16(buf->str + start + 41) & 32) << 11;
+                       pod_data = RL16(buf->str + start + 0x22);
+                       pod_data |= (RL16(buf->str + start + 0x29) & 32) << 11;
                        break;
                default:
+                       pod_data = 0;
                        sr_err("Don't know how to obtain data for pod %d.", pod);
                }
 
@@ -470,10 +569,7 @@ static void process_record_pi(struct sr_input *in, gsize start)
        if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
                sr_dbg("Trigger @%lf s, record #%d.",
                        timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
-
-               packet.type = SR_DF_TRIGGER;
-               packet.payload = NULL;
-               sr_session_send(in->sdi, &packet);
+               std_session_send_df_trigger(in->sdi);
                inc->trigger_sent = TRUE;
        }
 
@@ -484,7 +580,7 @@ static void process_record_pi(struct sr_input *in, gsize start)
        } else {
                /* It's not, so fill the time gap by sending lots of data. */
                next_timestamp = RL64(buf->str + start + inc->record_size);
-               packet_count = (int)(next_timestamp - timestamp) / TIMESTAMP_SCALE;
+               packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
 
                /* Make sure we send at least one data set. */
                if (packet_count == 0)
@@ -494,13 +590,12 @@ static void process_record_pi(struct sr_input *in, gsize start)
                        g_string_append_len(inc->out_buf, single_payload, payload_len);
        }
 
-       if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
+       if (inc->out_buf->len >= CHUNK_SIZE)
                flush_output_buffer(in);
 }
 
 static void process_record_iprobe(struct sr_input *in, gsize start)
 {
-       struct sr_datafeed_packet packet;
        struct context *inc;
        uint64_t timestamp, next_timestamp;
        char single_payload[3];
@@ -509,24 +604,21 @@ static void process_record_iprobe(struct sr_input *in, gsize start)
        inc = in->priv;
 
        /*
-        * 00-07 timestamp
-        * 08-09 IP15..0
-        * 10    CLK
+        * 0x00 u64 timestamp
+        * 0x08 u16 IP15..0
+        * 0x0A u8  CLK
         */
 
        timestamp = RL64(in->buf->str + start);
-       single_payload[0] = R8(in->buf->str + start + 8);
-       single_payload[1] = R8(in->buf->str + start + 9);
-       single_payload[2] = R8(in->buf->str + start + 10) & 1;
+       single_payload[0] = R8(in->buf->str + start + 0x08);
+       single_payload[1] = R8(in->buf->str + start + 0x09);
+       single_payload[2] = R8(in->buf->str + start + 0x0A) & 1;
        payload_len = 3;
 
        if (timestamp == inc->trigger_timestamp && !inc->trigger_sent) {
                sr_dbg("Trigger @%lf s, record #%d.",
                        timestamp * TIMESTAMP_RESOLUTION, inc->cur_record);
-
-               packet.type = SR_DF_TRIGGER;
-               packet.payload = NULL;
-               sr_session_send(in->sdi, &packet);
+               std_session_send_df_trigger(in->sdi);
                inc->trigger_sent = TRUE;
        }
 
@@ -537,7 +629,7 @@ static void process_record_iprobe(struct sr_input *in, gsize start)
        } else {
                /* It's not, so fill the time gap by sending lots of data. */
                next_timestamp = RL64(in->buf->str + start + inc->record_size);
-               packet_count = (int)(next_timestamp - timestamp) / TIMESTAMP_SCALE;
+               packet_count = (int)(next_timestamp - timestamp) / inc->timestamp_scale;
 
                /* Make sure we send at least one data set. */
                if (packet_count == 0)
@@ -547,7 +639,7 @@ static void process_record_iprobe(struct sr_input *in, gsize start)
                        g_string_append_len(inc->out_buf, single_payload, payload_len);
        }
 
-       if (inc->out_buf->len >= OUTBUF_FLUSH_SIZE)
+       if (inc->out_buf->len >= CHUNK_SIZE)
                flush_output_buffer(in);
 }
 
@@ -669,13 +761,13 @@ static int process_buffer(struct sr_input *in)
 
        if (!inc->header_read) {
                res = process_header(in->buf, inc);
-               g_string_erase(in->buf, 0, HEADER_SIZE);
+               g_string_erase(in->buf, 0, inc->header_size);
                if (res != SR_OK)
                        return res;
        }
 
        if (!inc->meta_sent) {
-               std_session_send_df_header(in->sdi, LOG_PREFIX);
+               std_session_send_df_header(in->sdi);
                send_metadata(in);
        }
 
@@ -743,11 +835,26 @@ static int end(struct sr_input *in)
        flush_output_buffer(in);
 
        if (inc->meta_sent)
-               std_session_send_df_end(in->sdi, LOG_PREFIX);
+               std_session_send_df_end(in->sdi);
 
        return ret;
 }
 
+static int reset(struct sr_input *in)
+{
+       struct context *inc = in->priv;
+
+       inc->meta_sent = FALSE;
+       inc->header_read = FALSE;
+       inc->records_read = FALSE;
+       inc->trigger_sent = FALSE;
+       inc->cur_record = 0;
+
+       g_string_truncate(in->buf, 0);
+
+       return SR_OK;
+}
+
 static struct sr_option options[] = {
        { "podA", "Import pod A / iprobe",
                "Create channels and data for pod A / iprobe", NULL, NULL },
@@ -763,6 +870,9 @@ static struct sr_option options[] = {
        { "podM", "Import pod M", "Create channels and data for pod M", NULL, NULL },
        { "podN", "Import pod N", "Create channels and data for pod N", NULL, NULL },
        { "podO", "Import pod O", "Create channels and data for pod O", NULL, NULL },
+
+       { "samplerate", "Reduced sample rate (MHz)", "Reduce the original sample rate of 12.8 GHz to the specified sample rate in MHz", NULL, NULL },
+
        ALL_ZERO
 };
 
@@ -781,6 +891,7 @@ static const struct sr_option *get_options(void)
                options[9].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
                options[10].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
                options[11].def = g_variant_ref_sink(g_variant_new_boolean(FALSE));
+               options[12].def = g_variant_ref_sink(g_variant_new_uint64(DEFAULT_SAMPLERATE));
        }
 
        return options;
@@ -797,4 +908,5 @@ SR_PRIV struct sr_input_module input_trace32_ad = {
        .init = init,
        .receive = receive,
        .end = end,
+       .reset = reset,
 };