Documentation
¶
Overview ¶
Package digipeater implements a WIDEn-N / TRACEn-N APRS digipeater with preemptive digipeating and cross-channel routing driven by per-channel rules stored in the configstore.
The engine is a pure function of (rules, mycall, rx frame). It does not own the RX frame source or the TX sink; cmd/graywolf wires it in:
digi := digipeater.New(digipeater.Config{...})
// on RX:
digi.Handle(ctx, rxChannel, frame, ingress.Modem())
Handle walks the path looking for the first unconsumed (H-bit clear) entry that matches a rule and, if it finds one, submits a cloned frame with the appropriate path mutation to the txgovernor at PriorityDigipeated. The RX frame is never mutated.
Index ¶
- type Config
- type Digipeater
- func (d *Digipeater) Handle(ctx context.Context, rxChannel uint32, frame *ax25.Frame, src ingress.Source) bool
- func (d *Digipeater) SetDedupeWindow(w time.Duration)
- func (d *Digipeater) SetEnabled(on bool)
- func (d *Digipeater) SetMyCall(a ax25.Address)
- func (d *Digipeater) SetRules(rules []Rule)
- func (d *Digipeater) Stats() Stats
- type Rule
- type Stats
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// MyCall is the per-digipeater callsign override. Empty string means
// "inherit from StationCallsign". A non-empty value wins (e.g. a
// mountaintop digi running under MTNTOP-1 distinct from the operator's
// personal call). The resolved callsign is used for preemptive digi
// (if a path slot equals it, the frame is repeated regardless of
// WIDEn-N rules) and for TRACEn-N insertion.
MyCall string
// StationCallsign is the resolved station callsign fallback used when
// MyCall is empty. The wiring layer resolves this once per
// start/reload from StationConfig and hands it in; the digipeater
// package does not read configstore directly.
StationCallsign string
// DedupeWindow is the time window within which an identical frame
// will be dropped. Default 30s if zero.
DedupeWindow time.Duration
// Rules lists all digipeater rules (across all channels). The
// engine filters by FromChannel on each call. The slice may be
// replaced via SetRules for live reconfig.
Rules []Rule
// Submit is the TX sink. Required. Typically
// func(ctx, ch, f, src) error { return gov.Submit(...) }.
Submit func(ctx context.Context, channel uint32, frame *ax25.Frame, src txgovernor.SubmitSource) error
// Logger is optional.
Logger *slog.Logger
// OnPacket is an optional hook invoked after a successful digipeat,
// carrying a short human-readable note. Used by packetlog to
// annotate entries.
OnPacket func(note string, fromChan, toChan uint32, f *ax25.Frame)
// OnDedup is an optional hook invoked when a frame is dropped as a
// duplicate within the dedup window.
OnDedup func()
// ChannelModes resolves Channel.Mode at RX time. Rules whose RX
// channel is "packet" cause Handle to short-circuit; rules whose
// ToChannel is "packet" are skipped per-rule. Nil = treat every
// channel as ChannelModeAPRS (preserves the legacy any-channel-
// does-anything behavior). Lookup errors are silently ignored
// (fail-open).
ChannelModes configstore.ChannelModeLookup
}
Config configures a Digipeater.
type Digipeater ¶
type Digipeater struct {
// contains filtered or unexported fields
}
Digipeater is the engine.
func New ¶
func New(cfg Config) (*Digipeater, error)
New builds a Digipeater. Resolves cfg.MyCall (override) against cfg.StationCallsign (fallback) via callsign.Resolve. An empty/N0CALL result is not fatal at construction — the engine starts disabled and Handle short-circuits when mycall is empty, so a freshly-wired digipeater with no station callsign yet simply no-ops until a valid callsign arrives via SetMyCall + SetEnabled on reload.
func (*Digipeater) Handle ¶
func (d *Digipeater) Handle(ctx context.Context, rxChannel uint32, frame *ax25.Frame, src ingress.Source) bool
Handle evaluates frame arriving on rxChannel against the rules. If a rule matches and Action is "repeat", a cloned+mutated frame is submitted to the TX sink. The RX frame is never mutated. Returns true if the frame was digipeated.
src identifies where the frame entered graywolf. Phase 1 threads it through without affecting behavior; later phases may use it to tighten dedup or suppress self-feedback for KISS-TNC-sourced frames.
func (*Digipeater) SetDedupeWindow ¶
func (d *Digipeater) SetDedupeWindow(w time.Duration)
SetDedupeWindow updates the dedupe window. A non-positive duration is ignored to preserve a sane default. Existing entries stay in the cache and are re-evaluated against the new window on their next touch, matching the previous in-place update behavior.
func (*Digipeater) SetEnabled ¶
func (d *Digipeater) SetEnabled(on bool)
SetEnabled toggles the engine on or off for live reconfig. When disabled, Handle short-circuits and returns false without touching the dedup map or rules.
func (*Digipeater) SetMyCall ¶
func (d *Digipeater) SetMyCall(a ax25.Address)
SetMyCall updates the local callsign for preemptive digi.
func (*Digipeater) SetRules ¶
func (d *Digipeater) SetRules(rules []Rule)
SetRules replaces the rule set under the lock. Safe for live reconfig.
type Rule ¶
type Rule struct {
FromChannel uint32
ToChannel uint32
Alias string // "WIDE", "TRACE", or an exact callsign for AliasTypeExact
AliasType string // "widen"|"trace"|"exact"
MaxHops uint32
Action string // "repeat"|"drop"
Priority uint32
}
Rule is a digipeater-local form of configstore.DigipeaterRule, decoupled from GORM.
func RulesFromStore ¶
func RulesFromStore(rows []configstore.DigipeaterRule) []Rule
RulesFromStore converts configstore rows to local Rule values.