Networking in C · intermediate · ~12 min

Parse an IQ-samples capture header

Read a fixed 20-byte header off the front of a software-defined-radio IQ capture file.

Overview

Bounds-check, memcmp the magic, build a u32 / u64 / u32 from byte shifts.

Why it matters

Same alignment-safe binary parse pattern as the pcap header, on a different fixture.

Lesson

Why this matters

Software-defined radio (SDR) captures store complex baseband samples in flat binary files. These are called I/Q samples — the in-phase (I) and quadrature (Q) components of a radio signal.

Every SDR tool — GNU Radio, SDR#, rtl_sdr — writes a small header in front of the samples. The header records:

  • the sample rate
  • the centre frequency
  • the sample count

We are not capturing any RF here. We only decode the header bytes.

What the header looks like (our format)

offset  size  field
0       4     magic "IQHD"
4       4     sample_rate_hz  (u32 LE)
8       8     center_freq_hz  (u64 LE)
16      4     sample_count    (u32 LE)

The header is exactly 20 bytes. All integers are little-endian (LE): the lowest-value byte comes first in the file.

Your job

Implement:

int parse_iq_header(const uint8_t *buf, size_t n, iq_hdr_t *out);

Return 0 when the magic is valid. Return -1 on any of these:

  • NULL inputs
  • n < 20 (not enough bytes)
  • bad magic

Common mistakes

  • Wrong byte order for the u64. Little-endian means the low 4 bytes come first. Reading a u64 as two u32s in the wrong order swaps the halves.
  • Casting (uint64_t *)buf. This is alignment-unsafe and may crash or misread on some platforms. Build the value from individual byte shifts instead.
  • Mis-handling the magic. It is the first 4 raw bytes (IQHD), with no terminating NUL.

What this is NOT

  • Not a demodulator. We never touch the sample data.
  • Not a radio. We do not transmit anything.

Summary

  • The header is a fixed 20 bytes: one magic check plus three integers.
  • All integers are little-endian (lowest byte first).
  • Bounds-check first, then memcmp the magic, then build each integer from byte shifts.
  • Build integers from byte shifts, not pointer casts, to stay alignment-safe.

Practice with these exercises