Skip to content

Commit 902c22e

Browse files
authored
Merge pull request #22 from ftl/parrot_indicator
Indicator for the CQ parrot
2 parents 3d54e3d + 9e7a734 commit 902c22e

File tree

22 files changed

+640
-402
lines changed

22 files changed

+640
-402
lines changed

core/app/app.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ func (c *Controller) Startup() {
183183
c.Workmode.Notify(c.Entry)
184184

185185
c.VFO = vfo.NewVFO("VFO 1", c.bandplan, c.asyncRunner)
186-
c.VFO.Notify(c.Entry)
187186
c.Entry.SetVFO(c.VFO)
188187
c.VFO.Notify(c.Bandmap)
189188
c.Bandmap.SetVFO(c.VFO)
@@ -215,10 +214,11 @@ func (c *Controller) Startup() {
215214
c.Bandmap.Notify(c.Callinfo)
216215
c.Score.Notify(c.Callinfo)
217216

218-
c.Parrot = parrot.New(c.Workmode, c.Keyer)
219-
c.Keyer.Notify(c.Parrot)
217+
c.Parrot = parrot.New(c.Workmode, c.Keyer, c.asyncRunner)
218+
c.Keyer.SetParrot(c.Parrot)
220219
c.Workmode.Notify(c.Parrot)
221220
c.Entry.Notify(c.Parrot)
221+
c.Parrot.Notify(c.Entry)
222222

223223
c.Settings.Notify(c.Entry)
224224
c.Settings.Notify(c.Workmode)

core/core.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -471,12 +471,13 @@ const (
471471
)
472472

473473
type KeyerSettings struct {
474-
WPM int `json:"wpm"`
475-
Preset string `json:"preset"`
476-
SPMacros []string `json:"sp_macros"`
477-
RunMacros []string `json:"run_macros"`
478-
SPLabels []string `json:"sp_labels"`
479-
RunLabels []string `json:"run_labels"`
474+
WPM int `json:"wpm"`
475+
Preset string `json:"preset"`
476+
SPMacros []string `json:"sp_macros"`
477+
RunMacros []string `json:"run_macros"`
478+
SPLabels []string `json:"sp_labels"`
479+
RunLabels []string `json:"run_labels"`
480+
ParrotIntervalSeconds int `json:"parrot_interval_seconds"`
480481
}
481482

482483
type KeyerPreset struct {
@@ -1207,6 +1208,10 @@ type VFOXITListener interface {
12071208
VFOXITChanged(bool, Frequency)
12081209
}
12091210

1211+
type VFOPTTListener interface {
1212+
VFOPTTChanged(bool)
1213+
}
1214+
12101215
type Service int
12111216

12121217
const (

core/entry/entry.go

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"math"
77
"strconv"
88
"strings"
9+
"time"
910

1011
"github.com/ftl/hamradio/callsign"
1112

@@ -28,6 +29,7 @@ type View interface {
2829
SetMode(text string)
2930
SetXITActive(active bool)
3031
SetXIT(active bool, offset core.Frequency)
32+
SetTXState(ptt bool, parrotActive bool, parrotTimeLeft time.Duration)
3133
SetMyExchange(int, string)
3234
SetTheirExchange(int, string)
3335

@@ -139,6 +141,10 @@ type Controller struct {
139141
editQSO core.QSO
140142
ignoreQSOSelection bool
141143
ignoreFrequencyJump bool
144+
145+
ptt bool
146+
parrotActive bool
147+
parrotTimeLeft time.Duration
142148
}
143149

144150
func (c *Controller) Notify(listener any) {
@@ -541,6 +547,25 @@ func (c *Controller) XITActiveChanged(active bool) {
541547
c.view.SetXITActive(active)
542548
}
543549

550+
func (c *Controller) VFOPTTChanged(active bool) {
551+
c.ptt = active
552+
c.updateTXState()
553+
}
554+
555+
func (c *Controller) ParrotActive(active bool) {
556+
c.parrotActive = active
557+
c.updateTXState()
558+
}
559+
560+
func (c *Controller) ParrotTimeLeft(timeLeft time.Duration) {
561+
c.parrotTimeLeft = timeLeft
562+
c.updateTXState()
563+
}
564+
565+
func (c *Controller) updateTXState() {
566+
c.view.SetTXState(c.ptt, c.parrotActive, c.parrotTimeLeft)
567+
}
568+
544569
func (c *Controller) SendQuestion() {
545570
if c.keyer == nil {
546571
return
@@ -955,24 +980,25 @@ func (c *Controller) EntrySelected(entry core.BandmapEntry) {
955980

956981
type nullView struct{}
957982

958-
func (n *nullView) SetUTC(string) {}
959-
func (n *nullView) SetMyCall(string) {}
960-
func (n *nullView) SetFrequency(core.Frequency) {}
961-
func (n *nullView) SetCallsign(string) {}
962-
func (n *nullView) SetBand(text string) {}
963-
func (n *nullView) SetMode(text string) {}
964-
func (n *nullView) SetXITActive(active bool) {}
965-
func (n *nullView) SetXIT(active bool, offset core.Frequency) {}
966-
func (n *nullView) SetMyExchange(int, string) {}
967-
func (n *nullView) SetTheirExchange(int, string) {}
968-
func (n *nullView) SetMyExchangeFields([]core.ExchangeField) {}
969-
func (n *nullView) SetTheirExchangeFields([]core.ExchangeField) {}
970-
func (n *nullView) SetActiveField(core.EntryField) {}
971-
func (n *nullView) SelectText(core.EntryField, string) {}
972-
func (n *nullView) SetDuplicateMarker(bool) {}
973-
func (n *nullView) SetEditingMarker(bool) {}
974-
func (n *nullView) ShowMessage(...interface{}) {}
975-
func (n *nullView) ClearMessage() {}
983+
func (n *nullView) SetUTC(string) {}
984+
func (n *nullView) SetMyCall(string) {}
985+
func (n *nullView) SetFrequency(core.Frequency) {}
986+
func (n *nullView) SetCallsign(string) {}
987+
func (n *nullView) SetBand(text string) {}
988+
func (n *nullView) SetMode(text string) {}
989+
func (n *nullView) SetXITActive(active bool) {}
990+
func (n *nullView) SetXIT(active bool, offset core.Frequency) {}
991+
func (n *nullView) SetTXState(ptt bool, parrotActive bool, parrotTimeLeft time.Duration) {}
992+
func (n *nullView) SetMyExchange(int, string) {}
993+
func (n *nullView) SetTheirExchange(int, string) {}
994+
func (n *nullView) SetMyExchangeFields([]core.ExchangeField) {}
995+
func (n *nullView) SetTheirExchangeFields([]core.ExchangeField) {}
996+
func (n *nullView) SetActiveField(core.EntryField) {}
997+
func (n *nullView) SelectText(core.EntryField, string) {}
998+
func (n *nullView) SetDuplicateMarker(bool) {}
999+
func (n *nullView) SetEditingMarker(bool) {}
1000+
func (n *nullView) ShowMessage(...interface{}) {}
1001+
func (n *nullView) ClearMessage() {}
9761002

9771003
type nullVFO struct{}
9781004

core/hamlib/hamlib.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ type vfoSettings struct {
5252
mode core.Mode
5353
xitActive bool
5454
xitOffset core.Frequency
55+
ptt bool
5556
}
5657

5758
func (c *Client) KeepOpen() {
@@ -105,6 +106,7 @@ func (c *Client) connect(whenClosed func()) error {
105106
client.PollCommand(client.OnModeAndPassband(c.setIncomingModeAndPassband)),
106107
client.PollCommand(c.onXITActive()),
107108
client.PollCommand(c.onXITOffset()),
109+
client.PollCommand(c.onPTTActive()),
108110
)
109111

110112
c.conn.WhenClosed(func() {
@@ -217,6 +219,25 @@ func (c *Client) setIncomingXITOffset(offset int) {
217219
// log.Printf("incoming XIT offset: %v %d", c.incoming.xitActive, c.incoming.xitOffset)
218220
}
219221

222+
func (c *Client) onPTTActive() (client.ResponseHandler, string) {
223+
return client.ResponseHandlerFunc(func(r protocol.Response) {
224+
if len(r.Data) == 0 {
225+
return
226+
}
227+
active := (r.Data[0] != "0")
228+
c.setPTTActive(active)
229+
}), "get_ptt"
230+
}
231+
232+
func (c *Client) setPTTActive(active bool) {
233+
if c.incoming.ptt == active {
234+
return
235+
}
236+
c.incoming.ptt = active
237+
c.emitPTTChanged(c.incoming.ptt)
238+
// log.Printf("incoming PTT active: %v", c.incoming.ptt)
239+
}
240+
220241
func (c *Client) SetFrequency(f core.Frequency) {
221242
if f == c.outgoing.frequency {
222243
return
@@ -335,6 +356,7 @@ func (c *Client) Refresh() {
335356
c.emitModeChanged(c.incoming.mode)
336357
}
337358
c.emitXITChanged(c.incoming.xitActive, c.incoming.xitOffset)
359+
c.emitPTTChanged(c.incoming.ptt)
338360
}
339361

340362
func (c *Client) Speed(speed int) {
@@ -421,6 +443,14 @@ func (c *Client) emitXITChanged(active bool, offset core.Frequency) {
421443
}
422444
}
423445

446+
func (c *Client) emitPTTChanged(active bool) {
447+
for _, listener := range c.listeners {
448+
if xitListener, ok := listener.(core.VFOPTTListener); ok {
449+
xitListener.VFOPTTChanged(active)
450+
}
451+
}
452+
}
453+
424454
func toCoreBand(bandName bandplan.BandName) core.Band {
425455
if bandName == bandplan.BandUnknown {
426456
return core.NoBand

core/keyer/keyer.go

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strconv"
88
"strings"
99
"text/template"
10+
"time"
1011

1112
"github.com/ftl/hamradio/callsign"
1213

@@ -32,6 +33,7 @@ type SettingsView interface {
3233
SetMacro(core.Workmode, int, string)
3334
SetPresetNames([]string)
3435
SetPreset(string)
36+
SetParrotIntervalSeconds(int)
3537
}
3638

3739
// CWClient defines the interface used by the Keyer to output the CW.
@@ -53,10 +55,16 @@ type KeyerStoppedListener interface {
5355
KeyerStopped()
5456
}
5557

58+
type Parrot interface {
59+
KeyerStoppedListener
60+
SetInterval(time.Duration)
61+
}
62+
5663
// New returns a new Keyer that has no patterns or templates defined yet.
5764
func New(settings core.Settings, client CWClient, keyerSettings core.KeyerSettings, workmode core.Workmode, presets []core.KeyerPreset) *Keyer {
5865
result := &Keyer{
5966
writer: new(nullWriter),
67+
parrot: new(nullParrot),
6068
stationCallsign: settings.Station().Callsign,
6169
workmode: workmode,
6270
spLabels: make(map[int]string),
@@ -88,6 +96,7 @@ func presetNames(presets []core.KeyerPreset) []string {
8896

8997
type Keyer struct {
9098
writer Writer
99+
parrot Parrot
91100
buttonView ButtonView
92101
settingsView SettingsView
93102
client CWClient
@@ -99,18 +108,19 @@ type Keyer struct {
99108

100109
listeners []any
101110

102-
stationCallsign callsign.Callsign
103-
workmode core.Workmode
104-
wpm int
105-
spLabels map[int]string
106-
spPatterns map[int]string
107-
spTemplates map[int]*template.Template
108-
runLabels map[int]string
109-
runPatterns map[int]string
110-
runTemplates map[int]*template.Template
111-
labels *map[int]string
112-
patterns *map[int]string
113-
templates *map[int]*template.Template
111+
stationCallsign callsign.Callsign
112+
workmode core.Workmode
113+
wpm int
114+
parrotIntervalSeconds int
115+
spLabels map[int]string
116+
spPatterns map[int]string
117+
spTemplates map[int]*template.Template
118+
runLabels map[int]string
119+
runPatterns map[int]string
120+
runTemplates map[int]*template.Template
121+
labels *map[int]string
122+
patterns *map[int]string
123+
templates *map[int]*template.Template
114124
}
115125

116126
func (k *Keyer) setWorkmode(workmode core.Workmode) {
@@ -135,6 +145,15 @@ func (k *Keyer) SetWriter(writer Writer) {
135145
k.writer = writer
136146
}
137147

148+
func (k *Keyer) SetParrot(parrot Parrot) {
149+
if parrot == nil {
150+
k.parrot = new(nullParrot)
151+
return
152+
}
153+
k.parrot = parrot
154+
k.parrot.SetInterval(time.Duration(k.parrotIntervalSeconds) * time.Second)
155+
}
156+
138157
func (k *Keyer) SetSettings(settings core.KeyerSettings) {
139158
k.savedSettings = settings
140159

@@ -167,6 +186,9 @@ func (k *Keyer) SetSettings(settings core.KeyerSettings) {
167186
k.runTemplates[i], _ = template.New("").Parse(pattern)
168187
}
169188

189+
k.parrotIntervalSeconds = settings.ParrotIntervalSeconds
190+
k.parrot.SetInterval(time.Duration(k.parrotIntervalSeconds) * time.Second)
191+
170192
k.showPatterns()
171193
if k.buttonView != nil {
172194
k.buttonView.SetSpeed(k.wpm)
@@ -266,6 +288,7 @@ func (k *Keyer) showKeyerSettings() {
266288
for i, pattern := range k.runPatterns {
267289
k.settingsView.SetMacro(core.Run, i, pattern)
268290
}
291+
k.settingsView.SetParrotIntervalSeconds(k.parrotIntervalSeconds)
269292
}
270293

271294
func (k *Keyer) WorkmodeChanged(workmode core.Workmode) {
@@ -298,6 +321,7 @@ func (k *Keyer) KeyerSettings() core.KeyerSettings {
298321
func (k *Keyer) getKeyerSettings() (core.KeyerSettings, bool) {
299322
var keyer core.KeyerSettings
300323
keyer.WPM = k.wpm
324+
keyer.ParrotIntervalSeconds = k.parrotIntervalSeconds
301325
keyer.SPLabels = make([]string, len(k.spLabels))
302326
for i := range keyer.SPLabels {
303327
label, ok := k.spLabels[i]
@@ -351,12 +375,13 @@ func (k *Keyer) SelectPreset(name string) {
351375
k.settingsView.SetPreset(preset.Name)
352376

353377
settings := core.KeyerSettings{
354-
WPM: k.savedSettings.WPM,
355-
Preset: name,
356-
SPLabels: make([]string, len(preset.SPLabels)),
357-
SPMacros: make([]string, len(preset.SPMacros)),
358-
RunLabels: make([]string, len(preset.RunLabels)),
359-
RunMacros: make([]string, len(preset.RunMacros)),
378+
WPM: k.savedSettings.WPM,
379+
ParrotIntervalSeconds: k.savedSettings.ParrotIntervalSeconds,
380+
Preset: name,
381+
SPLabels: make([]string, len(preset.SPLabels)),
382+
SPMacros: make([]string, len(preset.SPMacros)),
383+
RunLabels: make([]string, len(preset.RunLabels)),
384+
RunMacros: make([]string, len(preset.RunMacros)),
360385
}
361386
copy(settings.SPLabels, preset.SPLabels)
362387
copy(settings.SPMacros, preset.SPMacros)
@@ -372,6 +397,12 @@ func (k *Keyer) EnterSpeed(speed int) {
372397
k.client.Speed(k.wpm)
373398
}
374399

400+
func (k *Keyer) EnterParrotIntervalSeconds(interval int) {
401+
log.Printf("parrot interval entered: %d", interval)
402+
k.parrotIntervalSeconds = interval
403+
k.parrot.SetInterval(time.Duration(k.parrotIntervalSeconds) * time.Second)
404+
}
405+
375406
func (k *Keyer) EnterLabel(workmode core.Workmode, index int, text string) {
376407
switch workmode {
377408
case core.SearchPounce:
@@ -514,6 +545,7 @@ func (k *Keyer) send(s string) {
514545
func (k *Keyer) Stop() {
515546
log.Println("abort sending")
516547
k.client.Abort()
548+
k.parrot.KeyerStopped()
517549
k.emitKeyerStopped()
518550
}
519551

@@ -567,7 +599,7 @@ func noValues() core.KeyerValues {
567599

568600
type nullWriter struct{}
569601

570-
func (w *nullWriter) WriteKeyer(core.KeyerSettings) error { return nil }
602+
func (*nullWriter) WriteKeyer(core.KeyerSettings) error { return nil }
571603

572604
type nullClient struct{}
573605

@@ -576,3 +608,8 @@ func (*nullClient) IsConnected() bool { return true }
576608
func (*nullClient) Speed(int) {}
577609
func (*nullClient) Send(text string) {}
578610
func (*nullClient) Abort() {}
611+
612+
type nullParrot struct{}
613+
614+
func (*nullParrot) KeyerStopped() {}
615+
func (*nullParrot) SetInterval(time.Duration) {}

0 commit comments

Comments
 (0)