Rename 9cm band 3456 → 3400

The US 9cm amateur allocation was cut from 3300-3500 to 3300-3450 MHz
in 2020, so 3456 (the legacy weak-signal calling frequency) is no
longer in-band. Move the canonical key to 3400, migrate existing
contacts, and keep 3456 input resolving to 3400 via the
nearest-band snap so historical ADIF/CSV logs still import cleanly.
This commit is contained in:
Graham McIntire 2026-04-16 12:40:33 -05:00
parent bc7f3f4137
commit f93f5c9a57
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
10 changed files with 79 additions and 12 deletions

View file

@ -25,7 +25,7 @@ interface ContactsMapHook extends ViewHook {
const BAND_COLORS: Record<number, string> = {
1296: "#475569", // slate-600
2304: "#7c3aed", // violet-600
3456: "#4f46e5", // indigo-600
3400: "#4f46e5", // indigo-600
5760: "#2563eb", // blue-600
10000: "#059669", // emerald-600
24000: "#d97706", // amber-600

View file

@ -253,9 +253,9 @@ defmodule Microwaveprop.Propagation.BandConfig do
extended_range_km: 600,
exceptional_range_km: 1000
},
3_456 => %{
freq_mhz: 3_456,
label: "3456 MHz",
3_400 => %{
freq_mhz: 3_400,
label: "3400 MHz",
o2_db_km: 0.006,
h2o_coeff: 0.0,
humidity_effect: :beneficial,

View file

@ -21,7 +21,7 @@ defmodule Microwaveprop.Radio.BandResolver do
902,
1296,
2304,
3456,
3400,
5760,
10_000,
24_000,
@ -44,7 +44,7 @@ defmodule Microwaveprop.Radio.BandResolver do
"33cm" => 902,
"23cm" => 1_296,
"13cm" => 2_304,
"9cm" => 3_456,
"9cm" => 3_400,
"6cm" => 5_760,
"3cm" => 10_000,
"1.25cm" => 24_000,

View file

@ -65,7 +65,7 @@ defmodule Microwaveprop.Radio.Contact do
# Keep in sync with Microwaveprop.Propagation.BandConfig.all_bands/0 and
# Microwaveprop.Radio.AdifImport.@allowed_bands.
@allowed_bands Enum.map(
~w(50 144 222 432 902 1296 2304 3456 5760 10000 24000 47000 68000 75000 122000 134000 241000),
~w(50 144 222 432 902 1296 2304 3400 5760 10000 24000 47000 68000 75000 122000 134000 241000),
&Decimal.new/1
)

View file

@ -12,7 +12,7 @@ defmodule Microwaveprop.Radio.ContactEdit do
@foreign_key_type :binary_id
@allowed_modes ~w(CW SSB FM FT8 FT4 Q65)
@allowed_bands ~w(902 1296 2304 3456 5760 10000 24000 47000 68000 75000 122000 134000 241000)
@allowed_bands ~w(50 144 222 432 902 1296 2304 3400 5760 10000 24000 47000 68000 75000 122000 134000 241000)
schema "contact_edits" do
belongs_to :contact, Contact

View file

@ -7,7 +7,7 @@ defmodule MicrowavepropWeb.ContactMapLive do
@band_colors %{
1296 => "#475569",
2304 => "#7c3aed",
3456 => "#4f46e5",
3400 => "#4f46e5",
5760 => "#2563eb",
10_000 => "#059669",
24_000 => "#d97706",

View file

@ -0,0 +1,14 @@
defmodule Microwaveprop.Repo.Migrations.RenameBand3456To3400 do
use Ecto.Migration
# US 9cm amateur allocation was cut from 3300-3500 to 3300-3450 MHz in
# 2020, so the legacy 3456 weak-signal calling frequency is no longer
# in-band. Canonicalize existing contacts on the new 3400 value.
def up do
execute "UPDATE contacts SET band = 3400 WHERE band = 3456"
end
def down do
execute "UPDATE contacts SET band = 3456 WHERE band = 3400"
end
end

View file

@ -11,7 +11,7 @@ defmodule Microwaveprop.Propagation.BandConfigTest do
902,
1_296,
2_304,
3_456,
3_400,
5_760,
10_000,
24_000,
@ -363,7 +363,7 @@ defmodule Microwaveprop.Propagation.BandConfigTest do
end
test "sub-10 GHz bands are beneficial, 24+ GHz are harmful" do
beneficial = [50, 144, 222, 432, 902, 1_296, 2_304, 3_456, 5_760, 10_000]
beneficial = [50, 144, 222, 432, 902, 1_296, 2_304, 3_400, 5_760, 10_000]
harmful = @all_freqs -- beneficial
for freq <- beneficial do

View file

@ -0,0 +1,53 @@
defmodule Microwaveprop.Radio.BandResolverTest do
use ExUnit.Case, async: true
alias Microwaveprop.Radio.BandResolver
describe "resolve/1" do
test "resolves wavelength labels" do
assert BandResolver.resolve("6m") == 50
assert BandResolver.resolve("2m") == 144
assert BandResolver.resolve("1.25m") == 222
assert BandResolver.resolve("70cm") == 432
assert BandResolver.resolve("9cm") == 3_400
assert BandResolver.resolve("3cm") == 10_000
end
test "resolves canonical values through passthrough" do
for band <- BandResolver.allowed_bands() do
assert BandResolver.resolve(band) == band
assert BandResolver.resolve(Integer.to_string(band)) == band
end
end
test "resolves the legacy 3456 MHz calling frequency to the current 9cm band" do
# 9cm was cut from 3300-3500 to 3300-3450 MHz in 2020, so the old
# weak-signal calling frequency 3456 is no longer in the allocation
# but historical logs still carry it.
assert BandResolver.resolve("3456") == 3_400
assert BandResolver.resolve(3456) == 3_400
assert BandResolver.resolve(3456.1) == 3_400
end
test "snaps near-band frequencies to amateur allocations" do
assert BandResolver.resolve("50.125") == 50
assert BandResolver.resolve("144.200") == 144
assert BandResolver.resolve("222.100") == 222
assert BandResolver.resolve("432.100") == 432
assert BandResolver.resolve("10368.100") == 10_000
end
test "returns nil for HF and gaps between amateur bands" do
assert BandResolver.resolve("14.200") == nil
assert BandResolver.resolve("28.400") == nil
assert BandResolver.resolve("500") == nil
assert BandResolver.resolve("800") == nil
end
test "returns nil for unparseable input" do
assert BandResolver.resolve(nil) == nil
assert BandResolver.resolve("") == nil
assert BandResolver.resolve("banana") == nil
end
end
end

View file

@ -84,7 +84,7 @@ defmodule Microwaveprop.Radio.ContactSubmissionTest do
end
test "accepts valid bands" do
for band <- ~w(50 144 222 432 902 1296 2304 3456 5760 10000 24000 47000 75000) do
for band <- ~w(50 144 222 432 902 1296 2304 3400 5760 10000 24000 47000 75000) do
attrs = Map.put(@valid_attrs, :band, band)
changeset = Contact.submission_changeset(%Contact{}, attrs)
assert changeset.valid?, "Expected band #{band} to be valid"