IRIG 106 for people who live in PX4 logs

A range recorder stamps every packet with a 48-bit counter that ticks at 10 MHz, and it records absolute time as a channel of its own. If you can put a ULog on UTC, you already know the method.

If you can put a PX4 flight log on UTC, you can read the clock in a range recording. Both files stamp data with a free-running counter, and each can tie that counter to absolute time through a separate record.

What differs is the counter’s rate, who shares it, and how the file writes the tie down. This post walks the recorder side in terms that a ULog reader already uses.

Chapter 10 records, Chapter 11 defines the packets

The Range Commanders Council publishes its telemetry standards as IRIG 106, and the release posted on its public site is 106-24R1.1 The range commanders recommend that telemetry equipment in programs that need range support conform to it.2

Chapter 10 is the digital recording standard. It began when test ranges started fielding high-rate recorders without tape, most of them solid-state.3 The chapter names on-board recorders as its basis and original justification.3

Since RCC 106-17, the packet format lives in Chapter 11. Section 10.6.1 says that data shall be formatted in accordance with Chapter 11.3 Under the data type version field, Chapter 11 notes that RCC 106-04 through 106-15 refer to Chapter 10, and 106-17 onward to Chapter 11.4

So what people call a Chapter 10 file is Chapter 11 packets inside a Chapter 10 recording. Every recording opens with a setup record packet, and a time packet follows as the first dynamic packet.3

Every packet opens with the same 24 bytes

A packet has a header, a body and a trailer, plus an optional secondary header.4 The framing is little-endian, while the recorded data keeps its native byte order.4 The header is fixed at 24 bytes in ten fields:4

  • a 2-byte sync pattern, always 0xEB25;
  • a 2-byte channel ID, unique for each data source;
  • a 4-byte packet length and a 4-byte data length;
  • one byte each for data type version, sequence number, packet flags and data type;
  • a 6-byte relative time counter;
  • a 2-byte header checksum.

The relative time counter, or RTC, is the clock. Section 11.2.1.1 defines it as a free-running 10 MHz binary counter of 48 bits, common to all data channels.4 It runs from an internal 10 MHz crystal and stays free-running for the whole recording.4

One count is 100 ns. The counter holds 248 counts, or 281,474,976,710,656, so it wraps after 28,147,498 seconds, about 326 days. Chapter 10 also caps every packet except computer-generated ones at 100 ms of data, measured on the RTC.3

The flags byte carries an RTC sync error bit.4 An optional secondary header adds a second time to a packet: Chapter 4 binary time, IEEE 1588 time or a 1 GHz extended relative counter.4

Data inside a packet carries its own stamps. Each MIL-STD-1553 message gets an 8-byte intra-packet time stamp that holds the RTC at a set bit of that message.4 If the flags select it, the stamp uses the secondary header’s time format instead.4

What rides in a recording

The format was written for mixed inputs: PCM, MIL-STD-1553, time, analog, video, ARINC 429, discrete and UART serial data.4 Chapter 11 defines data types for all of those and more:4

  • PCM in Chapter 4, 7 or 8 formats, data type 0x09;
  • MIL-STD-1553B bus data, one bus transaction per message, 0x19;
  • ARINC-429 words, timed from the packet header and the gap times between words, 0x38;
  • MPEG-2 or H.264 video, 0x42;
  • Ethernet MAC frames, 0x68, and UDP payloads, 0x69;
  • UART data from RS-232, RS-422 or RS-485 channels, 0x50;
  • CAN bus, Fibre Channel, IEEE 1394, analog, discrete, imagery and GPS NMEA-RTCM.

For a small UAS, a serial telemetry link fits the UART type. UDP traffic fits Ethernet Format 1, which takes any UDP packets.4

Time is just another channel

Chapter 10 treats time like any other data channel. With a time source in use, the recorder writes a time packet at least once a second.3 A Format 1 time packet opens with a 32-bit word that says what kind of time follows:4

RCC 106-24R1, Chapter 11Section 11.2.3.2, item a, abridgedtext
Time Format (FMT). Bits 7-4 indicate the time data packet format.
   0x0 = IRIG-B
   0x1 = IRIG-A
   0x2 = IRIG-G
   0x3 = Real-Time Clock
   0x4 = Universal Coordinated Time (UTC) time from GPS
   0x5 = Native GPS Time
