Repository navigation
AX25
AX.25 is the packet radio protocol of amateur radio, which is used by APRS (position reports, messages and weather data). The physical layer is Bell 202 AFSK with 1200 baud (1200 and 2200 Hz), NRZI coding and HDLC framing: frames are delimited by flags (0x7E), long runs of 1 bits are broken up by bit stuffing, and each frame ends with a frame check sequence (CRC). A frame contains a destination, a source and up to 8 digipeater addresses, followed by the information field.
Two newer frame formats add Reed-Solomon error correction, so that frames with a few wrong bits can still be received:
- FX.25: the complete HDLC frame becomes the data part of a Reed-Solomon code word, which is preceded by a 64 bit correlation tag. A receiver without FX.25 still decodes the HDLC frame in the data part, so FX.25 is backward compatible.
- IL2P (Improved Layer 2 Protocol): the addresses and the control field are compressed into a header, and header and payload are scrambled and protected with Reed-Solomon blocks, without HDLC framing and NRZI. It is shorter and more robust than FX.25, but only receivers with IL2P support decode it.
The transmitter sends the format of the mode, the receiver always decodes all three formats (like direwolf).
Advantages
- Compatible with direwolf, multimon-ng, APRS programs and hardware TNCs. Tested with direwolf in both directions, also FX.25 and IL2P.
- Each frame has a CRC: frames with errors are dropped. FX.25 and IL2P correct wrong bytes.
- Text format for APRS (
SRC>DEST,PATH:info, like direwolf) and KISS format for packet radio applications.
Disadvantages
- Fixed at 1200 baud.
- Only frames with addresses, no plain byte stream.
- Transmitting on the radio needs an amateur radio license.
- At 8000 Hz a bit has only 6.7 samples: use 22050 Hz or more, like direwolf.
- On direwolf's test with 100 frames of increasing noise, the decoder finds a few frames less than direwolf (68 against 72).
| Mode | Transmitted format | Error correction |
|---|---|---|
aprs, ax25
|
AX.25 (HDLC) | – (CRC only) |
fx25 |
FX.25 with 16 check bytes | up to 8 wrong bytes per frame |
fx25-32, fx25-64
|
FX.25 with 32 / 64 check bytes | up to 16 / 32 wrong bytes |
il2p |
IL2P with 16 parity bytes per block | up to 8 wrong bytes per block of up to 239 bytes |
FX.25 selects the smallest code word (32, 64, 128, 191 to 239 data bytes) which fits the frame, like direwolf. IL2P uses the compressed header (type 1) for frames with 2 addresses and the complete AX.25 frame as payload (type 0) for frames with digipeaters. il2p_max_fec = false selects the smaller parity of 2 to 8 bytes per block.
The data has one of two formats (AX25Config::format):
-
AX25Format::TEXT(default): one frame per line in the monitor formatSRC>DEST,DIGI1,DIGI2:info, for exampleN0CALL-9>APRS,WIDE1-1,WIDE2-1:!4903.50N/07201.75W-Hello. A digipeater which has repeated the frame is marked with a*. -
AX25Format::KISS: KISS frames (0xC0 0x00 frame 0xC0, with0xC0and0xDBescaped), the format of packet radio applications and TNCs.
I2SStream i2s;
AX25Stream aprs(i2s, RXTX_MODE);
void setup() {
AX25Config cfg = aprs.defaultConfig();
cfg.sample_rate = 22050;
...
aprs.begin(cfg);
}
void loop() {
aprs.println("N0CALL-9>APRS,WIDE1-1:!4903.50N/07201.75W-TinyModem");
aprs.flush(); // ends the transmission
delay(60000);
}echo "N0CALL>APRS:>Hello" | tinymodem --tx -f aprs.wav aprs
tinymodem --rx -f aprs.wav aprs # monitor format
tinymodem --rx --kiss -f aprs.wav aprs # KISS framesAX25Config, AX25Encoder, AX25Decoder, AX25Stream, AX25StreamRTOS, AX25Frame (conversion between frames, the text format and KISS), FX25 and IL2P (the frame formats with error correction) are the AX.25 classes. See the aprs-beacon example. On the command line, -R, -v, -M / -S (tones), -q, --kiss and --Xrxnoise are supported; --tx-carrier sends flags (IL2P: the preamble 0x55). aprs and ax25 are the same mode.
AX25Config field |
Default | |
|---|---|---|
format |
TEXT |
TEXT or KISS
|
baud_rate |
1200 | Bits per second |
mark_freq / space_freq
|
1200 / 2200 | Tones (Hz) |
volume |
1.0 | Transmit amplitude |
tx_delay_ms |
300 | Flags before the first frame (time for the transmitter to switch on) |
tx_tail_ms |
50 | Flags after the last frame |
layer2 |
AX25 |
Transmitter: AX25, FX25 or IL2P (set by the mode) |
fx25_check_bytes |
16 | Transmitter: FX.25 check bytes (16, 32 or 64) |
il2p_max_fec |
true | Transmitter: IL2P with 16 parity bytes per block (otherwise 2 - 8) |
channel |
0 | Receiver: input channel |
sample_rate, channels, bits_per_sample
|
48000, 1, 16 | Audio format |
A frame is sent as soon as its line (or KISS frame) is complete. Call flush() (or endTransmission()) after the last frame of a transmission. At the end, the decoder reports ### AX25 frames=… fcs_errors=… fx25=… il2p=… corrected=… ###: the received frames, the AX.25 frames with a wrong CRC, the frames which were received as FX.25 or IL2P and the bytes which were corrected.
The receiver measures the energy of the mark and space tones with sliding correlators over one bit (rotating phasors, so there is no sin() per sample). A digital PLL recovers the bit clock from the tone changes. The bits are NRZI decoded (no tone change = 1), the flags are detected, the stuffed bits are removed and the frame check sequence is verified. Only correct frames are provided.
In parallel, the receiver searches the FX.25 correlation tags (up to 8 wrong bits) in the NRZI decoded bits and the IL2P sync word (1 wrong bit, both polarities) in the raw bits. After a tag, the code word is collected and corrected; its data part is HDLC decoded and checked. An AX.25 frame which is found inside an FX.25 code word is held back until the code word is complete, so each frame is provided only once. After the IL2P sync word, the header is corrected and descrambled, which provides the length of the payload; the payload blocks are corrected and descrambled and the AX.25 frame is rebuilt from the header.
- direwolf (
atest) decodes all frames oftinymodem, andtinymodemdecodes all frames of direwolf'sgen_packets, with identical text. This is also true for FX.25 (16, 32 and 64 check bytes and direwolf's automatic selection) and IL2P (both FEC levels, also with inverted polarity). - 100 frames with increasing noise from
gen_packets -n 100at 44100 Hz: AX.25 68 (direwolf 72), FX.25 with 16 check bytes 75 (80), FX.25 with 64 check bytes 73 (83), IL2P 76 (88). The error correction works, but direwolf, which runs several demodulators in parallel, finds more frames. - AX.25 frames with increasing noise from
gen_packets -n 100:tinymodem68, direwolf 72 at 44100 Hz; 68 and 70 at 48000 Hz; 50 and 50 at 22050 Hz. - Noise (
--Xrxnoise) at 8000 Hz without errors: AX.25 0.3, FX.25 0.6, IL2P 0.5. - Loopback at 48000 Hz: no errors with noise 0.5 and with ±1% different clocks. At 8000 Hz: all frames with noise 0.3, but frames are lost with a clock difference of −1%.