fix(editor): a corrected frequency corrects its band
Band and frequency have to agree — the log, every award and every upload are read on the BAND — and an operator fixing a wrong frequency is not also expecting to fix the band by hand. That is exactly how a QSO ends up filed on 20m at 7 MHz. Both sides follow, TX and RX. Only when the number lands in a known allocation, because half a frequency is typed on the way to all of it and a band must never be blanked by that. bandForMHz moves out of App.tsx into lib/bandplan, where the two callers can share one answer instead of drifting.
This commit is contained in:
+1
-15
@@ -59,6 +59,7 @@ import { Combobox } from '@/components/ui/combobox';
|
||||
import { applyAwardRefs, parseAwardRefs as parseManualRefs, spotRefList , withIOTARef, withRDARef } from '@/lib/awardRefs';
|
||||
import { ListRadios, SetActiveRadio } from '../wailsjs/go/main/App';
|
||||
import { formatDistance } from '@/lib/units';
|
||||
import { bandForMHz } from '@/lib/bandplan';
|
||||
import { EventsOn, BrowserOpenURL, WindowMinimise, WindowToggleMaximise, WindowIsMaximised, Quit } from '../wailsjs/runtime/runtime';
|
||||
import type { adif as adifModels, lookup as lookupModels, cat as catModels } from '../wailsjs/go/models';
|
||||
import type { QSOForm, WorkedBeforeView, StationSettingsForm, ListsSettingsForm, ModePresetForm } from '@/types';
|
||||
@@ -432,21 +433,6 @@ function entryQSYCommand(text: string): { band?: string; hz?: number } | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
function bandForMHz(mhz: number): string {
|
||||
if (!mhz || isNaN(mhz)) return '';
|
||||
const plan: [number, number, string][] = [
|
||||
[1.8, 2.0, '160m'], [3.5, 4.0, '80m'], [5.06, 5.45, '60m'], [7.0, 7.3, '40m'],
|
||||
[10.1, 10.15, '30m'], [14.0, 14.35, '20m'], [18.068, 18.168, '17m'], [21.0, 21.45, '15m'],
|
||||
[24.89, 24.99, '12m'], [28.0, 29.7, '10m'], [50, 54, '6m'], [70, 71, '4m'],
|
||||
[144, 148, '2m'], [222, 225, '1.25m'], [420, 450, '70cm'], [902, 928, '33cm'], [1240, 1300, '23cm'],
|
||||
// Microwave, ADIF 3.1.7 ranges — kept in step with BandFromHz on the Go side.
|
||||
[2300, 2450, '13cm'], [3300, 3500, '9cm'], [5650, 5925, '6cm'], [10000, 10500, '3cm'],
|
||||
[24000, 24250, '1.25cm'], [47000, 47200, '6mm'], [75500, 81000, '4mm'],
|
||||
[119980, 123000, '2.5mm'], [134000, 149000, '2mm'], [241000, 250000, '1mm'],
|
||||
];
|
||||
for (const [lo, hi, b] of plan) if (mhz >= lo && mhz <= hi) return b;
|
||||
return '';
|
||||
}
|
||||
|
||||
// modeAccent maps a mode to a theme-aware colour for the live-stations widget:
|
||||
// CW gold, phone green, digital blue, unknown muted.
|
||||
|
||||
@@ -22,6 +22,7 @@ import { Combobox } from '@/components/ui/combobox';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { flagURL } from '@/lib/flags';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { bandForMHz } from '@/lib/bandplan';
|
||||
import { titleCase, sentenceCase } from '@/lib/textCase';
|
||||
import type { QSOForm } from '@/types';
|
||||
|
||||
@@ -312,6 +313,19 @@ export function QSOEditModal({ qso, onSave, onDelete, onClose, countries = [], b
|
||||
const splitHz = (hz?: number) => hz
|
||||
? { khz: String(Math.floor(hz / 1000)), hz: String(hz % 1000).padStart(3, '0') }
|
||||
: { khz: '', hz: '' };
|
||||
// Correcting a frequency corrects its band. The pair has to agree — the log,
|
||||
// every award and every upload are read on the BAND — and an operator fixing
|
||||
// a wrong frequency is not also expecting to fix the band by hand, which is
|
||||
// exactly how a QSO ends up filed on 20m at 7 MHz.
|
||||
//
|
||||
// Only when the number lands in a known allocation: half a frequency is typed
|
||||
// on the way to all of it, and a band must never be blanked by that.
|
||||
const syncBand = (khz: string, hz: string, field: 'band' | 'band_rx') => {
|
||||
if (!khz.trim()) return;
|
||||
const b = bandForMHz((parseInt(khz, 10) * 1000 + (parseInt(hz, 10) || 0)) / 1_000_000);
|
||||
if (b) set(field, b as any);
|
||||
};
|
||||
|
||||
const f0 = splitHz(draft.freq_hz);
|
||||
const fr0 = splitHz(draft.freq_rx_hz);
|
||||
const [freqKHz, setFreqKHz] = useState(f0.khz);
|
||||
@@ -634,13 +648,13 @@ export function QSOEditModal({ qso, onSave, onDelete, onClose, countries = [], b
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="w-20 shrink-0">{t('qedit.txFreq')}</Label>
|
||||
<Input value={freqKHz} onChange={(e) => setFreqKHz(e.target.value.replace(/\D/g, ''))} className="font-mono w-24" placeholder="kHz" />
|
||||
<Input value={freqHz} onChange={(e) => setFreqHz(e.target.value.replace(/\D/g, ''))} maxLength={3} className="font-mono w-16" placeholder="Hz" />
|
||||
<Input value={freqKHz} onChange={(e) => { const v = e.target.value.replace(/\D/g, ''); setFreqKHz(v); syncBand(v, freqHz, 'band'); }} className="font-mono w-24" placeholder="kHz" />
|
||||
<Input value={freqHz} onChange={(e) => { const v = e.target.value.replace(/\D/g, ''); setFreqHz(v); syncBand(freqKHz, v, 'band'); }} maxLength={3} className="font-mono w-16" placeholder="Hz" />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="w-20 shrink-0">{t('qedit.rxFreq')}</Label>
|
||||
<Input value={freqRxKHz} onChange={(e) => setFreqRxKHz(e.target.value.replace(/\D/g, ''))} className="font-mono w-24" placeholder="kHz" />
|
||||
<Input value={freqRxHz} onChange={(e) => setFreqRxHz(e.target.value.replace(/\D/g, ''))} maxLength={3} className="font-mono w-16" placeholder="Hz" />
|
||||
<Input value={freqRxKHz} onChange={(e) => { const v = e.target.value.replace(/\D/g, ''); setFreqRxKHz(v); syncBand(v, freqRxHz, 'band_rx'); }} className="font-mono w-24" placeholder="kHz" />
|
||||
<Input value={freqRxHz} onChange={(e) => { const v = e.target.value.replace(/\D/g, ''); setFreqRxHz(v); syncBand(freqRxKHz, v, 'band_rx'); }} maxLength={3} className="font-mono w-16" placeholder="Hz" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -111,3 +111,26 @@ export function bandRange(band: string): [number, number] | undefined {
|
||||
export function bandSegments(band: string): Seg[] {
|
||||
return plan().segments[band] ?? [];
|
||||
}
|
||||
|
||||
// bandForMHz maps a dial frequency (MHz) to its ADIF band, or '' when it falls
|
||||
// outside every known allocation.
|
||||
//
|
||||
// Lives here rather than in a panel because more than one place has to answer
|
||||
// the same question the same way: the entry form retunes on a typed frequency,
|
||||
// and the QSO editor has to keep band and frequency agreeing when one of them
|
||||
// is corrected. Kept in step with BandFromHz on the Go side.
|
||||
export function bandForMHz(mhz: number): string {
|
||||
if (!mhz || isNaN(mhz)) return '';
|
||||
const plan: [number, number, string][] = [
|
||||
[1.8, 2.0, '160m'], [3.5, 4.0, '80m'], [5.06, 5.45, '60m'], [7.0, 7.3, '40m'],
|
||||
[10.1, 10.15, '30m'], [14.0, 14.35, '20m'], [18.068, 18.168, '17m'], [21.0, 21.45, '15m'],
|
||||
[24.89, 24.99, '12m'], [28.0, 29.7, '10m'], [50, 54, '6m'], [70, 71, '4m'],
|
||||
[144, 148, '2m'], [222, 225, '1.25m'], [420, 450, '70cm'], [902, 928, '33cm'], [1240, 1300, '23cm'],
|
||||
// Microwave, ADIF 3.1.7 ranges.
|
||||
[2300, 2450, '13cm'], [3300, 3500, '9cm'], [5650, 5925, '6cm'], [10000, 10500, '3cm'],
|
||||
[24000, 24250, '1.25cm'], [47000, 47200, '6mm'], [75500, 81000, '4mm'],
|
||||
[119980, 123000, '2.5mm'], [134000, 149000, '2mm'], [241000, 250000, '1mm'],
|
||||
];
|
||||
for (const [lo, hi, b] of plan) if (mhz >= lo && mhz <= hi) return b;
|
||||
return '';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user