diff --git a/README.md b/README.md index 844941f..1911e7c 100644 --- a/README.md +++ b/README.md @@ -229,8 +229,21 @@ The bars on the scale are as wide as the mode passes: green where you are listening, red where the radio transmits when working split, orange for the other radio of a two-radio station. -Band-plan colouring of the scale is not there. The segments differ by ITU region -and the program has no band-plan table, so it would be guesswork. +Behind the scale the CW part of the band is one shade and the phone part +another, with a third shade for a digital block where one is set, and the CW +boundary named in the status line. Anything past a band edge is shaded red, +which is where the edges show. N1MM paints the same blocks from its `CWBands`, +`DigiBands` and `SSBBands` tables. + +**Config → Sub bands** edits them, one row per band, in kilohertz, with a +Defaults button. The digital pair starts empty, as N1MM's does, so nothing is +painted for it until it is filled in. Only the rows that differ from the +defaults are written to the settings file. + +The defaults are one worldwide set, as N1MM's are, not one per ITU region. They +are a drawing aid: a contact logs either side of a boundary just the same. +Bands where N1MM's own numbers contradict themselves — 60M, and everything +above 2M — get no shading rather than a guess. ### Band edges diff --git a/docs/unfinished.md b/docs/unfinished.md index 25a6b73..05612b7 100644 --- a/docs/unfinished.md +++ b/docs/unfinished.md @@ -45,10 +45,6 @@ data. The file is read without them. the contest rules score it, but there is no digital window: no decoding, no transmitting, no interface to fldigi, MMTTY or similar. -**Band-plan colouring of the bandmap scale.** N1MM colours the scale by CW, -digital and phone segment. The segments differ by ITU region and there is no -band-plan table here, so it is left out rather than guessed. - **Contest coverage.** Eight contests are built in, plus whatever `.udc` files are in the user-defined folder. N1MM ships well over a hundred. Opening a log from a contest not in the registry fails with the contest name, which is the @@ -64,6 +60,12 @@ thread, and `AppSession.TakeFromNetwork` reopens the whole contest to do it. That is correct but heavy, and it discards what is typed in an entry window on another radio. +**The band plan covers 160M to 2M only.** 60M and everything above 2M get no +CW, digital or phone shading on the bandmap, because N1MM's own numbers for +those bands contradict themselves — its CW top for 1.25M is below its phone +start, and its 70CM CW top is above the band. Config ▸ Sub bands cannot add a +band that has no default. + **Available mults and Qs is only as good as the bandmap.** It lists what is spotted, so a multiplier nobody has spotted is not there. N1MM's window also shows the mults a contest has and nobody has worked, which needs a list of every diff --git a/src/Nonemm.App/AppSession.cs b/src/Nonemm.App/AppSession.cs index da05c1f..7872761 100644 --- a/src/Nonemm.App/AppSession.cs +++ b/src/Nonemm.App/AppSession.cs @@ -36,6 +36,7 @@ public sealed class AppSession : IDisposable History = LoadCallHistory(settings.CallHistoryFile); Registry = ContestRegistry.FromFolder(paths.UserDefinedContests, out IReadOnlyList problems); UserDefinedContestProblems = problems; + BandPlan = settings.ToBandPlan(); } public UserPaths Paths { get; } @@ -44,6 +45,9 @@ public sealed class AppSession : IDisposable public ContestRegistry Registry { get; private set; } + /// Where the bandmap paints the CW, digital and phone parts of a band. + public BandPlan BandPlan { get; private set; } + public IReadOnlyList UserDefinedContestProblems { get; } public CountryFile? Countries { get; private set; } @@ -101,6 +105,7 @@ public sealed class AppSession : IDisposable public void Save(Settings settings) { Settings = settings; + BandPlan = settings.ToBandPlan(); settings.Save(Paths.SettingsFile); Changed?.Invoke(this, EventArgs.Empty); } diff --git a/src/Nonemm.App/Configuration/Settings.cs b/src/Nonemm.App/Configuration/Settings.cs index 1cb6620..b567133 100644 --- a/src/Nonemm.App/Configuration/Settings.cs +++ b/src/Nonemm.App/Configuration/Settings.cs @@ -62,6 +62,10 @@ public sealed record Settings public int KeyerSpeed { get; init; } = 28; + /// Sub-band boundaries the operator has changed. Empty means the defaults + /// in `BandPlan.Default`; an entry replaces one band's boundaries. + public IReadOnlyList SubBands { get; init; } = []; + /// The twelve function key messages, keyed F1 to F12. public IReadOnlyList CwMessages { get; init; } = []; @@ -91,6 +95,20 @@ public sealed record Settings } } + /// The defaults with the operator's changes applied over them. + public BandPlan ToBandPlan() + { + BandPlan plan = BandPlan.Default; + foreach (StoredSubBand stored in SubBands) + { + if (stored.ToSegments() is { } segments) + { + plan = plan.With(segments); + } + } + return plan; + } + public void Save(string path) => File.WriteAllText(path, JsonSerializer.Serialize(this, Json)); @@ -112,6 +130,38 @@ public sealed record Settings }; } +/// One band's sub-band boundaries as they are stored. Kilohertz, because that +/// is what the operator types into the editor. +public sealed record StoredSubBand +{ + public string Band { get; init; } = ""; + + public double CwHigh { get; init; } + + public double DigitalLow { get; init; } + + public double DigitalHigh { get; init; } + + /// Null when the band name is not one we know, or the CW boundary is + /// missing: an entry that cannot be read leaves the default in place. + public BandSegments? ToSegments() => + Bands.Named(Band) is { } band && CwHigh > 0 + ? new BandSegments( + band, + Frequency.FromKilohertz(CwHigh), + Frequency.FromKilohertz(DigitalLow), + Frequency.FromKilohertz(DigitalHigh)) + : null; + + public static StoredSubBand From(BandSegments segments) => new() + { + Band = segments.Band.Name, + CwHigh = segments.CwHigh.Kilohertz, + DigitalLow = segments.DigitalLow.Kilohertz, + DigitalHigh = segments.DigitalHigh.Kilohertz, + }; +} + /// One radio's `rigctld`. public sealed record StoredRadio { diff --git a/src/Nonemm.App/Dialogs/SubBandsDialog.axaml b/src/Nonemm.App/Dialogs/SubBandsDialog.axaml new file mode 100644 index 0000000..2063a5d --- /dev/null +++ b/src/Nonemm.App/Dialogs/SubBandsDialog.axaml @@ -0,0 +1,20 @@ + + + + +