fix(icom): ask the radio which shape its scope commands take
The IC-7851 rejects "27 11 01" and wants "27 11 00 01" — the main/sub selector its two scopes need. The shape was picked from a list of CI-V addresses, so an address not on it got the wrong one and the scope stayed blank with a rejection in the log and nothing on screen. A rejection is an unambiguous answer, so a rejected 0x27 set is now re-sent in the other shape and the winner remembered. A TIMEOUT deliberately is not retried: several firmwares never ack a 0x27 set, and treating silence as a wrong guess would flip a rig that was already working.
This commit is contained in:
+4
-2
@@ -4,11 +4,13 @@
|
||||
"date": "",
|
||||
"en": [
|
||||
"Each radio carries its own MY_RIG (Settings → CAT), written on every QSO made with it — ahead of the per-band station in Operating conditions, which says what you planned to use rather than which radio is keying. Left empty, nothing changes.",
|
||||
"Worked-before matrix: a dot in the corner of a cell now means the callsign you are working has already been worked on that slot, whatever colour the entity status gave the cell. Its two colours (worked / confirmed with this callsign) are in Appearance with the other matrix colours."
|
||||
"Worked-before matrix: a dot in the corner of a cell now means the callsign you are working has already been worked on that slot, whatever colour the entity status gave the cell. Its two colours (worked / confirmed with this callsign) are in Appearance with the other matrix colours.",
|
||||
"Icom spectrum scope: the IC-7851 (and any radio that wants the main/sub selector on its scope commands) now works over CI-V — the command shape is worked out from the radio’s own answer instead of a list of models."
|
||||
],
|
||||
"fr": [
|
||||
"Chaque radio porte son propre MY_RIG (Réglages → CAT), inscrit sur chaque QSO fait avec elle — avant la station par bande des Conditions de trafic, qui dit ce qui était prévu et non quelle radio émet. Laissé vide, rien ne change.",
|
||||
"Matrice des contacts : un point dans le coin d'une case indique que l'indicatif en cours a déjà été contacté sur ce créneau, quelle que soit la couleur donnée par le statut de l'entité. Ses deux couleurs (contacté / confirmé avec cet indicatif) se règlent dans Apparence avec les autres couleurs de la matrice."
|
||||
"Matrice des contacts : un point dans le coin d'une case indique que l'indicatif en cours a déjà été contacté sur ce créneau, quelle que soit la couleur donnée par le statut de l'entité. Ses deux couleurs (contacté / confirmé avec cet indicatif) se règlent dans Apparence avec les autres couleurs de la matrice.",
|
||||
"Scope Icom : l'IC-7851 (et toute radio qui attend le sélecteur main/sub sur les commandes de scope) fonctionne maintenant en CI-V — la forme de la commande est déduite de la réponse de la radio au lieu d'une liste de modèles."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+39
-16
@@ -982,6 +982,40 @@ func (b *IcomSerial) assembleSweep(regions map[byte][]byte, total byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// execScope sends a 0x27 SET and, if the rig rejects it, sends it once more in
|
||||
// the other shape — with or without the leading main/sub selector byte — and
|
||||
// remembers which one this rig speaks.
|
||||
//
|
||||
// The shape used to be decided from the CI-V address, which meant every new
|
||||
// model was a blank scope until someone reported it: the IC-7851 (0x8E) rejects
|
||||
// "27 11 01" outright and wants "27 11 00 01", exactly as the IC-7610 does not.
|
||||
// A rejection is a cheap and unambiguous answer, so ask the rig instead of
|
||||
// keeping a list. Only the SET commands need this — the waveform parser already
|
||||
// detects the selector per frame.
|
||||
func (b *IcomSerial) execScope(what string, sub byte, args ...byte) error {
|
||||
try := func(sel bool) error {
|
||||
p := []byte{civ.CmdScope, sub}
|
||||
if sel {
|
||||
p = append(p, 0x00) // main scope
|
||||
}
|
||||
return b.exec(append(p, args...)...)
|
||||
}
|
||||
err := try(b.dualScope)
|
||||
// Only a REJECTION means "wrong shape". A timeout says nothing (several
|
||||
// firmwares simply don't ack a 0x27 set), and retrying it in the other shape
|
||||
// would flip a working rig onto the wrong one.
|
||||
if err == nil || !strings.Contains(err.Error(), "rejected") {
|
||||
return err
|
||||
}
|
||||
if err2 := try(!b.dualScope); err2 == nil {
|
||||
b.dualScope = !b.dualScope
|
||||
applog.Printf("icom scope: %s rejected — this rig wants the %s form (selector=%v)",
|
||||
what, map[bool]string{true: "27 xx 00 …", false: "27 xx …"}[b.dualScope], b.dualScope)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SetScope enables or disables the spectrum scope. Two commands are needed and
|
||||
// RS-BA1 sends both: 0x27 0x10 turns the scope DISPLAY on (without it the rig
|
||||
// streams nothing — the case when we're remote and can't touch the front panel),
|
||||
@@ -1000,14 +1034,14 @@ func (b *IcomSerial) SetScope(on bool) error {
|
||||
// radio, and closing OpsLog (SetScope(false)) blanking a local IC-7300's
|
||||
// screen is exactly the regression this avoids. Some firmwares don't ack a
|
||||
// 0x27 set; a timeout isn't fatal, so log and continue.
|
||||
if err := b.exec(civ.CmdScope, civ.SubScopeOnOff, 0x01); err != nil {
|
||||
if err := b.execScope("display on", civ.SubScopeOnOff, 0x01); err != nil {
|
||||
applog.Printf("icom scope: display on ack: %v", err)
|
||||
}
|
||||
}
|
||||
// Waveform data OUTPUT over CI-V: enabled with the scope, and — crucially —
|
||||
// the ONLY thing we switch off on disable, so the radio's own scope display is
|
||||
// left exactly as the operator had it.
|
||||
if err := b.exec(civ.CmdScope, civ.SubScopeOn, boolByte(on)); err != nil {
|
||||
if err := b.execScope("data output", civ.SubScopeOn, boolByte(on)); err != nil {
|
||||
applog.Printf("icom scope: output on=%v ack: %v", on, err)
|
||||
}
|
||||
b.scopeMu.Lock()
|
||||
@@ -1041,13 +1075,7 @@ func (b *IcomSerial) scopeReadCfg() {
|
||||
// makes the scope follow the VFO, so tuning pans the view left/right.
|
||||
func (b *IcomSerial) SetScopeMode(fixed bool) error {
|
||||
mode := boolByte(fixed) // 0 = center, 1 = fixed (verify on rig via the cfg log)
|
||||
var payload []byte
|
||||
if b.dualScope {
|
||||
payload = []byte{civ.CmdScope, civ.SubScopeMode, 0x00, mode}
|
||||
} else {
|
||||
payload = []byte{civ.CmdScope, civ.SubScopeMode, mode}
|
||||
}
|
||||
if err := b.exec(payload...); err != nil {
|
||||
if err := b.execScope("set mode", civ.SubScopeMode, mode); err != nil {
|
||||
applog.Printf("icom scope: set mode fixed=%v ack: %v", fixed, err)
|
||||
}
|
||||
b.scopeMu.Lock()
|
||||
@@ -1093,13 +1121,8 @@ func (b *IcomSerial) SetScopeEdges(low, high int64) error {
|
||||
if rangeID == 0 {
|
||||
return fmt.Errorf("icom scope: freq out of range")
|
||||
}
|
||||
if b.dualScope {
|
||||
_ = b.exec(civ.CmdScope, civ.SubScopeMode, 0x00, 0x01) // fixed mode (main)
|
||||
_ = b.exec(civ.CmdScope, civ.SubScopeEdge, 0x00, 0x01) // activate edge set 1
|
||||
} else {
|
||||
_ = b.exec(civ.CmdScope, civ.SubScopeMode, 0x01)
|
||||
_ = b.exec(civ.CmdScope, civ.SubScopeEdge, 0x01)
|
||||
}
|
||||
_ = b.execScope("fixed mode", civ.SubScopeMode, 0x01)
|
||||
_ = b.execScope("edge set 1", civ.SubScopeEdge, 0x01)
|
||||
payload := append([]byte{civ.CmdScope, civ.SubScopeFixEdge, rangeID, 0x01}, civ.FreqToBCD(low)...)
|
||||
payload = append(payload, civ.FreqToBCD(high)...)
|
||||
b.scopeMu.Lock()
|
||||
|
||||
Reference in New Issue
Block a user