...
   0xF = NONE (time packet payload invalid)
Time Source (SRC). Bits 3-0 indicate the source of the time in the payload of each
time packet.
   0x0 = Internal (time derived from a clock in the recorder)
   0x1 = External (time derived from a clock not in the recorder)
...

The source field reads external when time comes from a clock outside the recorder. If the recorder loses lock on that source, the field reads internal until lock returns.4 For IRIG formats, a second field says whether the time code generator is freewheeling or locked to IRIG, GPS, NTP, PTP or embedded time.4

The time itself follows as BCD digits, from the date down to tens of milliseconds.4 Format 2 packets carry NTP or PTP time instead.4 Chapter 11 notes that NTP time is UTC with leap seconds, while PTP time is TAI without them.4

From counter to clock time

A time packet is a pair. Its header holds the RTC value that the recorder captured, and its body holds the absolute time that goes with it.4 Every channel shares the counter, so that one pair converts any other stamp in the recording.4

For a stamp r after a time packet with count r1 and time T1, the time is T1 + (r - r1) / 10,000,000 seconds. A 1553 message stamped 4,250,000 counts after that packet happened 0.425 s after its time.

Mind the precision of the pair. For IRIG formats the capture follows IRIG 200, the serial time code standard.4 For every other format, including UTC from GPS, Chapter 11 ties the capture to the body’s 10 ms resolution.4

Interpolating between time packets assumes that the crystal ran at exactly 10 MHz. A time packet reports its source and lock state, but not the crystal’s error.4

The same idea in a ULog

PX4’s clock is hrt_absolute_time(). Despite the name, it counts microseconds from an arbitrary point shortly after boot:5

src/drivers/drv_hrt.hv1.16.0, lines 56 to 62 and 129 to 132C
/**
 * Absolute time, in microsecond units.
 *
 * Absolute time is measured from some arbitrary epoch shortly after
 * system startup.  It should never wrap or go backwards.
 */
typedef uint64_t	hrt_abstime;
...
/**
 * Get absolute time in [us] (does not wrap).
 */
__EXPORT extern hrt_abstime hrt_absolute_time(void);

The ULog header carries an 8-byte timestamp, which the spec describes as the time logging started, in microseconds.6 The logger fills it from the same boot clock:7

src/modules/logger/logger.cppv1.16.0, lines 2028 to 2039, abridgedC++
void Logger::write_header(LogType type)
{
	ulog_file_header_s header = {};
...
	header.timestamp = hrt_absolute_time();

So the header is not UTC. Message timestamps are in microseconds as well, and the tie to UTC rides inside the GPS samples:69

msg/SensorGps.msgv1.16.0, lines 3 and 53 to 54text
uint64 timestamp		# time since system start (microseconds)
...
int32 timestamp_time_relative	# timestamp + timestamp_time_relative = Time of the UTC timestamp since system start, (microseconds)
uint64 time_utc_usec		# Timestamp (microseconds, UTC), this is the timestamp which comes from the gps module. It might be unavailable right after cold start, indicated by a value of 0

The sum timestamp + timestamp_time_relative is the boot time of the UTC stamp, and time_utc_usec is the stamp itself.8 It reads 0 when the time is unavailable, for example right after a cold start.8 The conversion has the same shape as the recorder’s, in microseconds rather than counts.

Same arithmetic, different clocks

  • Rate and width: the RTC counts 100 ns steps in 48 bits and wraps after about 326 days.4 hrt_abstime counts microseconds in 64 bits and should never wrap or go backwards.5
  • Scope: one RTC serves every channel in a recording, so bus traffic, video and PCM share a clock.4 hrt_absolute_time() is the flight controller’s own clock.5
  • The anchor: a recorder writes it as a packet type of its own, with source and lock state.34 A ULog carries it inside GPS samples, and the UTC field reads 0 while the time is unavailable.8
  • Time scale: Chapter 11 lists UTC from GPS and native GPS time as separate formats.4 time_utc_usec is UTC by its definition.8

Lining up a ULog with a recording

With both conversions in hand, the two files can share one axis. The figure shows the chain, and the steps below follow it.

time packets at least once a secondRecorder RTC10 MHz, 48-bit counttime packet (r1, T1)time packet (r2, T2)1553 message (r)every header holds the RTCAbsolute timeUTCT1T1 + (r - r1) / 10 MHzT2U1U1 + (t - g1) µsU2ULog clockµs from boot, hrtsensor_gps (g1, U1)sensor_gps (g2, U2)logged sample (t)
Illustration, not to scale. A time packet pairs an RTC count r1 with an absolute time T1, and the shared counter carries any other packet to absolute time. A sensor_gps sample pairs a boot time g1, timestamp plus timestamp_time_relative, with its time_utc_usec U1.
  1. In the recording, read every time packet. Note the format and source fields, and any packet that flags an RTC sync error.
  2. Convert each packet or message stamp to absolute time from the nearest time packet.
  3. In the ULog, keep the sensor_gps samples where time_utc_usec is not 0. Add timestamp_time_relative to each timestamp.
  4. Convert the flight log’s timestamps to UTC from the nearest such sample.
  5. Confirm that both sides use the same time scale. Only then compare an event in one file with an event in the other.

What each file proves about time

Each file orders its own events on its own counter. Across the two files, you get only as far as their anchors go.

What the evidence shows

  • The RTC count of every packet, on one 10 MHz counter that all channels in the recording share.
  • The absolute time the recorder held at each time packet, and whether it came from an external source.
  • For the flight log, the boot time at which each GPS sample’s UTC stamp applies.

What it does not

  • Whether the external source itself was right. The source field says where time came from, not how good it was.
  • How the crystal ran between time packets. Interpolation assumes exactly 10 MHz.
  • A direct link between the flight log and the recording. The two files meet only at absolute time.

Sources

  1. 1106 Telemetry Standards, IRIG 106-24R1Range Commanders Council, TRMC public wiki. Accessed September 26, 2026.
  2. 2IRIG 106-24R1, cover, changes and prefaceRange Commanders Council. Accessed September 26, 2026.
  3. 3IRIG 106-24R1 Chapter 10, Digital Recording Standard, sections 10.1, 10.3 and 10.6Range Commanders Council. Accessed September 26, 2026.
  4. 4IRIG 106-24R1 Chapter 11, Recorder Data Packet Format Standard, sections 11.1 and 11.2Range Commanders Council. Accessed September 26, 2026.
  5. 5drv_hrt.h, hrt_abstime and hrt_absolute_time()PX4 Autopilot v1.16.0. Accessed September 26, 2026.
  6. 6ULog File FormatPX4 User Guide. Accessed September 26, 2026.
  7. 7logger.cpp, Logger::write_header()PX4 Autopilot v1.16.0. Accessed September 26, 2026.
  8. 8SensorGps (uORB message)PX4 User Guide. Accessed September 26, 2026.
  9. 9SensorGps.msgPX4 Autopilot v1.16.0. Accessed September 26, 2026.

More field notes

Flight testAt jet speed, clock error is distanceAt 170 m/s, a ±40 ms clock bound is ±6.8 m of track; on a quad it is ±0.48 m. Draw it along the track, and you can see when a log cannot say which side of a line an event fell on.Time and clocksBoot time, wall time, arrival time: one incident, three clocksA PX4 flight log counts from boot, the companion journal keeps wall time and the ground station logs arrival. Give each an explicit error bound, and you know which events you can put in order and which you cannot.Companion computersReading an OOM kill in a journalctl exportThe kernel’s out-of-memory report names the thread that asked, the process it killed and every page it counted. systemd then records the unit’s result, and two settings decide what happens next.

A pilot on your own data

Bring your hardest incident.

Send one failure you have already investigated. We rebuild it on your data, beside your current tools, and show where the evidence agrees with your conclusion and where it doesn’t.

  1. 1
    Send one incidentA failure you have already investigated, with the flight log and whatever companion or ground evidence you kept.
  2. 2
    We reconstruct itBeside your current tools, on your data, with every claim traced to its source.
  3. 3
    Compare the answersWhere the evidence agrees with your conclusion, where it does not, and what it cannot decide.