fleetsync

package
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package fleetsync decodes Kenwood FleetSync (and FleetSync II) in-band ANI signaling.

FleetSync is the analog in-band data burst Kenwood commercial two-way radios key at the start of a PTT transmission. It carries the transmitting radio's identity — a Fleet number and a Unit ID (the ANI, automatic number identification) — plus status and messaging, on otherwise-analog conventional NBFM voice channels. Scanner operators use it to see *which* radio is transmitting on a system that is otherwise just FM voice, the same role MDC1200 plays for Motorola.

On the air it is a 1200-baud FFSK burst (CCIR tones: mark = 1200 Hz, space = 1800 Hz) carried inside the narrowband-FM voice channel — the same modulation class GopherTrunk already demodulates for MDC1200 and MPT 1327, so a DSP front end can reuse internal/dsp/demod.FFSK. This package owns the protocol layer only: a stream of sliced FSK bits is framed by Framer (sync hunt → capture), and each captured frame is handed to DecodeFrame here, which validates it and returns a typed Message carrying the Fleet/Unit ANI.

Frame layout, after a ≥24-bit alternating preamble and the 16-bit sync word 0xA23E (most-significant bit first, read straight off the FSK slicer): the payload begins 4 bits into the captured stream and is two 32-bit words —

word1[31:0], word2[31:16] : data (fleet / unit / status)
word2[15:0]               : the 16-bit block check (CRC)

FleetSync I validates word2's low 16 bits against [fsyncCRC]. FleetSync II adds forward error correction: four consecutive 64-bit blocks, each 16-bit half-word carrying a single-error-correcting code ([fs2ECCRepair]); the corrected nibbles reassemble word1/word2, which are then CRC-checked the same way. The ANI is read identically from the recovered words.

The Fleet/Unit field extraction, the CRC (polynomial 0x6815, parity bit, 0x0002 final term) and the FS-II parity-check / repair tables are the FleetSync framing facts as implemented by the multimon-ng `fsync` decoder, which is proven on air; this is a clean-room Go port of that framing, cross-checked against a working Python reference contributed on issue #437. No third-party source is incorporated.

Verification status: the protocol constants below are pinned to the multimon-ng reference and exercised by reference-literal + single-bit ECC-correction tests, but this decoder has NOT yet been confirmed against a real Kenwood off-air capture. Wiring it to a live DSP front end, the events bus, storage and the REST/web surface is deliberately staged for after that on-air A/B (issue #437).

Index

Constants

View Source
const (
	// SyncWord is the 16-bit FleetSync frame synchronization word, most-
	// significant bit first, read directly off the FSK slicer.
	SyncWord uint16 = 0xA23E

	// SyncBits is the length of SyncWord in bits.
	SyncBits = 16

	// FrameBits is the number of payload bits captured after the sync
	// word. This is the FleetSync II maximum (a 4-bit lead-in plus four
	// 64-bit ECC blocks); a FleetSync I frame uses only the first
	// fs1FrameBits of them.
	FrameBits = frameOffset + 4*64 // 260

)

Variables

This section is empty.

Functions

This section is empty.

Types

type Framer

type Framer struct {
	// contains filtered or unexported fields
}

Framer turns a demodulated FleetSync bit stream into decoded Message bursts. It hunts the 24-bit alternating preamble plus 16-bit sync word, captures the FrameBits payload that follows, hands it to DecodeFrame, and invokes the OnMessage callback for each burst.

This is the protocol-side framer only: the DSP layer above it (a 1200-baud FFSK demodulator + slicer, e.g. internal/dsp/demod.FFSK) feeds it one wire bit per Push call. Keeping the framer callback-based and free of the events bus / storage lets it be unit-tested in isolation; the bus/REST/web wiring is a separate, on-air-gated step (issue #437).

Polarity: an FM discriminator can present the burst with either tone sense depending on tuning, so the sync hunt accepts both the sync word and its bitwise complement; when it locks on the complement the captured payload bits are inverted to recover the true data — the same approach internal/radio/mdc1200/receiver uses.

func NewFramer

func NewFramer(onMsg func(Message)) *Framer

NewFramer constructs a Framer. onMsg is invoked once per decoded burst (including CRC-failed bursts, with Message.CRCOK == false, so a caller can choose to surface marginal signals); it must not be nil.

func (*Framer) Push

func (f *Framer) Push(bit byte)

Push feeds one sliced wire bit through the framer. Bits outside {0, 1} are masked to their low bit.

func (*Framer) Stats

func (f *Framer) Stats() Stats

Stats returns the current counters.

type Message

type Message struct {
	Fleet  int    // transmitting radio's Fleet number
	Unit   int    // transmitting radio's Unit ID
	IsFS2  bool   // decoded via the FleetSync II ECC path
	CRCOK  bool   // the block check validated
	RawHex string // hex of the two recovered 32-bit words
	Body   string // one-line summary for logs / panel
}

Message is one decoded FleetSync ANI burst.

func DecodeFrame

func DecodeFrame(raw []byte) (Message, bool)

DecodeFrame decodes the raw FSK bits captured immediately after the 16-bit sync word (one byte per bit; only bit 0 of each is read). It tries FleetSync I first, then falls back to the FleetSync II ECC path when enough bits are present. The bool reports whether the block check validated; a failure still returns the best-effort FleetSync I words with CRCOK=false so a caller can surface marginal bursts.

type Stats

type Stats struct {
	BurstsIn      uint64 // preamble+sync locks
	BurstsBadCRC  uint64 // decoded frames that failed the block check
	BurstsEmitted uint64 // messages passed to OnMessage
}

Stats reports cumulative framer counters for metrics / debugging.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL