feat: HAMLOG.online upload and confirmations, Yaesu antenna, a named port holder
HAMLOG.online is a seventh external service: one API key, one ADIF record per QSO, immediate / delayed / on-close like the rest. They publish no API documentation, so the protocol is read from THEIR OWN client — the HAMLOG Agent (github.com/hamlogonline/Agent), which is the authoritative source short of asking them: POST https://hamlog.online/api/agent/ {"ADIFADD": {"APIKEY": k, "ADIFDATA": record}} → {"STATUS":"OK"} {"KEYSTATUS": {"APIKEY": k}} → {"STATUS":"OK","CALLSIGN":…} Success is STATUS == OK, not "no ERROR field": their failure carries ERROR and no STATUS, and reading an unknown reply — a proxy page, a maintenance notice — as an acceptance is how a contact goes missing without anyone noticing. KEYSTATUS buys something no other service here offers: the key can be checked BEFORE the first QSO, and the answer names the account. A key pasted from another callsign is caught in the settings panel rather than through a week of silent refusals. Their confirmations are also an award source now, ticked like LoTW rather than expressed through "custom". It reads the ADIF extras, not a column: the standard names a field for hamlog.EU and none for hamlog.ONLINE, and borrowing the other site's field would write a falsehood into every exported log. Three plausible key names from their own export are accepted too, so nobody has to rename a column by hand after an export. Yaesu gains antenna selection (AN), remembered per band — the antenna picked on a band comes back with it, with no table to fill in anywhere. Rigs with one socket never answer AN and never show the row; the log says which case it is. And a serial port that is refused now names its likely holder. OmniRig stays resident once activated and keeps the port of the rig configured in it, so a native backend never gets it — "Serial port busy" alone accused nobody, and an FTDX10 spent a morning being blamed for it.
This commit is contained in:
@@ -50,11 +50,15 @@ type YaesuTXState struct {
|
||||
Squelch int `json:"squelch"` // 0-100
|
||||
AGC string `json:"agc,omitempty"`
|
||||
Preamp int `json:"preamp"` // 0=IPO, 1=AMP1, 2=AMP2
|
||||
Att int `json:"att"` // 0=off, else dB
|
||||
NB bool `json:"nb"`
|
||||
NR bool `json:"nr"`
|
||||
NRLevel int `json:"nr_level"` // 1-15
|
||||
Narrow bool `json:"narrow"` // NAR filter
|
||||
// Antenna is the selected jack, 1-3, or 0 when the rig has no AN command —
|
||||
// an FT-891 or FT-991A has a single socket and answers nothing. 0 is what
|
||||
// tells the panel to draw no selector at all rather than a dead one.
|
||||
Antenna int `json:"antenna"`
|
||||
Att int `json:"att"` // 0=off, else dB
|
||||
NB bool `json:"nb"`
|
||||
NR bool `json:"nr"`
|
||||
NRLevel int `json:"nr_level"` // 1-15
|
||||
Narrow bool `json:"narrow"` // NAR filter
|
||||
// SWR is the RATIO (1.0, 1.5…), computed from the reflection coefficient —
|
||||
// what an operator reads on the rig, not a percentage of meter travel.
|
||||
SWR float64 `json:"swr"`
|
||||
@@ -80,6 +84,7 @@ type YaesuController interface {
|
||||
SetYaesuSquelch(int) error
|
||||
SetYaesuAGC(string) error
|
||||
SetYaesuPreamp(int) error
|
||||
SetYaesuAntenna(int) error
|
||||
SetYaesuAtt(int) error
|
||||
SetYaesuNB(bool) error
|
||||
SetYaesuNR(bool) error
|
||||
@@ -239,6 +244,24 @@ func (y *Yaesu) readPanelSettings() {
|
||||
if v, ok := y.askNum("PA0;", "PA0", 1); ok {
|
||||
y.panel.Preamp = v
|
||||
}
|
||||
// ANTENNA. "AN0;" → "AN01;" — the first digit is the receiver (0 = main,
|
||||
// 1 = sub on an FTDX101), the second the jack. A rig with one socket does
|
||||
// not implement it and simply says nothing, which askNum reports as
|
||||
// not-ok — and 0 then means "no antenna switching here".
|
||||
if v, ok := y.askNum("AN0;", "AN0", 1); ok {
|
||||
if y.panel.Antenna == 0 {
|
||||
debugLog.Printf("yaesu: antenna select available (AN0; → %d)", v)
|
||||
}
|
||||
y.panel.Antenna = v
|
||||
} else {
|
||||
// Said once, at the first read: an operator who expects the ANT row and
|
||||
// does not get it should find the reason in the log rather than wonder
|
||||
// whether OpsLog forgot the feature.
|
||||
if y.panel.Antenna != -1 {
|
||||
debugLog.Printf("yaesu: no answer to AN0; — this rig has no antenna selection, the ANT row stays hidden")
|
||||
}
|
||||
y.panel.Antenna = -1
|
||||
}
|
||||
if v, ok := y.askNum("RA0;", "RA0", 1); ok {
|
||||
y.panel.Att = yaesuAttDB(v)
|
||||
}
|
||||
@@ -356,6 +379,17 @@ func (y *Yaesu) SetYaesuPreamp(n int) error {
|
||||
return y.setAndRefresh(fmt.Sprintf("PA0%d;", clampInt(n, 0, 2)))
|
||||
}
|
||||
|
||||
// SetYaesuAntenna selects an antenna jack (1-3) on the main receiver.
|
||||
//
|
||||
// Verified against the FTDX10 command set; the FTDX101 adds a third jack and a
|
||||
// sub receiver, which is the "0" in AN0 — the day someone drives a sub receiver
|
||||
// from here it becomes a parameter rather than a constant. A rig without the
|
||||
// command ignores it, and readPanelSettings then keeps Antenna at 0, so the
|
||||
// control never appears in the first place.
|
||||
func (y *Yaesu) SetYaesuAntenna(n int) error {
|
||||
return y.setAndRefresh(fmt.Sprintf("AN0%d;", clampInt(n, 1, 3)))
|
||||
}
|
||||
|
||||
func (y *Yaesu) SetYaesuAtt(db int) error {
|
||||
return y.setAndRefresh(fmt.Sprintf("RA0%d;", yaesuAttCode(db)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user