F06 is based on a proprietary synchronous BFSK mode. Its standard configuration is speed of 200 bd, shift of 1000 Hz, space (0) frequency at -500 Hz, and mark (1) frequency at +500 Hz. The binary fields of its protocol use big-endian bit and byte orders.

Block structure

All the data of the BFSK transmission is split and packed into consecutive ordered 288-bit blocks of the following fixed structure: (hexadecimal/binary representations)

Sync sequence Block index with ECC Interleaved payload with ECC
7d12b0e6 0033 1f2ab981ebe994c78a5900eb32bb5ded40dd1d4f7fa2f0c603a56bbf64cd
32-bit constant,
always 0x7d12b0e6
00000000001 10011 1f2ab981ebe994c78a5900eb32bb5ded40dd 1d4f7fa2f0c603a56bbf64cd
11-bit raw block index 5-bit ECC data Interleaved 144-bit raw payload with CRC Interleaved 96-bit ECC data

The Error-Correcting Code for the block index seems to be a Hamming code variant. Its basics are understood and allow detecting corrupted block indices.

The payload is composed of 4 pieces, each separately encoded with ECC and containing 36 bits of raw original data followed by 24 bits of corresponding ECC data (the ECC algorithm is not known). These 4 pieces are interleaved together (a common FEC practice), 4 bits at a time, in the following manner:

  Raw original data, 36 bits/piece ECC data, 24 bits/piece
Interleaved payload AJS1BKT2CLU3DMV4ENW5FOX6GPY7HQZ8IR09 agmsbhntcioudjpvekqwflrx
Piece 1 ABCDEFGHI abcdef
Piece 2 JKLMNOPQR ghijkl
Piece 3 STUVWXYZ0 mnopqr
Piece 4 123456789 stuvwx
Deinterleaved payload ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789  

The deinterleaved 144-bit payload then actually contains, at the end, a 16-bit CRC of the previous 128-bit data. The CRC algorithm is the standard CRC-16-CCITT. This can be used to check and reject corrupted block payloads.

Deinterleaved 144-bit payload with CRC
1be980354f9b4a02d028ec5ebeda1979 bbdd
128-bit block payload 16-bit CRC

There are two types of block, and the interpretation of the payload of a block depends on its type.

Metadata blocks

The blocks with indices divisible by 16, including zero (#0, #16, #32, #48, #64, #80, ...) are metadata blocks. They are sent at regular intervals through the broadcast and report information on the numbers of blocks and messages contained in the broadcast, and the position where, in which block, to find the start of each message. The 128-bit metadata payload is spread out over eight 16-bit groups, as follows:

  Block and message counts Position of message 1 Position of message 2 Position of message 3 Positions of messages 4, 5 and 6... Position of message 7
Hexadecimal 0862 0020 02c0 0000 ... 0000
Binary 00001000011 00010 00000000001 00000 00000010110 00000 00000000000 00000 00000000000 00000
Decimal 67 2 1 0 22 0 0 0 0 0
  11-bit block count 5-bit message count 11-bit start block index 5 bits, unused, always 0 11-bit start block index 5 bits, unused, always 0 11-bit start block index 5 bits, unused, always 0 11-bit start block index 5 bits, unused, always 0

For example, this broadcast contains 67 blocks, on which 2 messages are mapped: the first message starts with block #1, and the second message starts with block #22.

This provides for up to seven messages in a broadcast. Most of the time there is only one or maybe two messages in the broadcast, and the remaining, unused message fields are simply set to 0. A block is never shared between two messages; messages always start with the beginning of a block. In practice, the position of the first message always equals 1. The block count includes block #0.

Message blocks

Message blocks are blocks whose indices, unlike metadata blocks, are not divisible by 16. Each of them is part of one of the possibly several messages in the broadcast. A message can use one of two known encodings for the payload of its blocks:

  • Raw binary - the 128 bits or 16 bytes of the payload are simply taken as they are and put together to form a binary message, left to further interpretation. This is used for usual test transmissions, and for rare broadcasts such as F06a.
  • A custom encoding used for normal messages based on typical 5-figure groups, mapping binary to decimal digits. 10-bit pieces of the payload are mapped to 3 digits each. A 10-bit value can range from 0 to 1023 (210-1), and is mapped to a 3-digit string ranging from 000 to 999, with simplicity, efficiency and very little wasted entropy. 4-digit 10-bit values from 1000 to 1023 are believed to be invalid, and should not be encountered during decoding. The first 120 bits of the 128-bit payload are encoded through these 10-bit pieces. The remaining 8 bits at the end are divided into two 4-bit pieces. In a similar way, a 4-bit value can range from 0 to 15 (24-1), and is mapped to a single digit from 0 to 9, while values from 10 to 15 are unused.
  Bits 1-10 Bits 11-20 Bits 21-30 Bits 31-40 ... Bits 101-110 Bits 111-120 Bits 121-124 Bits 125-128
Binary 0001101111 1010010100 0000000101 0100101001 ... 1111000001 0011010110 0101 1000
Decimal digits 111 660 005 297 961 214 5 8

The decoded payload yields 12 successive 3-digit strings, and 2 extra digits at the end, for a total of 38 digits per block. These digits can be used to reconstitute 5-figure groups only after being all put together from the different blocks into the whole message, as 5-figure group boundaries are not aligned with block boundaries, and a 5-figure group is often split across two successive blocks.

Separator block

A separator block contains the normal 32-bit synchronization constant 0x7d12b0e6 followed by 256 zero bits. It is not a valid block, and trying to decode it will result in a block #0 with an incorrect payload CRC, which should be rejected as corrupted. Although its purpose may seem intuitive, it is not known which actual technical constraints, if any, it is supposed to satisfy.

Broadcast structure

The broadcasts consist of all the blocks in index order, followed by a separator block, repeated over and over for 6 to 8 minutes. Decoding a message comes down to the following steps:

  1. Decoding a metadata block to know the number of message blocks to recover and map each message with an ordered list of message block. Any metadata block whose integrity was verified will do.
  2. Decoding message blocks to obtain a copy, whose integrity was verified, of the payload of each message block. They don't have to be received or processed in order, and can be taken from any repeated portion of the broadcast; corrupted or already-received blocks can be ignored, until all blocks were decoded.
  3. For each message in the broadcast, checking the payload of its first block to determine the payload encoding of this message. (In practice, no instance has been observed of multiple messages using different payload encoding methods inside a single broadcast.)
  4. Decoding the payload of every block part of each message. Each payload must be decoded separately, block by block, not as a whole.
  5. Putting the decoded payloads back together, in block index order, to reconstitute the messages.