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
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 ¶
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 ¶
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.
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.