fix: read the FIRST digit of the Yaesu VFO reply — I inverted it yesterday
Reported both ways round, which is what settles it. With RX and TX on SUB
everything worked; with them on MAIN the frequency froze and a spot click tuned
the sub VFO. The log shows the rig answering FR01 in the MAIN case.
So on an FTDX101 the state is the FIRST digit — 0 = main — and the second is a
separate parameter. Yesterday I changed this to the LAST digit, which read FR01
as SUB and inverted the whole thing. The fault that change was meant to fix ("SUB
shows MAIN") came from reading VS instead of FR, and probing FR had already fixed
it on its own; the digit change was an unnecessary guess layered on a real fix.
Nothing else needed changing for the operator's two questions: the display and
SetFrequency already follow curVFO, so a spot now tunes whichever VFO holds RX
and TX, and the main frequency is read when on main. A test states that in those
terms — active VFO and which command a spot writes — rather than only as a byte,
because the byte is what I got wrong while believing the logic was right.
This commit is contained in:
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
"Clicking a cluster spot while listening on the SUB VFO no longer pulls the radio back to MAIN. The command used to tune sets the main VFO by definition; on SUB the sub VFO is now written directly and the VFO selection is left alone.",
|
"Clicking a cluster spot while listening on the SUB VFO no longer pulls the radio back to MAIN. The command used to tune sets the main VFO by definition; on SUB the sub VFO is now written directly and the VFO selection is left alone.",
|
||||||
"Split showed the opposite of the radio on some Yaesus. Where the rig reports which VFO transmits rather than a split flag, that has to be compared with the VFO you are listening to — so it read backwards for an operator working on SUB and correctly for one on MAIN. Setting split had the same fault.",
|
"Split showed the opposite of the radio on some Yaesus. Where the rig reports which VFO transmits rather than a split flag, that has to be compared with the VFO you are listening to — so it read backwards for an operator working on SUB and correctly for one on MAIN. Setting split had the same fault.",
|
||||||
"A shared MySQL logbook could fail to build its schema, leaving OpsLog on the local SQLite database with a message that only said the connection failed. Column declarations that are quoted, or share a line with others, escaped the SQLite-to-MySQL type conversion — so MySQL refused an index and a default value. A database already left in that state is now repaired at startup instead of failing at every launch.",
|
"A shared MySQL logbook could fail to build its schema, leaving OpsLog on the local SQLite database with a message that only said the connection failed. Column declarations that are quoted, or share a line with others, escaped the SQLite-to-MySQL type conversion — so MySQL refused an index and a default value. A database already left in that state is now repaired at startup instead of failing at every launch.",
|
||||||
"Yaesu: selecting the SUB receiver showed the MAIN frequency, and the split flag flipped between polls. An FTDX101 answers the receive-VFO query with two digits where an FTDX10 uses one, and only the first was read. Both forms are now handled.",
|
"Yaesu: OpsLog now follows the VFO you are actually on — the displayed frequency, and the VFO a cluster spot tunes. An FTDX101 answers the receive-VFO query with two digits where an FTDX10 uses one, and the wrong one was read.",
|
||||||
"Clicking a spot in DXHunter can now tune the radio: a remote-call UDP packet carrying <FREQ> and <MODE> sets the frequency and mode on whichever CAT backend is active, as well as filling the callsign. A packet without them still just fills the callsign.",
|
"Clicking a spot in DXHunter can now tune the radio: a remote-call UDP packet carrying <FREQ> and <MODE> sets the frequency and mode on whichever CAT backend is active, as well as filling the callsign. A packet without them still just fills the callsign.",
|
||||||
"The ADIF import option that fills your station fields from your profile also stamps your default confirmation statuses (paper QSL, LoTW, eQSL, Club Log, HRDLog, QRZ.com) on the ones the file leaves empty — it always did, but nothing said so, and a WSJT-X log carries almost none. Existing confirmations are never overwritten.",
|
"The ADIF import option that fills your station fields from your profile also stamps your default confirmation statuses (paper QSL, LoTW, eQSL, Club Log, HRDLog, QRZ.com) on the ones the file leaves empty — it always did, but nothing said so, and a WSJT-X log carries almost none. Existing confirmations are never overwritten.",
|
||||||
"QRZ.com confirmation download marked far too many QSOs as confirmed: it accepted the paper-QSL received flag, which you uploaded to QRZ yourself, as if it were a QRZ confirmation. Only QRZ own confirmation now counts — check the QRZ received column after your next download, as earlier ones may have set it wrongly.",
|
"QRZ.com confirmation download marked far too many QSOs as confirmed: it accepted the paper-QSL received flag, which you uploaded to QRZ yourself, as if it were a QRZ confirmation. Only QRZ own confirmation now counts — check the QRZ received column after your next download, as earlier ones may have set it wrongly.",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"Cliquer sur un spot du cluster en écoutant sur le VFO SUB ne ramène plus la radio sur le VFO principal. La commande utilisée pour s'accorder agit par définition sur le VFO principal ; sur SUB, c'est désormais le VFO secondaire qui est écrit, sans toucher à la sélection.",
|
"Cliquer sur un spot du cluster en écoutant sur le VFO SUB ne ramène plus la radio sur le VFO principal. La commande utilisée pour s'accorder agit par définition sur le VFO principal ; sur SUB, c'est désormais le VFO secondaire qui est écrit, sans toucher à la sélection.",
|
||||||
"Le split affichait l'inverse de la radio sur certains Yaesu. Quand la radio indique quel VFO émet plutôt qu'un indicateur de split, il faut le comparer au VFO écouté — l'affichage était donc inversé pour un opérateur travaillant sur SUB et correct pour un autre sur MAIN. Le réglage du split souffrait du même défaut.",
|
"Le split affichait l'inverse de la radio sur certains Yaesu. Quand la radio indique quel VFO émet plutôt qu'un indicateur de split, il faut le comparer au VFO écouté — l'affichage était donc inversé pour un opérateur travaillant sur SUB et correct pour un autre sur MAIN. Le réglage du split souffrait du même défaut.",
|
||||||
"Un carnet MySQL partagé pouvait échouer à créer son schéma, laissant OpsLog sur la base SQLite locale avec un message indiquant seulement l'échec de la connexion. Les déclarations de colonnes entre guillemets, ou partageant une ligne avec d'autres, échappaient à la conversion de types SQLite vers MySQL — d'où le refus d'un index et d'une valeur par défaut. Une base déjà dans cet état est désormais réparée au démarrage au lieu d'échouer à chaque lancement.",
|
"Un carnet MySQL partagé pouvait échouer à créer son schéma, laissant OpsLog sur la base SQLite locale avec un message indiquant seulement l'échec de la connexion. Les déclarations de colonnes entre guillemets, ou partageant une ligne avec d'autres, échappaient à la conversion de types SQLite vers MySQL — d'où le refus d'un index et d'une valeur par défaut. Une base déjà dans cet état est désormais réparée au démarrage au lieu d'échouer à chaque lancement.",
|
||||||
"Yaesu : sélectionner le récepteur SUB affichait la fréquence du MAIN, et l'indicateur de split basculait d'une lecture à l'autre. Un FTDX101 répond à la question du VFO de réception avec deux chiffres là où un FTDX10 n'en donne qu'un, et seul le premier était lu. Les deux formes sont désormais gérées.",
|
"Yaesu : OpsLog suit désormais le VFO réellement utilisé — la fréquence affichée, et le VFO qu'un spot du cluster accorde. Un FTDX101 répond à la question du VFO de réception avec deux chiffres là où un FTDX10 n'en donne qu'un, et c'est le mauvais qui était lu.",
|
||||||
"Cliquer un spot dans DXHunter peut désormais accorder la radio : un paquet UDP remote_call contenant <FREQ> et <MODE> règle la fréquence et le mode sur le backend CAT actif, en plus de renseigner l'indicatif. Un paquet sans ces balises se contente, comme avant, de renseigner l'indicatif.",
|
"Cliquer un spot dans DXHunter peut désormais accorder la radio : un paquet UDP remote_call contenant <FREQ> et <MODE> règle la fréquence et le mode sur le backend CAT actif, en plus de renseigner l'indicatif. Un paquet sans ces balises se contente, comme avant, de renseigner l'indicatif.",
|
||||||
"L'option d'import ADIF qui remplit vos champs station depuis votre profil applique aussi vos statuts de confirmation par défaut (QSL papier, LoTW, eQSL, Club Log, HRDLog, QRZ.com) sur ceux que le fichier laisse vides — c'était déjà le cas, mais rien ne le disait, et un log WSJT-X n'en contient pratiquement aucun. Les confirmations existantes ne sont jamais écrasées.",
|
"L'option d'import ADIF qui remplit vos champs station depuis votre profil applique aussi vos statuts de confirmation par défaut (QSL papier, LoTW, eQSL, Club Log, HRDLog, QRZ.com) sur ceux que le fichier laisse vides — c'était déjà le cas, mais rien ne le disait, et un log WSJT-X n'en contient pratiquement aucun. Les confirmations existantes ne sont jamais écrasées.",
|
||||||
"Le téléchargement des confirmations QRZ.com marquait beaucoup trop de QSO comme confirmés : il acceptait l'indicateur de QSL papier reçue — que vous avez vous-même envoyé à QRZ — comme une confirmation QRZ. Seule la confirmation propre à QRZ compte désormais ; vérifiez la colonne QRZ.com reçu après votre prochain téléchargement, les précédents ayant pu la remplir à tort.",
|
"Le téléchargement des confirmations QRZ.com marquait beaucoup trop de QSO comme confirmés : il acceptait l'indicateur de QSL papier reçue — que vous avez vous-même envoyé à QRZ — comme une confirmation QRZ. Seule la confirmation propre à QRZ compte désormais ; vérifiez la colonne QRZ.com reçu après votre prochain téléchargement, les précédents ayant pu la remplir à tort.",
|
||||||
|
|||||||
+13
-16
@@ -506,27 +506,24 @@ func yaesuModeDigit(mode string, freqHz int64) byte {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// yaesuStateDigit returns the STATE digit of a reply — the last digit before the
|
// yaesuStateDigit returns the STATE digit of a reply — the FIRST digit after
|
||||||
// terminator — or 0 if the reply does not belong to this command.
|
// the command — or 0 if the reply does not belong to this command.
|
||||||
//
|
//
|
||||||
// The parameter is not always one character. An FTDX101 answers "FR01;" where an
|
// The parameter is not always one character: an FTDX101 answers "FR01;" where an
|
||||||
// FTDX10 answers "FR0;", and reading the FIRST digit took the 0 of "01" as the
|
// FTDX10 answers "FR0;". The state is the FIRST digit in both; the second is a
|
||||||
// answer: the rig was on SUB and OpsLog said MAIN, showing the main frequency and
|
// separate parameter. Reading the LAST digit inverted it — with RX and TX on
|
||||||
// making the split flip on alternate polls (F4NBZ, 2026-07-29). The last digit is
|
// MAIN the rig answered FR01, OpsLog concluded SUB, the main frequency stopped
|
||||||
// the state on both, so it is what gets read.
|
// updating and a spot click tuned VFO B (F4NBZ, 2026-07-29). That was my own
|
||||||
|
// correction of the previous evening, made the wrong way round: the earlier
|
||||||
|
// "SUB shows MAIN" fault came from reading VS, not from this digit.
|
||||||
func yaesuStateDigit(reply, cmd string) byte {
|
func yaesuStateDigit(reply, cmd string) byte {
|
||||||
r := strings.TrimSpace(reply)
|
r := strings.TrimSpace(reply)
|
||||||
if !strings.HasPrefix(r, cmd) {
|
if !strings.HasPrefix(r, cmd) || len(r) <= len(cmd) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
r = strings.TrimSuffix(r, ";")
|
d := r[len(cmd)]
|
||||||
digits := r[len(cmd):]
|
if d < '0' || d > '9' {
|
||||||
if digits == "" {
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
last := digits[len(digits)-1]
|
return d
|
||||||
if last < '0' || last > '9' {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return last
|
|
||||||
}
|
}
|
||||||
|
|||||||
+56
-13
@@ -225,11 +225,14 @@ func TestYaesuReceiveVFOAndSplit(t *testing.T) {
|
|||||||
|
|
||||||
// The state digit of a reply, when the parameter is not one character.
|
// The state digit of a reply, when the parameter is not one character.
|
||||||
//
|
//
|
||||||
// An FTDX101 answers "FR01;" where an FTDX10 answers "FR0;". Reading the FIRST
|
// An FTDX101 answers "FR01;" where an FTDX10 answers "FR0;". The state is the
|
||||||
// digit took the 0 of "01" as the answer, so a rig on SUB reported MAIN: OpsLog
|
// FIRST digit on both — the second is a separate parameter.
|
||||||
// showed the main frequency, and — since split is "TX VFO differs from RX VFO" —
|
//
|
||||||
// the split flag flipped between consecutive polls, swapping freq and rx in the
|
// This test asserted the opposite for one evening. Reading the LAST digit turned
|
||||||
// log (F4NBZ, 2026-07-29). The last digit is the state on both rigs.
|
// "FR01" into SUB, so an operator with RX and TX on MAIN saw the main frequency
|
||||||
|
// freeze and a spot click tune VFO B (F4NBZ, 2026-07-29). The fault it was meant
|
||||||
|
// to fix — "SUB shows MAIN" — came from reading VS, not from this digit, and the
|
||||||
|
// FR probe alone had already fixed it.
|
||||||
func TestYaesuStateDigit(t *testing.T) {
|
func TestYaesuStateDigit(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
reply, cmd string
|
reply, cmd string
|
||||||
@@ -237,8 +240,8 @@ func TestYaesuStateDigit(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"FR0;", "FR", '0'}, // FTDX10 form
|
{"FR0;", "FR", '0'}, // FTDX10 form
|
||||||
{"FR1;", "FR", '1'},
|
{"FR1;", "FR", '1'},
|
||||||
{"FR01;", "FR", '1'}, // FTDX101 form — the bug: this read as '0'
|
{"FR01;", "FR", '0'}, // FTDX101 form: MAIN — the reported bug read this as SUB
|
||||||
{"FR00;", "FR", '0'},
|
{"FR11;", "FR", '1'}, // …and this is SUB
|
||||||
{"ST1;", "ST", '1'},
|
{"ST1;", "ST", '1'},
|
||||||
{"FT0;", "FT", '0'},
|
{"FT0;", "FT", '0'},
|
||||||
{"VS1;", "VS", '1'},
|
{"VS1;", "VS", '1'},
|
||||||
@@ -255,12 +258,52 @@ func TestYaesuStateDigit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// End to end: the FTDX101 two-digit form must place the operator on SUB.
|
// End to end, both directions of the reported fault:
|
||||||
if d := yaesuStateDigit("FR01;", "FR"); d != '1' {
|
if d := yaesuStateDigit("FR01;", "FR"); d != '0' {
|
||||||
t.Fatalf("FR01 read as %q — the operator is on SUB and must be seen there", d)
|
t.Fatalf("FR01 read as %q — the operator is on MAIN and must be seen there", d)
|
||||||
}
|
}
|
||||||
// And with the receive VFO right, both-on-sub is correctly NOT split.
|
if d := yaesuStateDigit("FR11;", "FR"); d != '1' {
|
||||||
if yaesuSplitFromReply("FT01;", "FT", "B") {
|
t.Fatalf("FR11 read as %q — the operator is on SUB", d)
|
||||||
t.Error("RX and TX both on sub reported as split")
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A spot click tunes the VFO the operator is ON, and the display reads that same
|
||||||
|
// VFO. Both follow from the receive-VFO digit, which is why it is pinned here in
|
||||||
|
// the operator's terms rather than only as a byte.
|
||||||
|
//
|
||||||
|
// Reported both ways round on an FTDX101 (F4NBZ, 2026-07-29): with RX and TX on
|
||||||
|
// SUB everything worked, and with them on MAIN the frequency froze and a spot
|
||||||
|
// clicked tuned the sub VFO.
|
||||||
|
func TestYaesuActiveVFOFollowsReceiveVFO(t *testing.T) {
|
||||||
|
// Mirrors ReadState's choice of VFO and SetFrequency's choice of command.
|
||||||
|
activeVFO := func(frReply string) string {
|
||||||
|
if yaesuStateDigit(frReply, "FR") == '1' {
|
||||||
|
return "B"
|
||||||
|
}
|
||||||
|
return "A"
|
||||||
|
}
|
||||||
|
tuneCmd := func(vfo string) string {
|
||||||
|
if vfo == "B" {
|
||||||
|
return "FB" // sub
|
||||||
|
}
|
||||||
|
return "FA" // main
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name, fr, wantVFO, wantCmd string
|
||||||
|
}{
|
||||||
|
{"RX and TX on MAIN (FTDX101 two-digit reply)", "FR01;", "A", "FA"},
|
||||||
|
{"RX and TX on SUB (FTDX101)", "FR11;", "B", "FB"},
|
||||||
|
{"MAIN on a one-digit rig", "FR0;", "A", "FA"},
|
||||||
|
{"SUB on a one-digit rig", "FR1;", "B", "FB"},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
vfo := activeVFO(c.fr)
|
||||||
|
if vfo != c.wantVFO {
|
||||||
|
t.Errorf("%s: active VFO = %s, want %s", c.name, vfo, c.wantVFO)
|
||||||
|
}
|
||||||
|
if cmd := tuneCmd(vfo); cmd != c.wantCmd {
|
||||||
|
t.Errorf("%s: a spot click would write %s, want %s", c.name, cmd, c.wantCmd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user