ccdecoder

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: May 14, 2026 License: Apache-2.0 Imports: 33 Imported by: 0

Documentation

Overview

Package ccdecoder is the connector that closes the IQ → control- channel decoder gap listed in the README "Status & known gaps".

What this package does:

  • Owns the control SDR's IQ stream (one StreamIQ loop per Decoder lifetime).
  • Subscribes to events.KindHuntProgress so it learns which system / frequency the CC Hunter supervisor is currently attempting.
  • On every HuntProgress transition, swaps the active per- protocol pipeline (IQ → symbol-domain decoder → CC state machine) via the package-local factory map keyed on trunking.Protocol.
  • Pumps every IQ chunk arriving on the StreamIQ channel through the active pipeline's Process method. The pipeline's CC state machine publishes events.KindCCLocked / events.KindGrant on the same bus, which the supervisor + engine consume to drive the rest of the daemon.

What this package does NOT do:

  • It doesn't retune the SDR — that's the CC Hunter supervisor's job (`internal/scanner/cchunt`). The Decoder follows the supervisor's lead via HuntProgress events.
  • It doesn't open / close the SDR device — the daemon does that and hands a Tuner + IQSource through Options.
  • It doesn't decode every protocol from day one. Each pipeline is gated on having a control-channel state machine that accepts a raw dibit / bit stream. Protocols whose CC state machine still consumes pre-parsed PDUs (DMR / NXDN / dPMR / EDACS / MPT 1327 / LTR / Motorola / P25 P2 / TETRA) need a Process(...) adapter on their control package first — a documented follow-up per the per-protocol receiver PRs that already shipped.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SetTestFactory

func SetTestFactory(protocol trunking.Protocol, f PipelineFactory) (restore func())

factories maps a trunking.Protocol to its pipeline factory. Only protocols whose ControlChannel state machine already accepts a raw dibit / bit stream are wired here. Others land in follow-up PRs as the per-protocol Process(...) adapters ship.

The Protocol enum currently lumps P25 Phase 1 and Phase 2 together; this factory targets Phase 1 (the more common deployment + the protocol with a complete IQ → dibits → CC → bus chain shipping today). A future PR splits Phase 1 / Phase 2 once the daemon's config grows a per-system phase selector.

DMR / NXDN / dPMR / EDACS / MPT 1327 / LTR / Motorola Type II / TETRA all have IQ → symbol receivers shipping but their ControlChannel state machines still consume pre-parsed PDUs. Adding `Process(stream, baseIdx)` adapters that buffer + detect sync + frame + dispatch into the existing parsers is a follow-up. SetTestFactory replaces the registered pipeline factory for a single protocol and returns a restore function the caller is expected to defer. INTENDED FOR INTEGRATION TESTS ONLY — the in-package unit tests substitute factories by mutating the unexported map directly. Out-of-package integration tests (e.g. cmd/gophertrunk's end-to-end "lights up live trunked reception" check) need an exported hook so they can pump known-good dibit streams through the daemon's real ccdecoder without owning a working C4FM modulator.

Production code MUST NOT call this — the factory map is initialised once at package load and the daemon assumes it stays stable for the rest of the process lifetime.

Types

type Decoder

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

Decoder is the long-lived component that converts the control SDR's IQ stream into CC / grant events on the bus. Construct via New, run via Run.

func New

func New(opts Options) (*Decoder, error)

New constructs a Decoder. Returns an error when required Options are missing.

func (*Decoder) Close

func (d *Decoder) Close() error

Close releases the active pipeline. Safe to call from outside Run; Run also runs Close on the active pipeline as part of normal swap cleanup.

func (*Decoder) Run

func (d *Decoder) Run(ctx context.Context) error

Run blocks until ctx cancels. It opens one StreamIQ loop on the control SDR, subscribes to KindHuntProgress, swaps the active per-protocol pipeline whenever the supervisor reports a new (system, frequency) under attempt, and pumps every IQ chunk through the active pipeline.

Returns ctx.Err() on shutdown; any StreamIQ error from the SDR surfaces as the return value.

type IQSource

type IQSource interface {
	StreamIQ(ctx context.Context) (<-chan []complex64, error)
}

IQSource is the subset of sdr.Device the decoder consumes for IQ samples. Matches conventional.IQSource so the same Device satisfies both interfaces.

type Options

type Options struct {
	Bus     *events.Bus
	Log     *slog.Logger
	Tuner   Tuner    // currently unused but kept for API symmetry with cchunt
	IQ      IQSource // control SDR providing the live IQ stream
	Systems []trunking.System
	// SampleRateHz is the IQ stream rate. Forwarded to the per-
	// protocol receiver factories so they can size their matched
	// filters correctly.
	SampleRateHz float64
}

Options configure a Decoder.

type PipelineFactory

type PipelineFactory func(PipelineOptions) (ProtocolPipeline, error)

PipelineFactory constructs a fresh ProtocolPipeline for one tuned system. The factory returns an error when the protocol's per-receiver / per-state-machine wiring isn't complete enough to drive a live CC pipeline end-to-end yet — the connector skips the retune in that case and the system stays in `state=hunting`.

type PipelineOptions

type PipelineOptions struct {
	Bus          *events.Bus
	Log          *slog.Logger
	SystemName   string
	FrequencyHz  uint32
	SampleRateHz float64
	System       trunking.System
}

PipelineOptions is the per-pipeline construction shape — the connector hands the bus + log down, plus the (system, frequency) the supervisor is currently attempting and the IQ sample rate the receiver needs to size its matched filter.

System carries the full trunking.System the supervisor is hunting, so per-protocol factories can read protocol-specific config off it (TETRA colour code + expected channel, P25 WACN, etc.) without needing a new field on PipelineOptions per protocol. SystemName + FrequencyHz remain at the top level because they're consumed by every factory.

type ProtocolPipeline

type ProtocolPipeline interface {
	Process(iq []complex64)
	Reset()
	Close() error
}

ProtocolPipeline is the contract every per-protocol receiver pipeline satisfies. Process consumes one chunk of complex IQ; Reset clears symbol-domain state on stream re-sync; Close releases any held resources (it's idempotent and may return nil).

type Tuner

type Tuner interface {
	SetCenterFreq(hz uint32) error
}

Tuner is the subset of sdr.Device the decoder uses for retuning. Matches the same interface cchunt + conventional consume so the daemon can hand the same Device to all three.

Jump to

Keyboard shortcuts

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