Skip to main content

Reference Notes

These notes summarize what was learned from the reference projects.

devourer

devourer is the native Realtek USB WiFi implementation. It owns the hardware bring-up that matters for OpenIPC receive:

  • firmware download,
  • EFUSE/EEPROM and power sequencing,
  • BB/RF tables,
  • monitor mode,
  • channel and bandwidth selection,
  • TX descriptors,
  • RX descriptor parsing.

The current source also covers Jaguar3 rtl8822c and rtl8822e. The latter adds chip-ID dispatch for shared USB PIDs, its own firmware/tables, V1 EFUSE, RFE/PA setup, DACK/IQK/TXGAPK, and 7-bit TXAGC behavior. The corresponding Rust path lives in openipc-rtl88xx and uses nusb instead of libusb.

The USB model is vendor-control register access plus bulk endpoints:

  • request 0x05 for register reads and writes,
  • interface 0 claim,
  • descriptor-driven bulk IN and bulk OUT endpoint discovery,
  • 32 KiB RX transfer buffers to avoid splitting full chip-side aggregates.

The Realtek RX aggregate format is shared Rust logic in openipc-core.

aviateur

aviateur is the native OpenIPC ground station. It uses devourer for adapter access, then handles WFB, RTP, adaptive-link feedback, and video playback.

Packet flow:

  1. devourer emits parsed 802.11 frames.
  2. OpenIPC/WFB frame checks validate 57:42:<channel_id> MAC fields.
  3. WFB session packets decrypt a session key.
  4. WFB data packets decrypt into FEC fragments.
  5. Primary fragments emit RTP packets.
  6. RTP packets go to playback or optional UDP output.

For the tunnel/data path, aviateur creates a native TUN interface at 10.5.0.3/24. Downlink tunnel payloads arrive from WFB on radio port 0x20, drop a two-byte big-endian length prefix, and are written to the TUN device. Uplink IP packets read from TUN receive the same length prefix and are sent over radio port 0xa0.

openipc-rs mirrors the protocol behavior in shared Rust, while keeping UI, USB permissions, and rendering at platform edges.

Aviateur uses native concurrency and UDP-style boundaries because it is a native application composed from native receiver/playback pieces. The browser station does not need to mirror that exact shape. In openipc-rs, JavaScript drives an async receive loop, Rust/WASM processes each transfer, and WebCodecs owns decode/render scheduling.

openipc-zig

openipc-zig proves that browser/WebUSB OpenIPC receive is possible, even if its implementation is not the desired long-term shape. It is useful for:

  • browser permission flow,
  • WebUSB constraints,
  • WebCodecs playback reference,
  • understanding how much hardware setup must still happen in browser builds.

openipc-rs keeps WebUSB as a transport adapter and puts the actual receiver pipeline in Rust/WASM.

PixelPilot

PixelPilot is useful as an Android reference for packaging a full ground-station experience around an H.264/H.265 WFB feed. Its Android path wraps a USB file descriptor with libusb, runs the devourer Realtek driver, and routes parsed 802.11 frames into one wfb-ng aggregator per radio port.

Observed PixelPilot channel map:

  • video: port 0x00, channel 0x7505d600, recovered RTP goes to UDP 5600,
  • telemetry downlink: port 0x10, channel 0x7505d610, recovered bytes often carry MAVLink or MSP/OSD data,
  • generic data/tunnel RX: port 0x20, channel 0x7505d620, recovered bytes go to UDP 8000 and PixelPilot's VPN service writes them into a 10.5.0.3/24 TUN interface,
  • audio RX in the wfb-ng audio profile: port 0x30, channel 0x7505d630,
  • telemetry TX in wfb-ng profiles: port 0x90, channel 0x7505d690,
  • generic tunnel/adaptive uplink TX: port 0xa0, channel 0x7505d6a0,
  • audio TX in the wfb-ng audio profile: port 0xb0, channel 0x7505d6b0.

The OpenIPC firmware package under firmware/general/package/wifibroadcast-ng matches this map. Its default wfb.yaml uses link id 7669206 (0x7505d6). The wifibroadcast service starts video with wfb_tx's default radio port 0x00, telemetry with load_wfb 144 16 ... (0x90/0x10), and tunnel/data with load_wfb 160 32 ... (0xa0/0x20). Upstream wfb-ng's master.cfg uses the same ground-station stream map.

The station UI uses these names in its route radio-port selector. It derives full channel ids from the active link id so route rows do not expose the long decimal channel numbers.

The WFB receive behavior matters for compatibility:

  • session packets are decrypted with the receiver secret key and transmitter public key from gs.key;
  • session epoch must not move backwards;
  • session channel id must match the aggregator's channel id;
  • optional encrypted session TLV tags are allowed and ignored by the receiver;
  • FEC is Reed-Solomon VDM_RS, with 1 <= k <= n < 256;
  • contiguous primary fragments are emitted immediately;
  • missing primary fragments are recovered once enough primary/parity fragments arrive;
  • whole missing blocks are skipped once later blocks prove the stream has moved on;
  • per-packet decrypt or parse failures are treated as drops, not as receive-loop failures.

openipc-rs mirrors those protocol rules in openipc-core while keeping RTP depacketization explicit. Apps can forward recovered video-port bytes as RTP or feed them into RtpDepacketizer for Annex-B output.

PixelPilot is also a useful reminder that playback is a product feature, not just a parser feature. Resolution, decoder status, render FPS, bitrate, and error counters need to be visible when debugging field behavior.

PixelPilot Parity Checklist

The Rust core and Nebulus intentionally match PixelPilot at the wire boundaries:

  • 0x08 0x01 802.11 data frames with mirrored 57:42:<channel_id> source and destination fields;
  • 24-byte 802.11 headers and trailing 4-byte FCS removed before WFB parsing;
  • one WFB session/FEC state machine per (channel_id, key slot);
  • session packets accepted only for the configured channel and non-decreasing epoch;
  • repeated session packets with the same session key ignored without resetting the FEC assembler;
  • WFB VDM Reed-Solomon FEC with the same primary/parity block semantics as wfb-ng;
  • video RTP payload types 96 for H.264 and 97 for H.265, with Divinus compatibility for its shared video payload type 96;
  • Opus RTP payload type 98, either mixed into the video route or carried by the optional audio profile route;
  • adaptive-link feedback on tunnel uplink port 0xa0, wrapped as IPv4/UDP to 10.5.0.10:9999, with the same fec_change thresholds as PixelPilot.

The scheduling is different by design. PixelPilot uses native threads and UDP handoffs between its USB/WFB layer and player. openipc-rs keeps the same bytes in memory: the Nebulus native worker or browser/WebUSB loop pushes RX transfers into ReceiverRuntime, then routes payloads and submits Annex-B frames to the platform decoder. That difference reduces IPC and copies without changing the wire behavior.