sync

package
v0.3.0 Latest Latest
Warning

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

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

Documentation

Overview

Package sync provides symbol-time recovery and frame sync correlators.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Correlator

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

Correlator searches a stream of soft symbols for a known sync pattern by running a sliding inner product. It returns the indices where the correlation magnitude exceeds threshold.

func NewCorrelator

func NewCorrelator(pattern []float32, threshold float32) *Correlator

func (*Correlator) Process

func (c *Correlator) Process(dst []int, src []float32, baseIndex int) ([]int, int)

Process scans src and appends to dst the absolute indices (relative to the start of the first call to Process) where the pattern matches above threshold. The input index for each sample increases by 1 across calls.

type Gardner

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

Gardner is a feedback symbol-timing recovery loop for complex IQ signals. It's the standard non-data-aided timing detector used in PSK / QAM demodulators where the symbol decisions aren't yet available — useful for the π/4-DQPSK family (TETRA TMO, P25 Phase 2) where the receivers currently rely on naive decimation.

Algorithm (Gardner 1986): for each symbol period the detector samples once at the symbol time t_s and once at the midpoint t_s − sps/2. The timing error is

e[n] = Re{ (s[n] − s[n−1])^* · m[n] }

where s[n] is the symbol-time sample, s[n−1] is the previous symbol-time sample, and m[n] is the midpoint sample between them. The error has zero mean at the correct sampling instant and signed deviation otherwise; a scalar update applies it to the sub-sample phase.

Compared to MuellerMuller (this package) which is real-valued and decision-directed, Gardner:

  • Works on complex samples without sign decisions, so it starts converging before the demod has acquired symbol polarity.
  • Has zero self-noise at the optimum sampling instant for RRC-filtered PSK signals, so the surviving phase estimate is stable.
  • Requires a midpoint sample, so the natural input is samples at ≥ 2 sps (4 sps is typical for noisier captures).

Inputs are oversampled complex samples (e.g. 8 sps after the matched filter). Output is one sample per recovered symbol.

func NewGardner

func NewGardner(sps, gain float64) *Gardner

NewGardner constructs a Gardner timing-recovery loop. sps is the nominal samples-per-symbol (≥ 2); gain is the loop step (a small positive value, typical range 0.01..0.1). Panics on invalid sps. A non-positive gain defaults to 0.02 (slightly slower convergence than MuellerMuller, picked for stability on noisier IQ).

func (*Gardner) Process

func (g *Gardner) Process(dst, src []complex64) []complex64

Process consumes oversampled complex IQ samples and emits one recovered symbol per nominal symbol period. dst is reused if it has capacity. Symbols are interpolated linearly between adjacent input samples at the loop's current sub-sample phase. Cross-call state preserves the timing estimate so chunked streams converge once rather than per-chunk.

func (*Gardner) Reset

func (g *Gardner) Reset()

Reset clears the loop state. Call on stream re-tune so the next chunk doesn't carry a stale timing estimate.

type MuellerMuller

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

MuellerMuller is a feedback symbol-timing recovery loop for real-valued PAM signals. The loop adjusts a sub-sample symbol clock toward the optimum sampling instant by minimizing |s[n] - sgn(s[n-1])*s[mid]|.

Inputs are oversampled samples (e.g. 8 sps after the matched filter). Output is one sample per recovered symbol.

func NewMuellerMuller

func NewMuellerMuller(sps, gain float64) *MuellerMuller

func (*MuellerMuller) Mu added in v0.2.5

func (m *MuellerMuller) Mu() float64

Mu returns the current sub-sample phase accumulator (rad-equivalent; in (-1, sps] depending on where the loop is in the symbol period). At steady state on a noise-free signal mu cycles deterministically around the symbol period; a slow monotonic drift indicates the nominal sps does not match the stream's actual sample-rate / baud ratio. Exposed read-only for issue-#402-style diagnostics where the daemon (or replay) periodically logs the loop's internal state so a persistent clock slip can be distinguished from a slicer / AFC failure. Not safe for concurrent calls with Process.

func (*MuellerMuller) Process

func (m *MuellerMuller) Process(dst []float32, src []float32) []float32

Process consumes oversampled real samples and emits one recovered symbol per nominal symbol period. dst is reused if it has capacity. The symbol clock carries across calls, so a long stream may be processed in chunks without the recovered symbol count depending on the chunk size: prevTail bridges each chunk boundary so src[0] of a continuation chunk is a real clock step rather than being skipped.

func (*MuellerMuller) SPS added in v0.2.5

func (m *MuellerMuller) SPS() float64

SPS returns the nominal samples-per-symbol the loop was constructed with. Paired with Mu() so a diagnostic log line can render mu as a fraction of the symbol period without re-deriving the construction value.

Jump to

Keyboard shortcuts

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