Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d526d29db | ||
|
|
09c4358626 | ||
|
|
9ce7cf3b69 | ||
|
|
3745d23339 |
@@ -157,6 +157,7 @@ const (
|
||||
// the rule since is that nothing a backend does may be steered by another
|
||||
// backend's setting.
|
||||
keyCATYaesuLowLines = "cat.yaesu.low_dtr_rts" // deassert DTR/RTS on connect
|
||||
keyCATYaesuRTTYUSB = "cat.yaesu.rtty_usb" // set RTTY on USB rather than the older LSB
|
||||
keyCATKenwoodLowLines = "cat.kenwood.low_dtr_rts" // deassert DTR/RTS on connect
|
||||
keyCATKenwoodDataMode = "cat.kenwood.data_mode" // data modes → "usb" | "data" (MD6) | "keep"
|
||||
keyCATIcomPort = "cat.icom.port" // Icom USB CI-V serial port (e.g. COM5)
|
||||
@@ -510,7 +511,11 @@ type CATSettings struct {
|
||||
// Per-backend: deassert DTR and RTS after opening the CAT port, for
|
||||
// interfaces that read either line as PTT. Off by default: lowering them
|
||||
// stops some USB-serial interfaces transmitting at all.
|
||||
YaesuLowLines bool `json:"yaesu_low_lines"`
|
||||
YaesuLowLines bool `json:"yaesu_low_lines"`
|
||||
// YaesuRTTYUSB: ADIF says "RTTY" and Yaesu has both sidebands, so the rig
|
||||
// cannot be driven from the logged mode alone. Off = RTTY-L, the older
|
||||
// convention.
|
||||
YaesuRTTYUSB bool `json:"yaesu_rtty_usb"`
|
||||
KenwoodLowLines bool `json:"kenwood_low_lines"`
|
||||
IcomPort string `json:"icom_port"` // Icom USB CI-V serial port (e.g. COM5)
|
||||
IcomBaud int `json:"icom_baud"` // Icom CI-V baud (default 115200)
|
||||
@@ -8317,7 +8322,7 @@ func (a *App) GetCATSettings() (CATSettings, error) {
|
||||
if a.settings == nil {
|
||||
return CATSettings{Backend: "omnirig", OmniRigNum: 1, PollMs: 250}, fmt.Errorf("db not initialized")
|
||||
}
|
||||
m, err := a.settings.GetMany(a.ctx, keyCATEnabled, keyCATBackend, keyCATOmniRigNum, keyCATOmniRigVFO, keyCATFlexHost, keyCATFlexPort, keyCATFlexSpots, keyCATFlexDVKDax, keyCATFlexDecodeSpots, keyCATFlexDecodeSecs, keyCATXieguPort, keyCATXieguBaud, keyCATXieguAddr, keyCATXieguPTTLine, keyCATYaesuPort, keyCATYaesuBaud, keyCATKenwoodPort, keyCATKenwoodBaud, keyCATKenwoodHost, keyCATKenwoodLink, keyCATYaesuLowLines, keyCATKenwoodLowLines, keyCATKenwoodDataMode, keyCATIcomPort, keyCATIcomBaud, keyCATIcomAddr, keyCATIcomNetHost, keyCATIcomNetUser, keyCATIcomNetPass, keyCATIcomNetAudio, keyCATTCIHost, keyCATTCIPort, keyCATTCISpots, keyCATPttHotkeyEnabled, keyCATPttHotkey, keyCATPttHotkeyToggle, keyCATPollMs, keyCATDelayMs, keyCATOffsetOn, keyCATOffsetHz, keyCATDigitalDefault, keyCATShareEnabled, keyCATSharePort, keyCATShareProto, keyCATShareTCIPort, keyCATDigiUSB)
|
||||
m, err := a.settings.GetMany(a.ctx, keyCATEnabled, keyCATBackend, keyCATOmniRigNum, keyCATOmniRigVFO, keyCATFlexHost, keyCATFlexPort, keyCATFlexSpots, keyCATFlexDVKDax, keyCATFlexDecodeSpots, keyCATFlexDecodeSecs, keyCATXieguPort, keyCATXieguBaud, keyCATXieguAddr, keyCATXieguPTTLine, keyCATYaesuPort, keyCATYaesuBaud, keyCATYaesuRTTYUSB, keyCATKenwoodPort, keyCATKenwoodBaud, keyCATKenwoodHost, keyCATKenwoodLink, keyCATYaesuLowLines, keyCATKenwoodLowLines, keyCATKenwoodDataMode, keyCATIcomPort, keyCATIcomBaud, keyCATIcomAddr, keyCATIcomNetHost, keyCATIcomNetUser, keyCATIcomNetPass, keyCATIcomNetAudio, keyCATTCIHost, keyCATTCIPort, keyCATTCISpots, keyCATPttHotkeyEnabled, keyCATPttHotkey, keyCATPttHotkeyToggle, keyCATPollMs, keyCATDelayMs, keyCATOffsetOn, keyCATOffsetHz, keyCATDigitalDefault, keyCATShareEnabled, keyCATSharePort, keyCATShareProto, keyCATShareTCIPort, keyCATDigiUSB)
|
||||
if err != nil {
|
||||
return CATSettings{}, err
|
||||
}
|
||||
@@ -8343,6 +8348,7 @@ func (a *App) GetCATSettings() (CATSettings, error) {
|
||||
KenwoodLink: kenwoodLinkOr(m[keyCATKenwoodLink], m[keyCATKenwoodHost]),
|
||||
KenwoodBaud: 9600,
|
||||
YaesuLowLines: m[keyCATYaesuLowLines] == "1",
|
||||
YaesuRTTYUSB: m[keyCATYaesuRTTYUSB] == "1",
|
||||
KenwoodLowLines: m[keyCATKenwoodLowLines] == "1",
|
||||
KenwoodDataMode: m[keyCATKenwoodDataMode],
|
||||
IcomPort: m[keyCATIcomPort],
|
||||
@@ -8556,6 +8562,7 @@ func (a *App) SaveCATSettings(s CATSettings) error {
|
||||
keyCATKenwoodLink: kenwoodLinkOr(s.KenwoodLink, s.KenwoodHost),
|
||||
keyCATKenwoodBaud: strconv.Itoa(s.KenwoodBaud),
|
||||
keyCATYaesuLowLines: b01(s.YaesuLowLines),
|
||||
keyCATYaesuRTTYUSB: b01(s.YaesuRTTYUSB),
|
||||
keyCATKenwoodLowLines: b01(s.KenwoodLowLines),
|
||||
keyCATKenwoodDataMode: strings.ToLower(strings.TrimSpace(s.KenwoodDataMode)),
|
||||
keyCATIcomPort: strings.TrimSpace(s.IcomPort),
|
||||
@@ -16283,6 +16290,7 @@ func (a *App) reloadCAT() {
|
||||
// (a rig file that hides the VFO, a Freq property meaning A on one model
|
||||
// and B on another); talking to the radio directly removes it.
|
||||
yz := cat.NewYaesu(s.YaesuPort, s.YaesuBaud, s.DigitalDefault)
|
||||
yz.SetRTTYUpper(s.YaesuRTTYUSB)
|
||||
yz.SetLowerLines(s.YaesuLowLines)
|
||||
a.cat.Start(yz)
|
||||
case "kenwood", "elecraft":
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
[
|
||||
{
|
||||
"version": "0.27.16",
|
||||
"date": "",
|
||||
"en": [
|
||||
"[NEW] Typing a digital watering hole sets the mode with it. A spot click has always carried one; a frequency typed by hand carried none, so the rig stayed in SSB on 28.074 while the operator waited for decodes. Same table and same tolerance as a spot (±3 kHz of a known FT8/FT4/JS8 frequency), only towards the digital modes: tuning away from one leaves the mode alone, because there the frequency says nothing about what you mean to do.",
|
||||
"Yaesu CAT now drives the older radios. The FTDX10, FT-991A, FT-891 and FT-710 write a frequency in nine digits; everything before them — FTDX3000, FTDX5000, FTDX1200, FT-2000, FT-950, FT-450 — writes eight and answers a nine-digit command with a rejection, which is what an FTDX3000 owner saw: every FA refused and a radio that would not follow. The width is taken from the rig’s own reply rather than from a table of models, so a set is in the format that radio speaks — including models this backend has never heard of.",
|
||||
"Yaesu: RTTY can be set on USB (Settings → CAT). ADIF records only “RTTY” and the rig has both sidebands, so the log cannot answer for it — the older RTTY-L stays the default, and a station whose FSK controller wants the upper one says so once."
|
||||
],
|
||||
"fr": [
|
||||
"[NEW] Taper une fréquence d’appel numérique règle le mode avec elle. Un clic sur un spot en portait un depuis toujours ; une fréquence tapée à la main n’en portait aucun, si bien que le poste restait en SSB sur 28.074 pendant qu’on attendait les décodages. Même table et même tolérance qu’un spot (±3 kHz d’une fréquence FT8/FT4/JS8 connue), et seulement vers les modes numériques : en s’en éloignant le mode n’est pas touché, car là la fréquence ne dit rien de ce qu’on veut faire.",
|
||||
"Le CAT Yaesu pilote désormais les postes plus anciens. FTDX10, FT-991A, FT-891 et FT-710 écrivent une fréquence sur neuf chiffres ; tout ce qui précède — FTDX3000, FTDX5000, FTDX1200, FT-2000, FT-950, FT-450 — l’écrit sur huit et rejette une commande à neuf chiffres. C’est ce que voyait un possesseur de FTDX3000 : chaque FA refusée et une radio qui ne suivait pas. Le format est pris dans la réponse du poste plutôt que dans une table de modèles : l’envoi part donc dans la langue de cette radio-là, y compris pour des modèles que ce backend ne connaît pas.",
|
||||
"Yaesu : le RTTY peut être placé en USB (Réglages → CAT). L’ADIF n’enregistre que « RTTY » et le poste a les deux bandes latérales : le log ne peut pas répondre à sa place. Le RTTY-L ancien reste par défaut, et une station dont l’interface FSK veut la supérieure le dit une fois."
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.27.15",
|
||||
"date": "",
|
||||
|
||||
+24
-1
@@ -5926,7 +5926,29 @@ export default function App() {
|
||||
const mhz = parseFloat(mhzStr);
|
||||
if (!Number.isFinite(mhz) || mhz < 0.1 || mhz > 3000) return;
|
||||
noteManualEdit();
|
||||
SetCATFrequency(Math.round(mhz * 1_000_000)).catch(() => {});
|
||||
const hz = Math.round(mhz * 1_000_000);
|
||||
SetCATFrequency(hz).catch(() => {});
|
||||
tuneModeForWateringHole(hz);
|
||||
};
|
||||
|
||||
// 28.074 IS FT8, and typing it says so.
|
||||
//
|
||||
// A spot click has always carried a mode; a frequency typed by hand carried
|
||||
// none, so the rig stayed in whatever it was — an FTDX3000 landing on 28.074
|
||||
// in USB while the operator waited for decodes. Nobody tunes a watering hole
|
||||
// to listen to it in SSB, and this is the same table and the same tolerance a
|
||||
// spot click is judged with (±3 kHz of a known digital frequency).
|
||||
//
|
||||
// Only when it actually changes something, and only towards the digital
|
||||
// modes: tuning away from 28.074 leaves the mode alone, because there the
|
||||
// frequency says nothing about what the operator means to do.
|
||||
const tuneModeForWateringHole = (hz: number) => {
|
||||
const m = inferDigitalMode(hz);
|
||||
if (!m || m === mode) return;
|
||||
setMode(m);
|
||||
applyModePreset(m);
|
||||
if (catState.enabled && catState.connected && !locks.mode) SetCATMode(m).catch(() => {});
|
||||
ConfigureDecoderMode(m).catch(() => {});
|
||||
};
|
||||
// Carry out what was typed in the call field. Bands go through the ordinary
|
||||
// band change, so the antennas, the power table and the outbound integrations
|
||||
@@ -5944,6 +5966,7 @@ export default function App() {
|
||||
const b = bandForMHz(hz / 1_000_000);
|
||||
if (b) setBand(b);
|
||||
if (catState.enabled && catState.connected) SetCATFrequency(hz).catch(() => {});
|
||||
tuneModeForWateringHole(hz);
|
||||
showToast((hz / 1_000_000).toFixed(3) + ' MHz');
|
||||
};
|
||||
|
||||
|
||||
@@ -1628,7 +1628,7 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
|
||||
const [icomCustom, setIcomCustom] = useState(false);
|
||||
const [catCfg, setCatCfg] = useState<CATSettings>({
|
||||
enabled: false, backend: 'omnirig', omnirig_rig: 1, omnirig_vfo: '', flex_host: '', flex_port: 4992, flex_spots: false, flex_decode_spots: false, flex_decode_secs: 120, flex_dvk_dax: false,
|
||||
yaesu_port: '', yaesu_baud: 38400, yaesu_low_lines: false, kenwood_low_lines: false, kenwood_port: '', kenwood_baud: 9600, kenwood_host: '', kenwood_link: 'usb', kenwood_data_mode: 'usb', xiegu_port: '', xiegu_baud: 19200, xiegu_addr: 0x70, xiegu_ptt_line: '',
|
||||
yaesu_port: '', yaesu_baud: 38400, yaesu_low_lines: false, yaesu_rtty_usb: false, kenwood_low_lines: false, kenwood_port: '', kenwood_baud: 9600, kenwood_host: '', kenwood_link: 'usb', kenwood_data_mode: 'usb', xiegu_port: '', xiegu_baud: 19200, xiegu_addr: 0x70, xiegu_ptt_line: '',
|
||||
icom_port: '', icom_baud: 115200, icom_addr: 0x98, icom_net_host: '', icom_net_user: '', icom_net_pass: '', icom_net_audio: false,
|
||||
tci_host: '', tci_port: 40001, tci_spots: false, poll_ms: 250, delay_ms: 0, offset_on: false, offset_hz: 0,
|
||||
digital_default: 'FT8', share_enabled: false, share_port: 4532, share_proto: 'rigctl', share_tci_port: 40001,
|
||||
@@ -3532,6 +3532,17 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
|
||||
</label>
|
||||
<span className="text-xs text-muted-foreground">{t('cat.lowerLinesHint')}</span>
|
||||
</div>
|
||||
{/* ADIF records "RTTY" and nothing more, and the rig has two
|
||||
sidebands to put it on — so this is the operator's answer, not
|
||||
something the log can supply. */}
|
||||
<div className="col-span-2 space-y-1">
|
||||
<label className="flex items-center gap-2 text-xs cursor-pointer">
|
||||
<Checkbox checked={!!(catCfg as any).yaesu_rtty_usb}
|
||||
onCheckedChange={(c) => setCatCfg((s) => ({ ...s, yaesu_rtty_usb: !!c } as any))} />
|
||||
{t('cat.yaesuRttyUsb')}
|
||||
</label>
|
||||
<span className="text-xs text-muted-foreground">{t('cat.yaesuRttyUsbHint')}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{['icom', 'xiegu', 'kenwood', 'elecraft'].includes(catCfg.backend) && (
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
// Single source of truth for the app version shown in the UI (header + About).
|
||||
// Bump this on a release (the release script updates it alongside telemetry.go).
|
||||
export const APP_VERSION = '0.27.15';
|
||||
export const APP_VERSION = '0.27.16';
|
||||
|
||||
// Author / credits, shown in Help -> About.
|
||||
export const APP_AUTHOR = 'F4BPO';
|
||||
|
||||
@@ -2379,6 +2379,7 @@ export namespace main {
|
||||
kenwood_baud: number;
|
||||
kenwood_data_mode: string;
|
||||
yaesu_low_lines: boolean;
|
||||
yaesu_rtty_usb: boolean;
|
||||
kenwood_low_lines: boolean;
|
||||
icom_port: string;
|
||||
icom_baud: number;
|
||||
@@ -2432,6 +2433,7 @@ export namespace main {
|
||||
this.kenwood_baud = source["kenwood_baud"];
|
||||
this.kenwood_data_mode = source["kenwood_data_mode"];
|
||||
this.yaesu_low_lines = source["yaesu_low_lines"];
|
||||
this.yaesu_rtty_usb = source["yaesu_rtty_usb"];
|
||||
this.kenwood_low_lines = source["kenwood_low_lines"];
|
||||
this.icom_port = source["icom_port"];
|
||||
this.icom_baud = source["icom_baud"];
|
||||
|
||||
+79
-5
@@ -59,6 +59,13 @@ var yaesuModels = map[string]string{
|
||||
"0650": "FT-891",
|
||||
"0670": "FT-DX3000",
|
||||
"0460": "FT-450D",
|
||||
// The eight-digit family. Named for the console; the frequency format is
|
||||
// learned from the rig either way (see learnFreqWidth).
|
||||
"0251": "FT-2000",
|
||||
"0310": "FT-950",
|
||||
"0583": "FTDX1200",
|
||||
"0462": "FTDX3000",
|
||||
"0101": "FTDX5000",
|
||||
}
|
||||
|
||||
// yaesuModeToADIF maps the MD digit to an ADIF mode. The DATA and RTTY variants
|
||||
@@ -100,6 +107,11 @@ type Yaesu struct {
|
||||
// rxVFOCmd is "FR" when the rig reports its receive VFO that way, else empty
|
||||
// and VS is used — see ReadState.
|
||||
rxVFOCmd string
|
||||
// freqDigits is how many digits this rig writes a frequency in, LEARNED from
|
||||
// its own replies. See learnFreqWidth.
|
||||
freqDigits int
|
||||
// rttyUpper picks RTTY-U over RTTY-L — see SetRTTYUpper.
|
||||
rttyUpper bool
|
||||
|
||||
curFreq int64
|
||||
curRXFreq int64
|
||||
@@ -130,6 +142,18 @@ func NewYaesu(portName string, baud int, digital string) *Yaesu {
|
||||
return &Yaesu{portName: strings.TrimSpace(portName), baud: baud, digital: digital, curVFO: "A"}
|
||||
}
|
||||
|
||||
// SetRTTYUpper chooses which sideband RTTY is set on.
|
||||
//
|
||||
// Yaesu has both — MD06 is RTTY-L, MD09 is RTTY-U — and ADIF has neither: it
|
||||
// says "RTTY" and stops there, so the rig cannot be driven from the logged mode
|
||||
// alone. LSB is the older convention and stays the default; an operator whose
|
||||
// FSK controller or decoder wants the other one says so once here.
|
||||
func (y *Yaesu) SetRTTYUpper(v bool) {
|
||||
y.mu.Lock()
|
||||
defer y.mu.Unlock()
|
||||
y.rttyUpper = v
|
||||
}
|
||||
|
||||
// SetLowerLines chooses whether DTR and RTS are deasserted on connect. Set
|
||||
// before Connect.
|
||||
func (y *Yaesu) SetLowerLines(v bool) {
|
||||
@@ -275,6 +299,7 @@ func (y *Yaesu) ReadState() (RigState, error) {
|
||||
if err != nil {
|
||||
return RigState{}, err // the rig stopped answering — let the Manager reconnect
|
||||
}
|
||||
y.learnFreqWidth(faRaw, "FA")
|
||||
freqA, ok := parseYaesuFreq(faRaw, "FA")
|
||||
if !ok {
|
||||
return RigState{}, fmt.Errorf("yaesu: unparsable FA reply %q", faRaw)
|
||||
@@ -355,7 +380,7 @@ func (y *Yaesu) SetFrequency(hz int64) error {
|
||||
if y.curVFO == "B" {
|
||||
cmd = "FB"
|
||||
}
|
||||
return y.write(fmt.Sprintf("%s%09d;", cmd, hz))
|
||||
return y.write(fmt.Sprintf("%s%0*d;", cmd, y.freqWidth(), hz))
|
||||
}
|
||||
|
||||
func (y *Yaesu) SetMode(mode string) error {
|
||||
@@ -364,7 +389,7 @@ func (y *Yaesu) SetMode(mode string) error {
|
||||
if y.port == nil {
|
||||
return fmt.Errorf("yaesu: not connected")
|
||||
}
|
||||
d := yaesuModeDigit(mode, y.curFreq)
|
||||
d := yaesuModeDigit(mode, y.curFreq, y.rttyUpper)
|
||||
if d == 0 {
|
||||
return fmt.Errorf("yaesu: no CAT mode for %q", mode)
|
||||
}
|
||||
@@ -481,7 +506,53 @@ func cmdPrefix(cmd string) string {
|
||||
return c
|
||||
}
|
||||
|
||||
// parseYaesuFreq reads "FA014074000;" into Hz.
|
||||
// EIGHT DIGITS OR NINE — the rig says which, and it is not a matter of taste.
|
||||
//
|
||||
// The FTDX10, FT-991A, FT-891 and FT-710 write a frequency in nine digits;
|
||||
// everything before them — FTDX3000, FTDX5000, FTDX1200, FT-2000, FT-950,
|
||||
// FT-450 — writes eight, and answers a nine-digit SET with "?;". An operator
|
||||
// with an FTDX3000 saw exactly that: every FA command rejected, a radio that
|
||||
// would not follow, and nothing to say why.
|
||||
//
|
||||
// The width is LEARNED rather than tabulated: the rig announces it in every
|
||||
// reply to "FA;", so the answer comes from the radio in front of the operator
|
||||
// instead of from a list of models that will always be one release behind. Nine
|
||||
// until the first reply lands, which is what the modern rigs use and what this
|
||||
// backend was written against.
|
||||
const yaesuFreqDigitsDefault = 9
|
||||
|
||||
func (y *Yaesu) freqWidth() int {
|
||||
if y.freqDigits >= 8 && y.freqDigits <= 11 {
|
||||
return y.freqDigits
|
||||
}
|
||||
return yaesuFreqDigitsDefault
|
||||
}
|
||||
|
||||
// learnFreqWidth takes the width from a frequency reply. Only a reply that
|
||||
// parses as a frequency teaches anything — a "?;" or a stray frame says nothing
|
||||
// about the format, and a width learned from one would be worse than the
|
||||
// default.
|
||||
func (y *Yaesu) learnFreqWidth(reply, prefix string) {
|
||||
r := strings.TrimSpace(reply)
|
||||
if !strings.HasPrefix(r, prefix) {
|
||||
return
|
||||
}
|
||||
digits := strings.TrimSuffix(strings.TrimPrefix(r, prefix), ";")
|
||||
if len(digits) < 8 || len(digits) > 11 {
|
||||
return
|
||||
}
|
||||
for _, c := range digits {
|
||||
if c < '0' || c > '9' {
|
||||
return
|
||||
}
|
||||
}
|
||||
if y.freqDigits != len(digits) {
|
||||
debugLog.Printf("yaesu: this rig writes frequencies in %d digits — commands will match", len(digits))
|
||||
y.freqDigits = len(digits)
|
||||
}
|
||||
}
|
||||
|
||||
// parseYaesuFreq reads "FA014074000;" (or "FA14074000;") into Hz.
|
||||
func parseYaesuFreq(reply, prefix string) (int64, bool) {
|
||||
r := strings.TrimSpace(reply)
|
||||
if !strings.HasPrefix(r, prefix) {
|
||||
@@ -551,7 +622,7 @@ func resolveYaesuVFOs(freqA, freqB int64, vfo string, split bool) (tx, rx int64,
|
||||
// yaesuModeDigit maps an ADIF mode to the MD digit. SSB has no single digit —
|
||||
// the sideband follows the worldwide convention (LSB below 10 MHz, USB above),
|
||||
// which is why the current frequency is part of the decision.
|
||||
func yaesuModeDigit(mode string, freqHz int64) byte {
|
||||
func yaesuModeDigit(mode string, freqHz int64, rttyUpper bool) byte {
|
||||
switch strings.ToUpper(strings.TrimSpace(mode)) {
|
||||
case "SSB":
|
||||
if freqHz > 0 && freqHz < 10_000_000 {
|
||||
@@ -569,7 +640,10 @@ func yaesuModeDigit(mode string, freqHz int64) byte {
|
||||
case "AM":
|
||||
return '5'
|
||||
case "RTTY":
|
||||
return '6'
|
||||
if rttyUpper {
|
||||
return '9' // RTTY-U
|
||||
}
|
||||
return '6' // RTTY-L, the older convention
|
||||
case "":
|
||||
return 0
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package cat
|
||||
|
||||
import "testing"
|
||||
|
||||
// The FTDX10 family writes a frequency in nine digits; everything before it —
|
||||
// FTDX3000, FTDX5000, FTDX1200, FT-2000, FT-950, FT-450 — writes eight and
|
||||
// answers a nine-digit SET with "?;". Reported from an FTDX3000: every FA
|
||||
// command rejected, a radio that would not follow.
|
||||
//
|
||||
// The width is taken from the rig's own reply, so a model this backend has
|
||||
// never heard of is right on the first read.
|
||||
func TestYaesuFrequencyWidthIsLearnedFromTheRig(t *testing.T) {
|
||||
y := &Yaesu{}
|
||||
if got := y.freqWidth(); got != 9 {
|
||||
t.Errorf("before any reply the width is %d, want the modern 9", got)
|
||||
}
|
||||
|
||||
y.learnFreqWidth("FA14074000;", "FA") // an FTDX3000
|
||||
if got := y.freqWidth(); got != 8 {
|
||||
t.Errorf("width %d after an eight-digit reply, want 8", got)
|
||||
}
|
||||
|
||||
y.learnFreqWidth("FA014074000;", "FA") // and an FTDX10 on the next session
|
||||
if got := y.freqWidth(); got != 9 {
|
||||
t.Errorf("width %d after a nine-digit reply, want 9", got)
|
||||
}
|
||||
|
||||
// Nothing that is not a frequency teaches anything: a rejection, a stray
|
||||
// frame or a reply from another command would otherwise set the format for
|
||||
// every command that follows.
|
||||
for _, junk := range []string{"?;", "FA;", "FB014074000;", "FA1407400X;", "FA1234567;", "FA123456789012;"} {
|
||||
before := y.freqWidth()
|
||||
y.learnFreqWidth(junk, "FA")
|
||||
if after := y.freqWidth(); after != before {
|
||||
t.Errorf("%q changed the width from %d to %d", junk, before, after)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ func TestYaesuModeDigit(t *testing.T) {
|
||||
{"", 14074000, 0}, // nothing to set
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := yaesuModeDigit(c.mode, c.hz); got != c.want {
|
||||
if got := yaesuModeDigit(c.mode, c.hz, false); got != c.want {
|
||||
t.Errorf("yaesuModeDigit(%q, %d) = %q, want %q", c.mode, c.hz, got, c.want)
|
||||
}
|
||||
}
|
||||
@@ -406,3 +406,22 @@ func TestYaesuAntennaCommand(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ADIF says "RTTY" and stops there, but Yaesu has both sidebands and the rig
|
||||
// has to be told one. LSB is the older convention and the default; the other is
|
||||
// a station's own choice, made once.
|
||||
func TestYaesuRTTYSideband(t *testing.T) {
|
||||
if got := yaesuModeDigit("RTTY", 14_080_000, false); got != '6' {
|
||||
t.Errorf("RTTY = %q, want RTTY-L", got)
|
||||
}
|
||||
if got := yaesuModeDigit("RTTY", 14_080_000, true); got != '9' {
|
||||
t.Errorf("RTTY (upper) = %q, want RTTY-U", got)
|
||||
}
|
||||
// The switch is about RTTY and nothing else.
|
||||
if got := yaesuModeDigit("FT8", 28_074_000, true); got != 'C' {
|
||||
t.Errorf("FT8 = %q, want DATA-U", got)
|
||||
}
|
||||
if got := yaesuModeDigit("CW", 14_030_000, true); got != '3' {
|
||||
t.Errorf("CW = %q, want CW-U", got)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import (
|
||||
|
||||
const (
|
||||
// appVersion is stamped on every heartbeat (and could feed the About box).
|
||||
appVersion = "0.27.15"
|
||||
appVersion = "0.27.16"
|
||||
|
||||
// posthogHost is the PostHog ingestion endpoint. EU cloud by default; change
|
||||
// to https://us.i.posthog.com for a US project.
|
||||
|
||||
Reference in New Issue
Block a user