diff --git a/assets/js/contacts_map_hook.ts b/assets/js/contacts_map_hook.ts index 0e36ea2e..c7c205e2 100644 --- a/assets/js/contacts_map_hook.ts +++ b/assets/js/contacts_map_hook.ts @@ -25,7 +25,7 @@ interface ContactsMapHook extends ViewHook { const BAND_COLORS: Record = { 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 diff --git a/lib/microwaveprop/propagation/band_config.ex b/lib/microwaveprop/propagation/band_config.ex index 7d6f9519..a7ad03f2 100644 --- a/lib/microwaveprop/propagation/band_config.ex +++ b/lib/microwaveprop/propagation/band_config.ex @@ -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, diff --git a/lib/microwaveprop/radio/band_resolver.ex b/lib/microwaveprop/radio/band_resolver.ex index 56c48db5..597fcaae 100644 --- a/lib/microwaveprop/radio/band_resolver.ex +++ b/lib/microwaveprop/radio/band_resolver.ex @@ -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, diff --git a/lib/microwaveprop/radio/contact.ex b/lib/microwaveprop/radio/contact.ex index 037a8e98..a91f7092 100644 --- a/lib/microwaveprop/radio/contact.ex +++ b/lib/microwaveprop/radio/contact.ex @@ -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 ) diff --git a/lib/microwaveprop/radio/contact_edit.ex b/lib/microwaveprop/radio/contact_edit.ex index bd035c2f..520f5bed 100644 --- a/lib/microwaveprop/radio/contact_edit.ex +++ b/lib/microwaveprop/radio/contact_edit.ex @@ -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 diff --git a/lib/microwaveprop_web/live/contact_map_live.ex b/lib/microwaveprop_web/live/contact_map_live.ex index 398ddff8..4f9e7aa8 100644 --- a/lib/microwaveprop_web/live/contact_map_live.ex +++ b/lib/microwaveprop_web/live/contact_map_live.ex @@ -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", diff --git a/priv/repo/migrations/20260416173951_rename_band_3456_to_3400.exs b/priv/repo/migrations/20260416173951_rename_band_3456_to_3400.exs new file mode 100644 index 00000000..5f80b6c4 --- /dev/null +++ b/priv/repo/migrations/20260416173951_rename_band_3456_to_3400.exs @@ -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 diff --git a/test/microwaveprop/propagation/band_config_test.exs b/test/microwaveprop/propagation/band_config_test.exs index d308dccf..8aabc573 100644 --- a/test/microwaveprop/propagation/band_config_test.exs +++ b/test/microwaveprop/propagation/band_config_test.exs @@ -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 diff --git a/test/microwaveprop/radio/band_resolver_test.exs b/test/microwaveprop/radio/band_resolver_test.exs new file mode 100644 index 00000000..8bc085a1 --- /dev/null +++ b/test/microwaveprop/radio/band_resolver_test.exs @@ -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 diff --git a/test/microwaveprop/radio/contact_submission_test.exs b/test/microwaveprop/radio/contact_submission_test.exs index 9edf5173..e255a2c8 100644 --- a/test/microwaveprop/radio/contact_submission_test.exs +++ b/test/microwaveprop/radio/contact_submission_test.exs @@ -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"