Skip to content

Commit 00a8e75

Browse files
committed
#3: implement the Reporter interface to provide information about the current state of the strain mode
1 parent 3f89267 commit 00a8e75

File tree

8 files changed

+114
-75
lines changed

8 files changed

+114
-75
lines changed

cmd/tci.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"log"
7+
"os"
78

89
"github.com/spf13/cobra"
910

@@ -64,7 +65,9 @@ func runStrainTCI(ctx context.Context, cmd *cobra.Command, args []string) {
6465
}
6566
spotter.SetSilencePeriod(strainFlags.spotSilencePeriod)
6667

67-
process, err := tci.New(tciFlags.host, tciFlags.trx, rx.StrainMode, spotter, tciFlags.traceTCI)
68+
reporter := rx.NewTextReporter[int](os.Stdout)
69+
70+
process, err := tci.New(tciFlags.host, tciFlags.trx, rx.StrainMode, spotter, reporter, tciFlags.traceTCI)
6871
if err != nil {
6972
log.Fatal(err)
7073
}
@@ -86,7 +89,7 @@ func runStrainTCI(ctx context.Context, cmd *cobra.Command, args []string) {
8689
}
8790

8891
func runDecodeTCI(ctx context.Context, cmd *cobra.Command, args []string) {
89-
process, err := tci.New(tciFlags.host, tciFlags.trx, rx.DecodeMode, nil, tciFlags.traceTCI)
92+
process, err := tci.New(tciFlags.host, tciFlags.trx, rx.DecodeMode, nil, nil, tciFlags.traceTCI)
9093
if err != nil {
9194
log.Fatal(err)
9295
}

kiwi/kiwi.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"log"
66
"time"
77

8-
"github.com/ftl/sdrainer/dsp"
98
"github.com/ftl/sdrainer/rx"
109
"github.com/ftl/sdrainer/trace"
1110
)
@@ -42,7 +41,7 @@ func New(host string, username string, password string, centerFrequency float64,
4241
rxFrequency: centerFrequency,
4342
close: make(chan struct{}),
4443
}
45-
result.receiver = rx.NewReceiver("", rx.StrainMode, rx.WallClock, result)
44+
result.receiver = rx.NewReceiver[float32, int]("", rx.StrainMode, rx.WallClock)
4645

4746
edgeWidth := int(float32((maxBandwidth-bandwidth)/2) * (float32(blockSize) / float32(maxBandwidth)))
4847
result.receiver.SetEdgeWidth(edgeWidth)
@@ -142,9 +141,9 @@ func (p *Process) SetSignalDebounce(debounce int) {
142141
p.receiver.SetSignalDebounce(debounce)
143142
}
144143

