From ab0667385495d6d01a477eb15aec3d0ff683feec Mon Sep 17 00:00:00 2001 From: rouggy Date: Tue, 7 Jul 2026 17:10:42 +0200 Subject: [PATCH] feat: added APF for CW in Icom --- README.fr.md | 300 ++++++++++++++++++++++++++ README.md | 143 ++++++++++-- app.go | 7 + frontend/src/components/IcomPanel.tsx | 55 +++-- frontend/src/lib/i18n.tsx | 4 +- frontend/wailsjs/go/main/App.d.ts | 2 + frontend/wailsjs/go/main/App.js | 4 + frontend/wailsjs/go/models.ts | 2 + internal/cat/cat.go | 2 + internal/cat/civ/civ.go | 1 + internal/cat/icomserial.go | 11 + 11 files changed, 493 insertions(+), 38 deletions(-) create mode 100644 README.fr.md diff --git a/README.fr.md b/README.fr.md new file mode 100644 index 0000000..8a23b4d --- /dev/null +++ b/README.fr.md @@ -0,0 +1,300 @@ +# OpsLog + +Un logiciel de log radioamateur moderne et rapide pour Windows — saisie façon +Log4OM, CAT en temps réel pour **OmniRig**, **FlexRadio/SmartSDR** natif, +**Icom CI-V** natif (USB **et** à distance par internet, en remplacement de +RS-BA1) et **TCI** (SunSDR / Expert Electronics), cluster DX avec alertes de +spots, suivi des diplômes, cartes, log de concours, gestion des QSL et un +concepteur de cartes QSL. Construit avec **Wails v2** (backend Go + frontend +React/TypeScript), **100 % Go** (sans CGO) : SQLite pour la configuration, +**MySQL partagé** optionnel pour le journal afin que plusieurs opérateurs +partagent un même log. Entièrement thémable et bilingue (anglais / français). + +Développé par **F4BPO**. + +--- + +## Compilation / développement + +- **Dev :** `wails dev` (rechargement à chaud Vite ; méthodes Go accessibles sur http://localhost:34115). +- **Build :** `wails build` (utiliser la version wails v2.11 du projet — `~/go/bin/wails.exe`). +- **Régénérer les bindings Go↔TS** après modification des méthodes `App` + exportées : `wails generate module`. +- **Release :** `.vscode/release.ps1` (Ctrl+Maj+P → *Tasks: Run Task* → + *Release OpsLog*) — incrémente la version, pousse le source sur Gitea, compile + l'exe et le publie sur les releases Gitea + GitHub. + +--- + +## Journalisation + +- **Bandeau de saisie façon Log4OM :** indicatif, RST émis/reçu, nom/QTH/locator, + bande/mode, fréquence TX/RX (split), heure de début/fin, commentaire/note. Le + **drapeau** de l'entité contactée est affiché en grand à côté des champs RST. +- **Recherche d'indicatif** (QRZ.com / HamQTH) avec photo, pré-remplissage du + nom/QTH/locator et onglet QRZ.com. +- **Résolution DXCC hors ligne** depuis `cty.dat` (pays, zones CQ/ITU, + continent), avec gestion des `/MM` `/AM` `/B` (balise) et des changements de + district (`/8`, `/W6`), plus les dérogations DXpédition de ClubLog par dates. +- **QSO récents**, matrice **déjà contactés** (par créneau bande/mode), + re-résolution en masse depuis cty/QRZ/ClubLog, envoi en masse vers les services + QSL. +- **Constructeur de filtres QSO avancé** (champ / opérateur / valeur, ET / OU, + préréglages enregistrés) avec **export ADIF** des lignes filtrées ou + sélectionnées. +- **Recherche de doublons** (Outils) — regroupe les QSO par même indicatif + + bande + mode (optionnellement même jour / minute) et permet de choisir lesquels + supprimer. +- **Conforme ADIF 3.1.7** en import/export : dictionnaire complet des champs, + 30 colonnes promues, éditeur générique de « champs supplémentaires » et modes + d'export standard/complet. +- **Profils :** chaque réglage est par profil ; chaque profil peut pointer son + journal vers le fichier SQLite local ou une base **MySQL partagée** + (multi-opérateur). + +## Cartes & antenne + +- **Vue principale = deux volets configurables** (par profil, Réglages → Général + → *Vue principale*) : carte grand-cercle, carte locator (rue), la grille du + cluster, la grille des déjà-contactés, les QSO récents, les **commandes + FlexRadio**, la **console Icom** ou le panneau **Net control**. +- **Carte grand-cercle** avec distance & azimut trajet court/long, fonds de carte + sélectionnables (Light / Voyager / Street / Satellite, tous sans clé et + légendés) et le(s) **lobe(s) du faisceau d'antenne** tracés depuis l'azimut du + rotor. +- **Compas de rotor** (azimutal équidistant, clic pour tourner) piloté par + PstRotator. +- **Support Ultrabeam** (Normal / inversé 180° / bidirectionnel) : la direction + rayonnée est en vert et le **boom mécanique** en gris, à la fois sur le compas + et sur la carte, pour toujours savoir où pointe l'antenne. + +## Cluster DX + +- Plusieurs serveurs de cluster avec reconnexion auto, un maître pour les + commandes. +- **Barre latérale de filtres** (recherche d'indicatif, masquer-déjà-contactés, + grouper les doublons, bande / mode / statut / source) partagée entre l'onglet + Cluster et le volet cluster de la vue principale, avec bascule affichage/masquage. +- **Statut** par spot (nouveau / nouvelle bande / nouveau créneau / contacté), + clic pour accorder la radio, et une **bandmap** multi-bandes (bandes façon + panadapter). +- Les spots **POTA** sont étiquetés avec leur référence de parc (via + `api.pota.app`). +- **Alertes de spots** (façon Log4OM) : règles sur indicatif / pays / bande / + mode / spotter, avec notification sonore, visuelle et e-mail (Outils → + *Gestion des alertes*). + +## Contrôle CAT + +Quatre backends natifs (Réglages → CAT), chacun avec reconnexion auto et une +connexion rapide non bloquante — une radio éteinte ne fige jamais l'application : + +- **OmniRig** (Rig 1/2, changement à chaud) — fonctionne avec toute radio + supportée par OmniRig. +- **FlexRadio (SmartSDR)** via l'API TCP de la radio — fréquence / mode / split + de la slice en temps réel, découverte UDP, et **spots panadapter** (les spots + du cluster poussés sur l'écran Flex, clic → remplir l'indicatif). +- **Icom CI-V** — natif, via le port **USB** de la radio *ou* par internet via le + **serveur LAN intégré** de la radio (voir *Icom à distance* ci-dessous). Ni + RS-BA1 ni Remote Utility nécessaires. +- **TCI** (WebSocket) — SunSDR / ExpertSDR2 et tout serveur compatible TCI : + fréquence / mode / PTT / split, plus spots panorama optionnels. + +Le mode est lu depuis la radio ; le sous-mode numérique (FT4 vs FT8) est déduit +de la fréquence. Les **antennes RX/TX Flex par bande** sont configurables et +appliquées automatiquement au changement de bande. + +### Onglet de commande FlexRadio (façon SmartSDR) + +Affiché uniquement quand le backend CAT est une FlexRadio : + +- **Émission :** puissance RF, puissance d'accord, TUNE, MOX, processeur de + parole (NOR/DX/DX+), VOX (+ niveau + délai), moniteur (+ niveau), gain micro. +- **Réception (slice active) :** mode/seuil AGC, niveau audio, NB / NR / ANF. +- **Coupleur d'antenne (ATU) :** accord / bypass / mémoires. +- **Amplificateur :** PowerGenius XL operate/standby + défaut. +- **Mesures en direct** via le flux UDP VITA-49 : S-mètre (unités S), puissance + directe (W), ROS, ALC, température PA, tension, plus les mesures de l'ampli. + +### Onglet de commande Icom + +Affiché quand le backend CAT est Icom (USB ou réseau). Une console complète façon +RS-BA1 : + +- **Double afficheur VFO** (MAIN / SUB) avec la grande fréquence tabulaire, le + badge de mode, la bande et l'offset RIT/ΔTX, et une **rangée de boutons de + mode** (SSB / CW / RTTY / PSK / AM / FM). +- **Scope de spectre + waterfall** (panadapter) : ON/OFF, CTR/FIX, double-clic + pour accorder, et boutons **◀ ⊙ ▶** pour centrer le scope sur la fréquence + actuelle (±50 kHz) et le décaler à gauche/droite. +- **Mesures en direct** toujours visibles : S-mètre (clic → remplir le RST), + puissance en watts, ROS. +- **DSP réception :** gain AF / RF, squelch, AGC, préampli, atténuateur, filtre + (FIL1/2/3), NB, NR, ANF et — **en CW seulement** — l'**APF** (filtre de pic + audio). +- **Passe-bande / notch :** Twin PBT (intérieur / extérieur), notch manuel + + position. +- **Émission :** puissance RF, MOX, TUNE, **split avec offset automatique** + (+5 kHz en SSB, +1 kHz en CW), et moniteur. **En phonie seulement** : gain + micro, processeur de parole, VOX (+ gain + anti-VOX). Les commandes qui ne + s'appliquent pas au mode courant sont masquées automatiquement. +- **Bandes & antenne :** boutons de bande en un clic et sélection ANT1/ANT2. +- **Clarificateurs :** RIT et ΔTX avec accord molette / ± (Ctrl+←/→ décale le + RIT). +- Boutons **Marche / Arrêt** (manuels par choix — l'application ne réveille + jamais la radio à la connexion). +- La **manipulation CW** peut passer par le keyer intégré de la radio (voir + *Keyers* ci-dessous). + +### Icom à distance (par internet, sans RS-BA1) + +OpsLog parle directement le protocole réseau intégré de l'IC-7610 — il +**remplace à la fois l'Icom Remote Utility et RS-BA1**. Saisissez l'IP de la +radio, le nom/mot de passe Network User1 et l'adresse CI-V, et toute la console +Icom fonctionne sur le LAN/internet : login + token (renouvelé automatiquement), +tunnel CI-V, retransmission côté réception pour une liaison très stable même +avec le panadapter en flux, et Marche/Arrêt manuel. (L'audio est hors périmètre +— utilisez la radio en USB + une liaison voix comme Mumble.) + +## Keyers & audio + +- **Keyer CW** avec macros et macros sur touches F. Le moteur du keyer est + sélectionnable : **WinKeyer** (K1EL WK1/2/3 sur port COM), **Icom** (le keyer + intégré de la radio via CI-V — sans matériel supplémentaire, fonctionne aussi à + distance) ou **TCI**. +- **Keyer vocal numérique** (DVK) : enregistrer les messages vocaux F1–F6 et les + émettre. +- **Enregistrement audio des QSO :** capture continue en tampon glissant ; au + *Log QSO* le contact est sauvegardé dans un WAV par QSO + (`INDICATIF_AAAAMMJJ_HHMMSS.wav`) ; mixe RX + micro. + +## Amplis & commutateurs + +- Amplificateur **PowerGenius XL** (4O3A) — TCP direct : operate/standby, + sélecteur de mode ventilateur et affichage des défauts. +- Commutateur d'antenne **Antenna Genius** (4O3A) via TCP/GSCP — un widget de + commutation A/B ancré. + +## QSL & diplômes + +- **Moteur de diplômes :** définitions intégrées + personnalisées (partagées + **globalement** entre profils) — DXCC, WAS / WAZ / WAC, WPX, + IOTA / POTA / SOTA / WWFF, **DDFM**, contacté/confirmé/validé par bande & mode, + règles OU et affectation manuelle de références, détection de référence en + direct à la saisie de l'indicatif, **import de listes de références** pour les + totaux/noms, et un **Rescan** qui relit le journal (récupère les nouvelles + confirmations LoTW/QRZ). +- **Services QSL :** ClubLog (upload ADIF par lots), LoTW, QRZ.com, eQSL — + upload et **téléchargement des confirmations** (qui rafraîchit automatiquement + les stats de diplômes). +- **Concepteur de cartes QSL** (voir ci-dessous). +- **eQSL par e-mail :** clic droit sur un QSO → *Envoyer l'eQSL par e-mail* via + le compte SMTP configuré. (Outlook/Hotmail désactivent le SMTP basic-auth — + utilisez Gmail avec un mot de passe d'application, ou un mot de passe + d'application Microsoft.) + +## Log de concours + +- **Onglet Contest :** choisissez un concours (liste ADIF `CONTEST_ID` intégrée) + et un échange (numéro de série courant ou échange fixe). OpsLog remplit + automatiquement `CONTEST_ID` et les numéros émis/reçus (`STX` / `SRX`), impose + un début/fin de fenêtre, signale les doublons et tient un tableau de score en + direct. + +## Statut opérateur en direct (événements spéciaux) + +Pour un indicatif d'événement spécial multi-op sur un journal MySQL partagé (ex. +**TM74TFR**) : Réglages → Général → *Publier le statut opérateur en direct*. +Chaque instance OpsLog envoie un battement de cœur de son activité (indicatif de +l'opérateur, bande, fréquence, mode) dans une table `live_status` toutes les +~15 s. Un petit rendu PHP +([`docs/livestatus/tm74-status.php`](docs/livestatus/tm74-status.php)) sur votre +propre serveur web lit cette table et produit une page/image en direct que vous +pouvez intégrer sur la bio **QRZ.com** de la station +(``). OpsLog écrit seulement dans la base — +ce n'est pas un serveur web. + +## Net control + +- **Log de net dirigé** (Outils → Net) : un roster global (`nets.json`) plus une + session active en mémoire — pointez les stations présentes, puis loguez tout le + net d'un coup en utilisant la fréquence CAT. + +## Apparence & langue + +- **Thèmes :** quatre thèmes complets (Clair chaud, Sombre chaud, Graphite + sombre, Contraste élevé) plus **Auto** (suit la préférence clair/sombre du + système), sélectionnables dans Réglages → Général. Chaque panneau et chaque + table AG-Grid suit le thème. +- **Bilingue :** interface complète **anglais / français**, avec un choix de + drapeau au premier lancement et un sélecteur dans Réglages → Général. + +## Sécurité + +- **Coffre à secrets :** chiffrement optionnel par phrase de passe des mots de + passe stockés (AES-GCM + PBKDF2). Les valeurs chiffrées sont portables ; une + seule invite de déverrouillage au lancement les déchiffre pour la session. + +## Intégrations (sortantes) + +- **Émetteurs UDP :** pousser la fréquence actuelle vers **PstRotator**, les + infos radio au format **N1MM `RadioInfo`**, ou un **enregistrement ADIF à + chaque QSO logué** — pour que les outils externes (contrôle de rotor, + applications numériques, autres logiciels de log) restent synchronisés. + +## Divers + +- **Démarrage automatique :** lancer des programmes externes (WSJT-X, JTAlert, + contrôle de rotor…) au démarrage d'OpsLog, en sautant ceux déjà lancés. +- **Sauvegarde :** sauvegarde optionnelle base + ADIF à la fermeture. +- **Vérification de mise à jour** au démarrage avec un toast (désactivable). +- **Télémétrie d'usage anonyme** (un battement de cœur quotidien : ID + d'installation aléatoire + version + OS — aucune donnée d'indicatif ou de QSO ; + désactivable dans les Préférences). + +--- + +## Concepteur de cartes QSL + +Outils → *Concepteur de cartes QSL…* transforme quelques photos en une carte +eQSL soignée : + +1. Choisissez 1 à 6 photos (jpeg/png). OpsLog les analyse hors ligne (grille de + détail/luminance) et propose **3 designs** — indicatif dans la zone la plus + calme de la meilleure photo, nom de l'opérateur, zones CQ/ITU + ligne locator, + drapeau du pays, les autres photos en inserts encadrés, et un encart de + confirmation par QSO. +2. Choisissez une proposition et affinez-la : cliquez un élément pour le + sélectionner, glissez pour déplacer, changez la police / le préréglage de + style (gel or, gel argent, contour blanc classique, script, plat) et les + réglages par préréglage dans le panneau de droite. +3. Enregistrez le modèle (les photos sont copiées dans + `data/qsl/templates//`, les originaux peuvent donc être déplacés). Un + modèle peut être le défaut par profil. + +Envoi : clic droit sur un QSO → *Envoyer l'eQSL par e-mail*. La carte est rendue +avec les données de ce QSO, matricée en JPEG ≤ 800 Ko, archivée dans +`data/qsl/outbox/` et envoyée via le compte SMTP configuré à l'adresse trouvée +par la recherche QRZ/HamQTH. En cas de succès le QSO est marqué `EQSL_SENT=Y` +(ADIF). Les modèles de sujet/corps de l'e-mail sont dans le concepteur (variables +`{CALL} {DATE} {BAND} {MODE} {MYCALL}`). + +Polices : Archivo Black, Lilita One, Baloo 2, Oswald, Great Vibes, Allura +(toutes OFL, embarquées — licences dans `internal/qslcard/assets/fonts/`) ; +Cooper Black est proposée si MS Office l'a installée. Drapeaux : flag-icons +(MIT), embarqués pour les entités DXCC couramment contactées. + +--- + +## Données & stockage + +- La **config** (réglages, profils, radios/antennes, nœuds de cluster, cache de + recherche, listes de diplômes, modèles QSL) réside toujours dans le fichier + SQLite local sous `data/` — instantané même quand le journal est sur un MySQL + lointain. +- Le **journal** (QSO) réside là où pointe le profil actif : le fichier SQLite + local ou une base **MySQL** partagée par profil. + +--- + +*An English version of this document is available in [README.md](README.md).* diff --git a/README.md b/README.md index 5e213b2..9348787 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # OpsLog A modern, fast ham-radio logger for Windows — Log4OM-style entry, real-time CAT -(OmniRig **and** native FlexRadio/SmartSDR), DX cluster, awards tracking, maps, -QSL management and a QSL-card designer. Built with **Wails v2** (Go backend + +for **OmniRig**, native **FlexRadio/SmartSDR**, native **Icom CI-V** (USB **and** +remote-over-internet, replacing RS-BA1) and **TCI** (SunSDR / Expert Electronics), +DX cluster with spot alerts, awards tracking, maps, contest logging, QSL +management and a QSL-card designer. Built with **Wails v2** (Go backend + React/TypeScript frontend), **pure Go** (no CGO): SQLite for configuration, optional **shared MySQL** for the logbook so several operators can run one log. +Fully themeable and bilingual (English / French). Developed by **F4BPO**. @@ -30,10 +33,16 @@ Developed by **F4BPO**. - **Callsign lookup** (QRZ.com / HamQTH) with photo, auto-fill of name/QTH/grid and the QRZ.com tab. - **Offline DXCC** resolution from `cty.dat` (country, CQ/ITU zones, continent), - with `/MM` `/AM` and call-area (`/8`, `/W6`) handling, plus ClubLog DXpedition - date overrides. + with `/MM` `/AM` `/B` (beacon) and call-area (`/8`, `/W6`) handling, plus + ClubLog DXpedition date overrides. - **Recent QSOs**, **Worked-before** matrix (per band/mode slot), bulk re-resolve from cty/QRZ/ClubLog, bulk send to QSL services. +- **Advanced QSO filter builder** (field / operator / value, AND / OR, saved + presets) with filtered- and selected-row **ADIF export**. +- **Find duplicates** (Tools) — groups QSOs by same call + band + mode (optionally + same day / minute) and lets you pick which to delete. +- **ADIF 3.1.7 compliant** import/export: a full field dictionary, 30 promoted + columns, a generic "extra fields" editor and standard/all export modes. - **Profiles:** every setting is per-profile; each profile can point its logbook at the local SQLite file or a **shared MySQL** database (multi-operator). @@ -41,7 +50,8 @@ Developed by **F4BPO**. - **Main view = two configurable panes** (per profile, Settings → General → *Main view*): great-circle map, locator (street) map, the cluster grid, the - worked-before grid, or the **FlexRadio controls**. + worked-before grid, recent QSOs, the **FlexRadio controls**, the **Icom + console** or the **Net control** panel. - **Great-circle map** with short/long-path distance & azimuth, selectable basemaps (Light / Voyager / Street / Satellite, all key-free and labelled) and the **antenna beam lobe(s)** drawn from the rotor azimuth. @@ -58,15 +68,29 @@ Developed by **F4BPO**. pane, with a show/hide toggle. - Per-spot **status** (new / new-band / new-slot / worked), click-to-tune the rig, and a multi-band **Band Map** (panadapter-style strips). +- **POTA** spots are tagged with their park reference (via `api.pota.app`). +- **Spot alerts** (Log4OM-style): rules on call / country / band / mode / + spotter, with sound, visual and e-mail notification (Tools → *Alert + management*). ## CAT control -- **OmniRig** backend (Rig 1/2, hot-swap), and a native **FlexRadio (SmartSDR)** - backend over the radio's TCP API — real-time slice freq/mode/split, auto - reconnect, UDP discovery, and **panadapter spots** (cluster spots pushed to the - Flex display, click → fill the call). -- Mode is taken from the radio; the digital sub-mode (FT4 vs FT8) is inferred - from the frequency. +Four native backends (Settings → CAT), each with auto-reconnect and a fast, +non-blocking connect so a powered-off radio never freezes the app: + +- **OmniRig** (Rig 1/2, hot-swap) — works with any OmniRig-supported rig. +- **FlexRadio (SmartSDR)** over the radio's TCP API — real-time slice freq / + mode / split, UDP discovery, and **panadapter spots** (cluster spots pushed to + the Flex display, click → fill the call). +- **Icom CI-V** — native, over the radio's **USB** port *or* over the internet + via the radio's **built-in LAN server** (see *Remote Icom* below). No RS-BA1 or + Remote Utility needed. +- **TCI** (WebSocket) — SunSDR / ExpertSDR2 and any TCI-compatible server: + freq / mode / PTT / split, plus optional panorama spots. + +Mode is taken from the radio; the digital sub-mode (FT4 vs FT8) is inferred from +the frequency. **Per-band Flex RX/TX antennas** can be configured and are applied +automatically on band change. ### FlexRadio control tab (SmartSDR-style) @@ -80,19 +104,64 @@ Shown only when the CAT backend is a FlexRadio: - **Live meters** over the UDP VITA-49 stream: S-meter (S-units), forward power (W), SWR, ALC, PA temperature, voltage, plus the amplifier's meters. +### Icom control tab + +Shown when the CAT backend is Icom (USB or network). A full RS-BA1-style console: + +- **Twin VFO readout** (MAIN / SUB) with the big tabular frequency, mode badge, + band and RIT/ΔTX offset, and a **mode-button row** (SSB / CW / RTTY / PSK / + AM / FM). +- **Spectrum scope + waterfall** (panadapter): ON/OFF, CTR/FIX, double-click to + tune, and **◀ ⊙ ▶** buttons to centre the scope on the current frequency + (±50 kHz) and pan left/right. +- **Live meters** always visible: S-meter (click → fill RST), power in watts, SWR. +- **Receive DSP:** AF / RF gain, squelch, AGC, preamp, attenuator, filter + (FIL1/2/3), NB, NR, ANF and — **on CW only** — the **APF** (audio peak filter). +- **Passband / notch:** Twin PBT (inner / outer), manual notch + position. +- **Transmit:** RF power, MOX, TUNE, **split with an automatic offset** + (+5 kHz on SSB, +1 kHz on CW), and monitor. On **voice modes only**: mic gain, + speech compressor, VOX (+ gain + anti-VOX). Controls that don't apply to the + current mode are hidden automatically. +- **Bands & antenna:** one-touch band buttons and ANT1/ANT2 selection. +- **Clarifiers:** RIT and ΔTX with wheel / ± tuning (Ctrl+←/→ nudges RIT). +- **Power ON / OFF** buttons (manual by design — the app never wakes the rig on + connect). +- **CW keying** can run through the radio's own keyer (see *Keyers* below). + +### Remote Icom (over the internet, no RS-BA1) + +OpsLog speaks the IC-7610's built-in network protocol directly — it **replaces +both the Icom Remote Utility and RS-BA1**. Enter the radio's IP, the Network +User1 name/password and the CI-V address, and the whole Icom console works over +the LAN/internet: login + token (auto-renewed), CI-V tunnel, receive-side +retransmit for a rock-solid link even with the panadapter streaming, and manual +power ON/OFF. (Audio is out of scope — use the radio in USB + a voice link such +as Mumble.) + ## Keyers & audio -- **WinKeyer** CW keyer (macros, F-key macros, auto-call repeat). -- **Digital Voice Keyer** (DVK) message playback. -- **QSO audio recording** (SSB/DAX) archived per QSO; disabled for CW (no DAX - audio in CW). +- **CW keyer** with macros and F-key macros. The keyer engine is selectable: + **WinKeyer** (K1EL WK1/2/3 over a COM port), **Icom** (the radio's own keyer + over CI-V — no extra hardware, works over the remote link too) or **TCI**. +- **Digital Voice Keyer** (DVK): record F1–F6 voice messages and transmit them. +- **QSO audio recording:** continuous rolling capture; on *Log QSO* the contact + is saved to a per-QSO WAV (`CALL_YYYYMMDD_HHMMSS.wav`); mixes RX + mic. + +## Amplifiers & switches + +- **PowerGenius XL** (4O3A) amplifier — direct TCP: operate/standby, fan-mode + selector and fault display. +- **Antenna Genius** (4O3A) antenna switch over TCP/GSCP — a docked A/B + antenna-switch widget. ## QSL & awards - **Awards engine:** built-in + custom award definitions (shared **globally** - across profiles), worked/confirmed/validated by band & mode, OR rules and - manual reference assignment, live reference detection on call entry, and a - **Rescan** that re-pulls the logbook (picks up fresh LoTW/QRZ confirmations). + across profiles) — DXCC, WAS / WAZ / WAC, WPX, IOTA / POTA / SOTA / WWFF, + **DDFM**, worked/confirmed/validated by band & mode, OR rules and manual + reference assignment, live reference detection on call entry, **reference-list + import** for totals/names, and a **Rescan** that re-pulls the logbook (picks up + fresh LoTW/QRZ confirmations). - **QSL services:** ClubLog (batched ADIF upload), LoTW, QRZ.com, eQSL — upload and **confirmation download** (which auto-refreshes the award stats). - **QSL Card Designer** (see below). @@ -100,6 +169,13 @@ Shown only when the CAT backend is a FlexRadio: SMTP account. (Outlook/Hotmail disable basic-auth SMTP — use Gmail with an app password, or a Microsoft app password.) +## Contest logging + +- **Contest tab:** pick a contest (built-in ADIF `CONTEST_ID` list) and an + exchange (running serial or a fixed exchange). OpsLog auto-fills `CONTEST_ID` + and the sent/received serials (`STX` / `SRX`), enforces a window start/end, + flags dupes and keeps a live scoreboard. + ## Multi-operator live status (special events) For a multi-op special-event call on a shared MySQL logbook (e.g. **TM74TFR**): @@ -111,10 +187,37 @@ own web server reads that table and produces a live page/image you can embed on the station's **QRZ.com** bio (``). OpsLog only writes to the DB — it is not a web server. +## Net control + +- **Directed-net logging** (Tools → Net): a global roster (`nets.json`) plus an + in-memory active session — check stations in, then log the whole net at once + using the CAT frequency. + +## Appearance & language + +- **Themes:** four complete themes (Warm light, Warm dark, Graphite dark, High + contrast) plus **Auto** (follows the OS light/dark preference), selectable in + Settings → General. Every panel and every AG-Grid table follows the theme. +- **Bilingual:** full **English / French** UI, with a first-run flag chooser and + a switcher in Settings → General. + +## Security + +- **Secret vault:** opt-in passphrase encryption of the stored passwords + (AES-GCM + PBKDF2). Encrypted values are portable; a single unlock prompt at + launch decrypts them for the session. + +## Integrations (outbound) + +- **UDP emitters:** push the current frequency to **PstRotator**, radio info in + **N1MM `RadioInfo`** format, or an **ADIF record on each logged QSO** — so + external tools (rotator control, digital apps, other loggers) stay in sync. + ## Other - **Autostart:** launch external programs (WSJT-X, JTAlert, rotator control…) at OpsLog startup, skipping any already running. +- **Backup:** optional database + ADIF backup at shutdown. - **Update check** at startup with a toast (toggleable). - **Anonymous usage telemetry** (a once-a-day heartbeat: random install ID + version + OS — no callsign or QSO data; opt-out in Preferences). @@ -156,3 +259,7 @@ commonly-worked DXCC entities. `data/` — instant even when the logbook is on a far-away MySQL. - **Logbook** (QSOs) lives where the active profile points it: the local SQLite file or a per-profile shared **MySQL** database. + +--- + +*A French version of this document is available in [README.fr.md](README.fr.md).* diff --git a/app.go b/app.go index 08990dc..84037f5 100644 --- a/app.go +++ b/app.go @@ -7843,6 +7843,13 @@ func (a *App) IcomSetANF(on bool) error { return a.cat.IcomDo(func(ic cat.IcomController) error { return ic.SetANF(on) }) } +func (a *App) IcomSetAPF(on bool) error { + if a.cat == nil { + return fmt.Errorf("cat not initialized") + } + return a.cat.IcomDo(func(ic cat.IcomController) error { return ic.SetAPF(on) }) +} + func (a *App) IcomSetAGC(mode string) error { if a.cat == nil { return fmt.Errorf("cat not initialized") diff --git a/frontend/src/components/IcomPanel.tsx b/frontend/src/components/IcomPanel.tsx index 4334868..6de30cb 100644 --- a/frontend/src/components/IcomPanel.tsx +++ b/frontend/src/components/IcomPanel.tsx @@ -3,7 +3,7 @@ import { Radio, AudioLines, Mic, Activity, SlidersHorizontal, Antenna, Filter, P import { GetIcomState, IcomRefresh, IcomSetAFGain, IcomSetRFGain, IcomSetNB, IcomSetNBLevel, IcomSetNR, IcomSetNRLevel, - IcomSetANF, IcomSetAGC, IcomSetPreamp, IcomSetAtt, IcomSetFilter, + IcomSetANF, IcomSetAPF, IcomSetAGC, IcomSetPreamp, IcomSetAtt, IcomSetFilter, IcomSetRFPower, IcomSetMicGain, IcomSetSplit, IcomTune, IcomSetPTT, IcomSetScope, IcomScopeData, IcomSetScopeMode, IcomSetScopeEdges, GetCATState, SetCATFrequency, SetCATMode, IcomSetRIT, IcomSetRITOn, IcomSetXITOn, @@ -21,7 +21,7 @@ type IcomState = { s_meter: number; power_meter: number; swr_meter: number; rf_power: number; mic_gain: number; af_gain: number; rf_gain: number; - nb: boolean; nb_level: number; nr: boolean; nr_level: number; anf: boolean; + nb: boolean; nb_level: number; nr: boolean; nr_level: number; anf: boolean; apf: boolean; agc?: string; preamp: number; att: number; filter: number; rit_hz: number; rit_on: boolean; xit_on: boolean; antenna: number; @@ -35,7 +35,7 @@ const ZERO: IcomState = { available: false, transmitting: false, split: false, s_meter: 0, power_meter: 0, swr_meter: 0, rf_power: 0, mic_gain: 0, af_gain: 0, rf_gain: 0, - nb: false, nb_level: 0, nr: false, nr_level: 0, anf: false, + nb: false, nb_level: 0, nr: false, nr_level: 0, anf: false, apf: false, preamp: 0, att: 0, filter: 1, rit_hz: 0, rit_on: false, xit_on: false, antenna: 1, @@ -615,6 +615,11 @@ export function IcomPanel({ onReportRST }: { onReportRST?: (rst: string) => void const mainHz: number = split ? (cat?.freq_rx_hz || 0) : (cat?.freq_hz || 0); const subHz: number = split ? (cat?.freq_hz || 0) : 0; const curMode: string = cat?.mode || st.mode || ''; + // Mode-dependent controls: VOX / speech-comp / mic are voice-only (hidden on + // CW and data); APF (audio peak filter) is CW-only. Fold USB/LSB into phone. + const um = curMode.toUpperCase(); + const isCW = um === 'CW' || um === 'CWR'; + const isPhone = um === 'SSB' || um === 'USB' || um === 'LSB' || um === 'AM' || um === 'FM'; return (
@@ -735,10 +740,12 @@ export function IcomPanel({ onReportRST }: { onReportRST?: (rst: string) => void set({ rf_power: v }, () => IcomSetRFPower(v))} /> {st.rf_power} - - set({ mic_gain: v }, () => IcomSetMicGain(v))} /> - {st.mic_gain} - + {isPhone && ( + + set({ mic_gain: v }, () => IcomSetMicGain(v))} /> + {st.mic_gain} + + )}
- {/* Speech processor / monitor / VOX. */} + {/* Monitor (all modes) + speech processor / VOX (voice modes only — + they don't exist on CW or data). */}
- set({ comp: !st.comp }, () => IcomSetComp(!st.comp))} - onLevel={(v) => set({ comp_level: v }, () => IcomSetCompLevel(v))} /> set({ monitor: !st.monitor }, () => IcomSetMonitor(!st.monitor))} onLevel={(v) => set({ mon_level: v }, () => IcomSetMonLevel(v))} /> - set({ vox: !st.vox }, () => IcomSetVOX(!st.vox))} - onLevel={(v) => set({ vox_gain: v }, () => IcomSetVOXGain(v))} /> - - set({ anti_vox: v }, () => IcomSetAntiVOX(v))} /> - {st.anti_vox} - + {isPhone && ( + <> + set({ comp: !st.comp }, () => IcomSetComp(!st.comp))} + onLevel={(v) => set({ comp_level: v }, () => IcomSetCompLevel(v))} /> + set({ vox: !st.vox }, () => IcomSetVOX(!st.vox))} + onLevel={(v) => set({ vox_gain: v }, () => IcomSetVOXGain(v))} /> + + set({ anti_vox: v }, () => IcomSetAntiVOX(v))} /> + {st.anti_vox} + + + )}
@@ -837,6 +849,13 @@ export function IcomPanel({ onReportRST }: { onReportRST?: (rst: string) => void set({ anf: !st.anf }, () => IcomSetANF(!st.anf))} /> {t('icmp.autoNotch')}
+ {/* APF (audio peak filter) — CW only: peaks the CW tone. */} + {isCW && ( +
+ set({ apf: !st.apf }, () => IcomSetAPF(!st.apf))} /> + {t('icmp.apf')} +
+ )} diff --git a/frontend/src/lib/i18n.tsx b/frontend/src/lib/i18n.tsx index cd3eb26..ba048f3 100644 --- a/frontend/src/lib/i18n.tsx +++ b/frontend/src/lib/i18n.tsx @@ -193,7 +193,7 @@ const en: Dict = { 'agp.portDeselect': 'Port {letter} — click to deselect', 'agp.portSelect': 'Select on port {letter}', 'agp.online': 'online', 'agp.offline': 'offline', 'agp.close': 'Close', 'agp.connecting': 'Connecting…', 'agp.noAntennas': 'No antennas configured.', 'flxp.smartsdrRemote': 'SmartSDR remote control', 'flxp.offline': 'OFFLINE', 'flxp.waiting': 'Waiting for the FlexRadio… (set CAT to FlexRadio and connect)', 'flxp.transmit': 'Transmit', 'flxp.rfPower': 'RF Power', 'flxp.tunePwr': 'Tune Pwr', 'flxp.splitHint': 'Split: RX/TX on separate slices. ON creates a TX slice +1 kHz (CW) / +5 kHz (SSB) up, like SmartSDR.', 'flxp.voxDly': 'VOX Dly', 'flxp.speed': 'Speed', 'flxp.pitch': 'Pitch', 'flxp.delay': 'Delay', 'flxp.receiveActive': 'Receive (active slice)', 'flxp.muted': 'Muted — click to unmute', 'flxp.mute': 'Mute RX audio', 'flxp.filter': 'Filter', 'flxp.amplifier': 'Amplifier', 'flxp.ampInLine': 'Amplifier is in line (transmitting through PA).', 'flxp.ampBypassed': 'Amplifier bypassed (standby).', 'flxp.pgConnected': 'PowerGenius connected', 'flxp.pgOffline': 'PowerGenius offline', 'flxp.fan': 'Fan', 'flxp.fanStandard': 'Standard', 'flxp.fanContest': 'Contest', 'flxp.fanBroadcast': 'Broadcast', 'flxp.fault': 'FAULT', 'flxp.meters': 'Meters', 'flxp.noMeters': "No meters yet — waiting for the radio's UDP stream…", 'flxp.amplifierHdr': 'AMPLIFIER', - 'icmp.spectrum': 'Spectrum', 'icmp.scopeFixed': 'Fixed — double-click / wheel to tune', 'icmp.scopeCenter': 'Center — follows VFO', 'icmp.scopeOff': 'Scope off', 'icmp.scopePanDown': 'Shift scope −50 kHz', 'icmp.scopePanUp': 'Shift scope +50 kHz', 'icmp.scopeCenterVfo': 'Center scope on the current frequency (±50 kHz)', 'icmp.notConnected': "Icom not connected. Enable the Icom CI-V backend in Settings → CAT and connect the radio's USB port.", 'icmp.refresh': 'Refresh', 'icmp.meters': 'Meters', 'icmp.transmit': 'Transmit', 'icmp.power': 'Power', 'icmp.mic': 'Mic', 'icmp.receive': 'Receive', 'icmp.preamp': 'Preamp', 'icmp.filter': 'Filter', 'icmp.noiseNotch': 'Noise / Notch', 'icmp.autoNotch': 'Auto notch filter', 'icmp.clarifiers': 'RIT / ΔTX', 'icmp.ritHint': 'Wheel or ± to shift · Ctrl+←/→ shifts RIT when active', 'icmp.bandsAntenna': 'Bands & Antenna', 'icmp.antenna': 'Antenna', 'icmp.passband': 'Passband / Notch', 'icmp.pbtCenter': 'Center PBT', 'icmp.manualNotch': 'Manual notch — MN on, then set position', 'icmp.squelch': 'Squelch', 'icmp.powerOnHint': 'Power the radio ON (boots ~15 s)', 'icmp.powerOffHint': 'Power the radio OFF', 'icmp.powerOffConfirm': 'Switch the radio OFF?', + 'icmp.spectrum': 'Spectrum', 'icmp.scopeFixed': 'Fixed — double-click / wheel to tune', 'icmp.scopeCenter': 'Center — follows VFO', 'icmp.scopeOff': 'Scope off', 'icmp.scopePanDown': 'Shift scope −50 kHz', 'icmp.scopePanUp': 'Shift scope +50 kHz', 'icmp.scopeCenterVfo': 'Center scope on the current frequency (±50 kHz)', 'icmp.notConnected': "Icom not connected. Enable the Icom CI-V backend in Settings → CAT and connect the radio's USB port.", 'icmp.refresh': 'Refresh', 'icmp.meters': 'Meters', 'icmp.transmit': 'Transmit', 'icmp.power': 'Power', 'icmp.mic': 'Mic', 'icmp.receive': 'Receive', 'icmp.preamp': 'Preamp', 'icmp.filter': 'Filter', 'icmp.noiseNotch': 'Noise / Notch', 'icmp.autoNotch': 'Auto notch filter', 'icmp.apf': 'Audio peak filter (CW)', 'icmp.clarifiers': 'RIT / ΔTX', 'icmp.ritHint': 'Wheel or ± to shift · Ctrl+←/→ shifts RIT when active', 'icmp.bandsAntenna': 'Bands & Antenna', 'icmp.antenna': 'Antenna', 'icmp.passband': 'Passband / Notch', 'icmp.pbtCenter': 'Center PBT', 'icmp.manualNotch': 'Manual notch — MN on, then set position', 'icmp.squelch': 'Squelch', 'icmp.powerOnHint': 'Power the radio ON (boots ~15 s)', 'icmp.powerOffHint': 'Power the radio OFF', 'icmp.powerOffConfirm': 'Switch the radio OFF?', 'rst.clickToFill': 'Click to set RST tx from the signal', 'qrz.openTitle': 'Open {call} on QRZ.com', // Misc panels/modals (alerts / send-spot / net / udp / filter / details) @@ -379,7 +379,7 @@ const fr: Dict = { 'agp.portDeselect': 'Port {letter} — clic pour désélectionner', 'agp.portSelect': 'Sélectionner sur le port {letter}', 'agp.online': 'en ligne', 'agp.offline': 'hors ligne', 'agp.close': 'Fermer', 'agp.connecting': 'Connexion…', 'agp.noAntennas': 'Aucune antenne configurée.', 'flxp.smartsdrRemote': 'Contrôle à distance SmartSDR', 'flxp.offline': 'HORS LIGNE', 'flxp.waiting': 'En attente du FlexRadio… (règle le CAT sur FlexRadio et connecte)', 'flxp.transmit': 'Émission', 'flxp.rfPower': 'Puissance RF', 'flxp.tunePwr': 'Puiss. TUNE', 'flxp.splitHint': 'Split : RX/TX sur des slices séparées. ON crée une slice TX +1 kHz (CW) / +5 kHz (SSB) au-dessus, comme SmartSDR.', 'flxp.voxDly': 'Délai VOX', 'flxp.speed': 'Vitesse', 'flxp.pitch': 'Tonalité', 'flxp.delay': 'Délai', 'flxp.receiveActive': 'Réception (slice active)', 'flxp.muted': 'Coupé — clic pour rétablir', 'flxp.mute': "Couper l'audio RX", 'flxp.filter': 'Filtre', 'flxp.amplifier': 'Amplificateur', 'flxp.ampInLine': 'Amplificateur en ligne (émission via le PA).', 'flxp.ampBypassed': 'Amplificateur en bypass (standby).', 'flxp.pgConnected': 'PowerGenius connecté', 'flxp.pgOffline': 'PowerGenius hors ligne', 'flxp.fan': 'Ventilo', 'flxp.fanStandard': 'Standard', 'flxp.fanContest': 'Contest', 'flxp.fanBroadcast': 'Diffusion', 'flxp.fault': 'DÉFAUT', 'flxp.meters': 'Mesures', 'flxp.noMeters': 'Aucune mesure — en attente du flux UDP de la radio…', 'flxp.amplifierHdr': 'AMPLIFICATEUR', - 'icmp.spectrum': 'Spectre', 'icmp.scopeFixed': 'Fixe — double-clic / molette pour accorder', 'icmp.scopeCenter': 'Centré — suit le VFO', 'icmp.scopeOff': 'Scope éteint', 'icmp.scopePanDown': 'Décaler le scope −50 kHz', 'icmp.scopePanUp': 'Décaler le scope +50 kHz', 'icmp.scopeCenterVfo': 'Centrer le scope sur la fréquence actuelle (±50 kHz)', 'icmp.notConnected': 'Icom non connecté. Active le backend CI-V Icom dans Réglages → CAT et connecte le port USB de la radio.', 'icmp.refresh': 'Rafraîchir', 'icmp.meters': 'Mesures', 'icmp.transmit': 'Émission', 'icmp.power': 'Puissance', 'icmp.mic': 'Micro', 'icmp.receive': 'Réception', 'icmp.preamp': 'Préampli', 'icmp.filter': 'Filtre', 'icmp.noiseNotch': 'Bruit / Notch', 'icmp.autoNotch': 'Filtre notch auto', 'icmp.clarifiers': 'RIT / ΔTX', 'icmp.ritHint': 'Molette ou ± pour décaler · Ctrl+←/→ décale le RIT si actif', 'icmp.bandsAntenna': 'Bandes & Antenne', 'icmp.antenna': 'Antenne', 'icmp.passband': 'Passe-bande / Notch', 'icmp.pbtCenter': 'Centrer PBT', 'icmp.manualNotch': 'Notch manuel — active MN, puis règle la position', 'icmp.squelch': 'Squelch', 'icmp.powerOnHint': 'Allumer la radio (démarre en ~15 s)', 'icmp.powerOffHint': 'Éteindre la radio', 'icmp.powerOffConfirm': 'Éteindre la radio ?', + 'icmp.spectrum': 'Spectre', 'icmp.scopeFixed': 'Fixe — double-clic / molette pour accorder', 'icmp.scopeCenter': 'Centré — suit le VFO', 'icmp.scopeOff': 'Scope éteint', 'icmp.scopePanDown': 'Décaler le scope −50 kHz', 'icmp.scopePanUp': 'Décaler le scope +50 kHz', 'icmp.scopeCenterVfo': 'Centrer le scope sur la fréquence actuelle (±50 kHz)', 'icmp.notConnected': 'Icom non connecté. Active le backend CI-V Icom dans Réglages → CAT et connecte le port USB de la radio.', 'icmp.refresh': 'Rafraîchir', 'icmp.meters': 'Mesures', 'icmp.transmit': 'Émission', 'icmp.power': 'Puissance', 'icmp.mic': 'Micro', 'icmp.receive': 'Réception', 'icmp.preamp': 'Préampli', 'icmp.filter': 'Filtre', 'icmp.noiseNotch': 'Bruit / Notch', 'icmp.autoNotch': 'Filtre notch auto', 'icmp.apf': 'Filtre de pic audio (CW)', 'icmp.clarifiers': 'RIT / ΔTX', 'icmp.ritHint': 'Molette ou ± pour décaler · Ctrl+←/→ décale le RIT si actif', 'icmp.bandsAntenna': 'Bandes & Antenne', 'icmp.antenna': 'Antenne', 'icmp.passband': 'Passe-bande / Notch', 'icmp.pbtCenter': 'Centrer PBT', 'icmp.manualNotch': 'Notch manuel — active MN, puis règle la position', 'icmp.squelch': 'Squelch', 'icmp.powerOnHint': 'Allumer la radio (démarre en ~15 s)', 'icmp.powerOffHint': 'Éteindre la radio', 'icmp.powerOffConfirm': 'Éteindre la radio ?', 'rst.clickToFill': 'Clic pour remplir le RST tx depuis le signal', 'qrz.openTitle': 'Ouvrir {call} sur QRZ.com', 'altm.filterPh': 'Filtrer…', 'altm.noMatch': 'aucun résultat', 'altm.noneAll': 'aucune sélection = TOUT', 'altm.nSelected': '{n} sélectionné(s)', 'altm.giveName': 'Donne un nom à la règle', 'altm.deleteConfirm': "Supprimer l'alerte « {name} » ?", 'altm.title': 'Gestion des alertes', 'altm.desc': 'Alerte quand un spot correspond à une règle. Filtres vides = TOUT ; les filtres définis sont combinés par ET (ex. France + 20m = stations françaises sur 20m).', 'altm.rules': 'Règles', 'altm.noRules': 'Aucune règle — clique sur +', 'altm.emailTo': "E-mail d'alerte à", 'altm.selectOrCreate': 'Sélectionne ou crée une règle.', 'altm.tabDef': 'Définition', 'altm.tabCall': 'Indicatif / DXCC', 'altm.tabBandMode': 'Bande / Mode', 'altm.tabOrigin': 'Origine', 'altm.ruleName': 'Nom de la règle', 'altm.alertEnabled': 'Alerte activée', 'altm.againAfter': 'Réalerter après (min)', 'altm.againHint': '0 = une fois/session · -1 = toujours', 'altm.actions': 'Actions', 'altm.visual': 'Visuel', 'altm.sound': 'Son', 'altm.email': 'E-mail', 'altm.skipWorked': 'Ignorer les indicatifs déjà contactés (même bande + mode)', 'altm.callsigns': 'Indicatifs (un par ligne, jokers : IW3*, */P)', 'altm.countries': 'Pays (DXCC)', 'altm.continents': 'Continents', 'altm.bands': 'Bandes', 'altm.modes': 'Modes', 'altm.spotterCall': 'Indicatif du spotteur (joker)', 'altm.spotterCallPh': 'ex. F* ou DL1ABC', 'altm.spotterContinents': 'Continents du spotteur', 'altm.spotterCountries': 'Pays du spotteur', 'altm.delete': 'Supprimer', 'altm.saveRule': 'Enregistrer la règle', 'altm.close': 'Fermer', diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts index 64116fb..77cda9d 100644 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -350,6 +350,8 @@ export function IcomSetAGC(arg1:string):Promise; export function IcomSetANF(arg1:boolean):Promise; +export function IcomSetAPF(arg1:boolean):Promise; + export function IcomSetAntenna(arg1:number):Promise; export function IcomSetAntiVOX(arg1:number):Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index 28b7f93..93bdceb 100644 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -662,6 +662,10 @@ export function IcomSetANF(arg1) { return window['go']['main']['App']['IcomSetANF'](arg1); } +export function IcomSetAPF(arg1) { + return window['go']['main']['App']['IcomSetAPF'](arg1); +} + export function IcomSetAntenna(arg1) { return window['go']['main']['App']['IcomSetAntenna'](arg1); } diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts index f18c82a..59c6aa8 100644 --- a/frontend/wailsjs/go/models.ts +++ b/frontend/wailsjs/go/models.ts @@ -690,6 +690,7 @@ export namespace cat { nr: boolean; nr_level: number; anf: boolean; + apf: boolean; agc?: string; preamp: number; att: number; @@ -736,6 +737,7 @@ export namespace cat { this.nr = source["nr"]; this.nr_level = source["nr_level"]; this.anf = source["anf"]; + this.apf = source["apf"]; this.agc = source["agc"]; this.preamp = source["preamp"]; this.att = source["att"]; diff --git a/internal/cat/cat.go b/internal/cat/cat.go index b2649df..e380643 100644 --- a/internal/cat/cat.go +++ b/internal/cat/cat.go @@ -415,6 +415,7 @@ type IcomTXState struct { NR bool `json:"nr"` NRLevel int `json:"nr_level"` ANF bool `json:"anf"` + APF bool `json:"apf"` // audio peak filter (CW only) AGC string `json:"agc,omitempty"` // FAST | MID | SLOW Preamp int `json:"preamp"` // 0=off, 1=P.AMP1, 2=P.AMP2 Att int `json:"att"` // dB attenuation, 0=off @@ -450,6 +451,7 @@ type IcomController interface { SetNR(bool) error SetNRLevel(int) error SetANF(bool) error + SetAPF(bool) error SetAGC(string) error SetPreamp(int) error SetAtt(int) error diff --git a/internal/cat/civ/civ.go b/internal/cat/civ/civ.go index 16faae7..9c23b94 100644 --- a/internal/cat/civ/civ.go +++ b/internal/cat/civ/civ.go @@ -110,6 +110,7 @@ const ( SubSwVOX = 0x46 // VOX on/off SubSwBreakIn = 0x47 // CW break-in: 0=OFF, 1=SEMI, 2=FULL (needed so 0x17 CW keys TX) SubSwMN = 0x48 // manual notch on/off + SubSwAPF = 0x32 // audio peak filter on/off (CW only) ) // CW break-in modes (CmdSwitch 0x47). diff --git a/internal/cat/icomserial.go b/internal/cat/icomserial.go index 2c03eaf..53bb69b 100644 --- a/internal/cat/icomserial.go +++ b/internal/cat/icomserial.go @@ -1166,6 +1166,9 @@ func (b *IcomSerial) readDSP() { if v, ok := b.readSwitch(civ.SubSwANF); ok { st.ANF = v != 0 } + if v, ok := b.readSwitch(civ.SubSwAPF); ok { + st.APF = v != 0 + } if v, ok := b.readSwitch(civ.SubSwAGC); ok { st.AGC = agcName(v) } @@ -1357,6 +1360,14 @@ func (b *IcomSerial) SetANF(on bool) error { return nil } +func (b *IcomSerial) SetAPF(on bool) error { + if err := b.exec(civ.CmdSwitch, civ.SubSwAPF, boolByte(on)); err != nil { + return err + } + b.setCache(func(s *IcomTXState) { s.APF = on }) + return nil +} + func (b *IcomSerial) SetAGC(name string) error { v := agcValue(name) if v == 0 {