145-
func (p *Process) ShowDecode(receiver string, peak dsp.Peak[float32, int]) {}
146-
func (p *Process) HideDecode(receiver string) {}
147-
func (p *Process) ShowSpot(receiver string, callsign string, frequency int) {
148-
log.Printf("SPOT: %s at %.2fkHz", callsign, float32(frequency)/1000)
144+
func (p *Process) ListenerActivated(listener string, frequency int) {}
145+
func (p *Process) ListenerDeactivated(listener string, frequency int) {}
146+
func (p *Process) CallsignDecoded(listener string, callsign string, frequency int, count int, weight int) {
149147
}
150-
func (p *Process) HideSpot(receiver string, callsign string) {}
148+
func (p *Process) CallsignSpotted(listener string, callsign string, frequency int) {}
149+
func (p *Process) SpotTimeout(listener string, callsign string, frequency int) {}

rx/listener.go

+18-20
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,10 @@ const (
1616
defaultAttachmentTimeout = 2 * time.Minute
1717
)
1818

19-
type ListenerIndicator[T, F dsp.Number] interface {
20-
ShowDecode(receiver string, peak dsp.Peak[T, F])
21-
HideDecode(receiver string)
22-
ShowSpot(receiver string, callsign string, frequency F)
23-
HideSpot(receiver string, callsign string)
24-
}
25-
2619
type Listener[T, F dsp.Number] struct {
27-
id string
28-
clock Clock
29-
indicator ListenerIndicator[T, F]
20+
id string
21+
clock Clock
22+
reporter Reporter[F]
3023

3124
demodulator *cw.SpectralDemodulator[T, F]
3225
textProcessor *TextProcessor
@@ -38,11 +31,11 @@ type Listener[T, F dsp.Number] struct {
3831
attachmentTimeout time.Duration
3932
}
4033

41-
func NewListener[T, F dsp.Number](id string, out io.Writer, clock Clock, indicator ListenerIndicator[T, F], sampleRate int, blockSize int) *Listener[T, F] {
34+
func NewListener[T, F dsp.Number](id string, out io.Writer, clock Clock, reporter Reporter[F], sampleRate int, blockSize int) *Listener[T, F] {
4235
result := &Listener[T, F]{
43-
id: id,
44-
clock: clock,
45-
indicator: indicator,
36+
id: id,
37+
clock: clock,
38+
reporter: reporter,
4639

4740
silenceTimeout: defaultSilenceTimeout,
4841
attachmentTimeout: defaultAttachmentTimeout,
@@ -74,14 +67,18 @@ func (l *Listener[T, F]) SetSignalDebounce(debounce int) {
7467
l.demodulator.SetSignalDebounce(debounce)
7568
}
7669

77-
func (l *Listener[T, F]) ShowSpot(callsign string) {
70+
func (l *Listener[T, F]) CallsignDecoded(callsign string, count int, weight int) {
71+
l.reporter.CallsignDecoded(l.id, callsign, l.peak.SignalFrequency, count, weight)
72+
}
73+
74+
func (l *Listener[T, F]) CallsignSpotted(callsign string) {
7875
callsign = strings.ToUpper(callsign)
79-
l.indicator.ShowSpot(l.id, callsign, l.peak.SignalFrequency)
76+
l.reporter.CallsignSpotted(l.id, callsign, l.peak.SignalFrequency)
8077
}
8178

82-
func (l *Listener[T, F]) HideSpot(callsign string) {
79+
func (l *Listener[T, F]) SpotTimeout(callsign string) {
8380
callsign = strings.ToUpper(callsign)
84-
l.indicator.HideSpot(l.id, callsign)
81+
l.reporter.SpotTimeout(l.id, callsign, l.peak.SignalFrequency)
8582
}
8683

8784
func (l *Listener[T, F]) Attach(peak *dsp.Peak[T, F]) {
@@ -91,7 +88,7 @@ func (l *Listener[T, F]) Attach(peak *dsp.Peak[T, F]) {
9188
l.demodulator.Reset()
9289
l.textProcessor.Restart()
9390

94-
l.indicator.ShowDecode(l.id, *peak)
91+
l.reporter.ListenerActivated(l.id, l.peak.SignalFrequency)
9592
// log.Printf("\ndemodulating at %v (%d - %d)\n", peak.CenterFrequency(), peak.From, peak.To)
9693
}
9794

@@ -100,10 +97,11 @@ func (l *Listener[T, F]) Attached() bool {
10097
}
10198

10299
func (l *Listener[T, F]) Detach() {
100+
frequency := l.peak.SignalFrequency
103101
l.peak = nil
104102

105103
l.textProcessor.Stop()
106-
l.indicator.HideDecode(l.id)
104+
l.reporter.ListenerDeactivated(l.id, frequency)
107105
// log.Printf("\ndemodulation stopped\n")
108106
}
109107

rx/receiver.go

+40-11
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,11 @@ const (
6161
StrainMode ReceiverMode = "strain"
6262
)
6363

64-
type ReceiverIndicator[T, F dsp.Number] interface {
65-
ListenerIndicator[T, F]
66-
}
67-
6864
type Receiver[T, F dsp.Number] struct {
6965
id string
7066
mode ReceiverMode
7167
clock Clock
72-
indicator ReceiverIndicator[T, F]
68+
reporters []Reporter[F]
7369

7470
peakThreshold T
7571
edgeWidth int
@@ -94,15 +90,14 @@ type Receiver[T, F dsp.Number] struct {
9490
tracer trace.Tracer
9591
}
9692

97-
func NewReceiver[T, F dsp.Number](id string, mode ReceiverMode, clock Clock, indicator ReceiverIndicator[T, F]) *Receiver[T, F] {
93+
func NewReceiver[T, F dsp.Number](id string, mode ReceiverMode, clock Clock) *Receiver[T, F] {
9894
if clock == nil {
9995
clock = WallClock
10096
}
10197
result := &Receiver[T, F]{
102-
clock: clock,
103-
id: id,
104-
indicator: indicator,
105-
mode: mode,
98+
id: id,
99+
mode: mode,
100+
clock: clock,
106101

107102
peakThreshold: defaultPeakThreshold,
108103
edgeWidth: defaultEdgeWidth,
@@ -125,7 +120,7 @@ func NewReceiver[T, F dsp.Number](id string, mode ReceiverMode, clock Clock, ind
125120
}
126121

127122
func (r *Receiver[T, F]) newListener(id string) *Listener[T, F] {
128-
result := NewListener[T, F](id, r.out.Channel(id), r.clock, r.indicator, r.sampleRate, r.blockSize)
123+
result := NewListener[T, F](id, r.out.Channel(id), r.clock, r, r.sampleRate, r.blockSize)
129124
result.SetAttachmentTimeout(r.attachmentTimeout)
130125
result.SetSilenceTimeout(r.silenceTimeout)
131126
result.SetTracer(r.tracer)
@@ -178,6 +173,40 @@ func (r *Receiver[T, F]) do(f func()) {
178173
}
179174
}
180175

176+
func (r *Receiver[T, F]) AddReporter(reporter Reporter[F]) {
177+
r.reporters = append(r.reporters, reporter)
178+
}
179+
180+
func (r *Receiver[T, F]) ListenerActivated(listener string, frequency F) {
181+
for _, reporter := range r.reporters {
182+
reporter.ListenerActivated(listener, frequency)
183+
}
184+
}
185+
186+
func (r *Receiver[T, F]) ListenerDeactivated(listener string, frequency F) {
187+
for _, reporter := range r.reporters {
188+
reporter.ListenerDeactivated(listener, frequency)
189+
}
190+
}
191+
192+
func (r *Receiver[T, F]) CallsignDecoded(listener string, callsign string, frequency F, count int, weight int) {
193+
for _, reporter := range r.reporters {
194+
reporter.CallsignDecoded(listener, callsign, frequency, count, weight)
195+
}
196+
}
197+
198+
func (r *Receiver[T, F]) CallsignSpotted(listener string, callsign string, frequency F) {
199+
for _, reporter := range r.reporters {
200+
reporter.CallsignSpotted(listener, callsign, frequency)
201+
}
202+
}
203+
204+
func (r *Receiver[T, F]) SpotTimeout(listener string, callsign string, frequency F) {
205+
for _, reporter := range r.reporters {
206+
reporter.SpotTimeout(listener, callsign, frequency)
207+
}
208+
}
209+
181210
func (r *Receiver[T, F]) SetTracer(tracer trace.Tracer) {
182211
r.do(func() {
183212
r.tracer = tracer

rx/rx.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,21 @@ func NewTextReporter[F dsp.Number](out io.Writer) *TextReporter[F] {
2828
}
2929

3030
func (r *TextReporter[F]) ListenerActivated(listener string, frequency F) {
31-
fmt.Fprintf(r.out, "listener %s activated on %.2fkHz\n", listener, float64(frequency))
31+
fmt.Fprintf(r.out, "listener %s activated on %.2fkHz\n", listener, float64(frequency)/1000)
3232
}
3333

3434
func (r *TextReporter[F]) ListenerDeactivated(listener string, frequency F) {
35-
fmt.Fprintf(r.out, "listener %s deactivated on %.2fkHz\n", listener, float64(frequency))
35+
fmt.Fprintf(r.out, "listener %s deactivated on %.2fkHz\n", listener, float64(frequency)/1000)
3636
}
3737

3838
func (r *TextReporter[F]) CallsignDecoded(listener string, callsign string, frequency F, count int, weight int) {
39-
fmt.Fprintf(r.out, "callsign %s decoded on %.2fkHz, heard %d times, weight is %d\n", listener, float64(frequency), count, weight)
39+
fmt.Fprintf(r.out, "callsign %s heard %d times on %.2fkHz, weight is %d\n", callsign, count, float64(frequency)/1000, weight)
4040
}
4141

4242
func (r *TextReporter[F]) CallsignSpotted(listener string, callsign string, frequency F) {
43-
fmt.Fprintf(r.out, "callsign %s spotted on %.2fkHz\n", listener, float64(frequency))
43+
fmt.Fprintf(r.out, "callsign %s spotted on %.2fkHz\n", callsign, float64(frequency)/1000)
4444
}
4545

4646
func (r *TextReporter[F]) SpotTimeout(listener string, callsign string, frequency F) {
47-
fmt.Fprintf(r.out, "spot of %s on %.2fkHz timed out\n", listener, float64(frequency))
47+
fmt.Fprintf(r.out, "spot of %s on %.2fkHz timed out\n", callsign, float64(frequency)/1000)
4848
}

rx/text_processor.go

+20-17
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,20 @@ var (
2424
callsignExp = regexp.MustCompile(`\s(?:([a-z0-9]+)/)?(([a-z]|[a-z][a-z]|[0-9][a-z]|[0-9][a-z][a-z])[0-9][a-z0-9]*[a-z])(?:/([a-z0-9]+))?(?:/(p|a|m|mm|am))?`)
2525
)
2626

27-
type SpotIndicator interface {
28-
ShowSpot(callsign string)
29-
HideSpot(callsign string)
27+
type CallsignReporter interface {
28+
CallsignDecoded(callsign string, count int, weight int)
29+
CallsignSpotted(callsign string)
30+
SpotTimeout(callsign string)
3031
}
3132

32-
type SpotIndicatorFunc func(callsign string)
33+
type SpotReporterFunc func(callsign string)
3334

34-
func (f SpotIndicatorFunc) ShowSpot(callsign string) {
35+
func (f SpotReporterFunc) CallsignSpotted(callsign string) {
3536
f(callsign)
3637
}
3738

38-
func (f SpotIndicatorFunc) HideSpot(callsign string) {}
39+
func (f SpotReporterFunc) CallsignDecoded(callsign string, count int, weight int) {}
40+
func (f SpotReporterFunc) SpotTimeout(callsign string) {}
3941

4042
type dxccFinder interface {
4143
Find(string) ([]dxcc.Prefix, bool)
@@ -53,9 +55,9 @@ type collectedCallsign struct {
5355
}
5456

5557
type TextProcessor struct {
56-
out io.Writer
57-
clock Clock
58-
spotIndicator SpotIndicator
58+
out io.Writer
59+
clock Clock
60+
reporter CallsignReporter
5961

6062
lastWrite time.Time
6163
lastBestMatch callsign.Callsign
@@ -71,12 +73,12 @@ type TextProcessor struct {
7173
scpFinder scpFinder
7274
}
7375

74-
func NewTextProcessor(out io.Writer, clock Clock, spotIndicator SpotIndicator) *TextProcessor {
76+
func NewTextProcessor(out io.Writer, clock Clock, reporter CallsignReporter) *TextProcessor {
7577
result := &TextProcessor{
76-
out: out,
77-
clock: clock,
78-
spotIndicator: spotIndicator,
79-
lastWrite: clock.Now(),
78+
out: out,
79+
clock: clock,
80+
reporter: reporter,
81+
lastWrite: clock.Now(),
8082

8183
op: make(chan func(), 10),
8284
window: newTextWindow(defaultTextWindowSize),
@@ -263,16 +265,17 @@ func (p *TextProcessor) collectCallsign(candidate string) {
263265
}
264266
collected.count++
265267
p.collectedCallsigns[collected.call.String()] = collected
268+
p.reporter.CallsignDecoded(collected.call.String(), collected.count, collected.weight)
266269

267270
bestMatch := p.bestMatch()
268271
if bestMatch == callsign.NoCallsign {
269272
return
270273
}
271274

272-
if bestMatch != p.lastBestMatch {
273-
p.spotIndicator.HideSpot(p.lastBestMatch.String())
275+
if bestMatch != p.lastBestMatch && p.lastBestMatch != callsign.NoCallsign {
276+
p.reporter.SpotTimeout(p.lastBestMatch.String())
274277
}
275-
p.spotIndicator.ShowSpot(bestMatch.String())
278+
p.reporter.CallsignSpotted(bestMatch.String())
276279
p.lastBestMatch = bestMatch
277280
}
278281

rx/text_processor_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func TestTextWindow_FindNext_IncludeTail(t *testing.T) {
149149
}
150150

151151
func TestTextProcessor_CollectCallsign(t *testing.T) {
152-
p := NewTextProcessor(nil, WallClock, SpotIndicatorFunc(func(string) {}))
152+
p := NewTextProcessor(nil, WallClock, SpotReporterFunc(func(string) {}))
153153
p.Start()
154154
defer p.Stop()
155155
receivedText := "cq cq cq de dl1abc dl1abc dl1abc pse k"
@@ -163,7 +163,7 @@ func TestTextProcessor_CollectCallsign(t *testing.T) {
163163
}
164164

165165
func TestTextProcessor_WriteTimeout(t *testing.T) {
166-
p := NewTextProcessor(nil, WallClock, SpotIndicatorFunc(func(string) {}))
166+
p := NewTextProcessor(nil, WallClock, SpotReporterFunc(func(string) {}))
167167
p.Start()
168168
defer p.Stop()
169169
receivedText := "cq de dl1abc"

0 commit comments

Comments
 (0)