Update radio presets

This commit is contained in:
Jonathon Leight
2026-06-22 14:10:39 -04:00
parent a9bb3b7b29
commit f765e41735
5 changed files with 46 additions and 35 deletions
-1
View File
@@ -80,7 +80,6 @@ must serve the confirm/control pages over HTTPS.
| `MESHTENDER_MASTER_KEY` | 64 hex chars (32 bytes); AES-GCM key encrypting the identity seed at rest (required) |
| `MESHTENDER_ADDR` | listen address (default `:8080`) |
| `MESHTENDER_RP_ID` / `_RP_ORIGIN` / `_RP_NAME` | WebAuthn relying-party settings |
| `MESHTENDER_RADIO_FREQ_HZ` / `_BW_HZ` / `_SF` / `_CR` | default LoRa params suggested when adding a repeater |
> **Note:** `MESHTENDER_MASTER_KEY` is coupled to the stored identity — changing it makes the
> existing `server_identity` row undecryptable. Keep it stable.
+2 -21
View File
@@ -5,7 +5,6 @@ import (
"encoding/hex"
"fmt"
"os"
"strconv"
"strings"
)
@@ -62,13 +61,10 @@ type Config struct {
// production TLS usually terminates at a proxy and these stay empty.
TLSCert string
TLSKey string
// DefaultRadio holds fallback LoRa parameters offered when adding a
// repeater. Per-repeater values in the DB take precedence.
DefaultRadio RadioDefaults
}
// RadioDefaults are the suggested LoRa parameters for a new repeater.
// RadioDefaults is a set of LoRa parameters, used to match a repeater's stored
// radio config against the region presets offered in the UI.
type RadioDefaults struct {
FreqHz uint32
BwHz uint32
@@ -91,12 +87,6 @@ func Load() (*Config, error) {
WWWHost: os.Getenv("MESHTENDER_WWW_HOST"),
TLSCert: os.Getenv("MESHTENDER_TLS_CERT"),
TLSKey: os.Getenv("MESHTENDER_TLS_KEY"),
DefaultRadio: RadioDefaults{
FreqHz: uint32(envUintOr("MESHTENDER_RADIO_FREQ_HZ", 869525000)),
BwHz: uint32(envUintOr("MESHTENDER_RADIO_BW_HZ", 250000)),
SF: uint8(envUintOr("MESHTENDER_RADIO_SF", 11)),
CR: uint8(envUintOr("MESHTENDER_RADIO_CR", 5)),
},
}
if c.RootHost != "" && c.WWWHost == "" {
@@ -149,12 +139,3 @@ func splitOrigins(s string) []string {
}
return out
}
func envUintOr(key string, def uint64) uint64 {
if v := os.Getenv(key); v != "" {
if n, err := strconv.ParseUint(v, 10, 64); err == nil {
return n
}
}
return def
}
+1 -1
View File
@@ -44,7 +44,7 @@ func TestConfirmRoundTrip(t *testing.T) {
if err != nil {
t.Fatalf("auth: %v", err)
}
cfg := &config.Config{DefaultRadio: config.RadioDefaults{FreqHz: 869525000, BwHz: 250000, SF: 11, CR: 5}}
cfg := &config.Config{}
srv, err := NewServer(st, authSvc, idSvc, cfg)
if err != nil {
t.Fatalf("server: %v", err)
+40 -9
View File
@@ -14,18 +14,49 @@ type RadioPreset struct {
CR int
}
// radioPresets are the region presets shown in the dropdown.
// radioPresets are the community-suggested region presets shown in the
// dropdown. The list mirrors the "Select Radio Settings" presets in the
// MeshCore companion app.
var radioPresets = []RadioPreset{
{"eu868", "Europe 868 — 869.525 MHz · BW250 · SF11 · CR5", 869_525_000, 250_000, 11, 5},
{"eu433", "Europe 433 — 433.525 MHz · BW250 · SF11 · CR5", 433_525_000, 250_000, 11, 5},
{"us915", "US / Canada 915 — 910.525 MHz · BW250 · SF11 · CR5", 910_525_000, 250_000, 11, 5},
{"us915n", "US / Canada 915 narrow — 910.525 MHz · BW62.5 · SF7 · CR5", 910_525_000, 62_500, 7, 5},
{"anz915", "Australia / NZ — 915.800 MHz · BW250 · SF10 · CR5", 915_800_000, 250_000, 10, 5},
{"au", "Australia — 915.800 MHz · BW250 · SF10 · CR5", 915_800_000, 250_000, 10, 5},
{"au_narrow", "Australia (Narrow) — 916.575 MHz · BW62.5 · SF7 · CR8", 916_575_000, 62_500, 7, 8},
{"au_mid", "Australia (Mid) — 915.075 MHz · BW125 · SF9 · CR5", 915_075_000, 125_000, 9, 5},
{"au_sawa", "Australia: SA, WA — 923.125 MHz · BW62.5 · SF8 · CR8", 923_125_000, 62_500, 8, 8},
{"au_qld", "Australia: QLD — 923.125 MHz · BW62.5 · SF8 · CR5", 923_125_000, 62_500, 8, 5},
{"br", "Brazil — 923.125 MHz · BW62.5 · SF8 · CR8", 923_125_000, 62_500, 8, 8},
{"euuk_narrow", "EU/UK (Narrow) — 869.618 MHz · BW62.5 · SF8 · CR8", 869_618_000, 62_500, 8, 8},
{"euuk_dep", "EU/UK (Deprecated) — 869.525 MHz · BW250 · SF11 · CR5", 869_525_000, 250_000, 11, 5},
{"cz_narrow", "Czech Republic (Narrow) — 869.432 MHz · BW62.5 · SF7 · CR5", 869_432_000, 62_500, 7, 5},
{"eu433_lr", "EU 433 (Long Range) — 433.650 MHz · BW250 · SF11 · CR5", 433_650_000, 250_000, 11, 5},
{"eu433_narrow", "EU 433 (Narrow) — 433.650 MHz · BW62.5 · SF8 · CR8", 433_650_000, 62_500, 8, 8},
{"nl", "Netherlands — 869.618 MHz · BW62.5 · SF7 · CR5", 869_618_000, 62_500, 7, 5},
{"nz", "New Zealand — 917.375 MHz · BW250 · SF11 · CR5", 917_375_000, 250_000, 11, 5},
{"nz_narrow", "New Zealand (Narrow) — 917.375 MHz · BW62.5 · SF7 · CR5", 917_375_000, 62_500, 7, 5},
{"pt433", "Portugal 433 — 433.375 MHz · BW62.5 · SF9 · CR6", 433_375_000, 62_500, 9, 6},
{"pt868", "Portugal 868 — 869.618 MHz · BW62.5 · SF7 · CR6", 869_618_000, 62_500, 7, 6},
{"ch", "Switzerland — 869.618 MHz · BW62.5 · SF8 · CR8", 869_618_000, 62_500, 8, 8},
{"usca", "USA/Canada (Recommended) — 910.525 MHz · BW62.5 · SF7 · CR5", 910_525_000, 62_500, 7, 5},
{"vn_narrow", "Vietnam (Narrow) — 920.250 MHz · BW62.5 · SF8 · CR5", 920_250_000, 62_500, 8, 5},
{"vn_dep", "Vietnam (Deprecated) — 920.250 MHz · BW250 · SF11 · CR5", 920_250_000, 250_000, 11, 5},
}
// defaultPresetID returns the preset id matching the configured defaults, or
// "custom" when no preset matches.
func defaultPresetID(d config.RadioDefaults) string {
// defaultPresetID is the preset selected by default when adding a repeater:
// MeshCore's recommended USA/Canada profile.
const defaultPresetID = "usca"
// defaultPreset returns the preset offered first when adding a repeater.
func defaultPreset() RadioPreset {
for _, p := range radioPresets {
if p.ID == defaultPresetID {
return p
}
}
return radioPresets[0]
}
// presetIDFor returns the preset id whose parameters match d, or "custom" when
// none match. Used to pre-select the dropdown when editing an existing repeater.
func presetIDFor(d config.RadioDefaults) string {
for _, p := range radioPresets {
if p.FreqHz == int64(d.FreqHz) && p.BwHz == int64(d.BwHz) && p.SF == int(d.SF) && p.CR == int(d.CR) {
return p.ID
+3 -3
View File
@@ -33,9 +33,9 @@ func (s *Handlers) pageAddRepeater(w http.ResponseWriter, r *http.Request) {
"ServerPubKey": s.Identity.PublicKeyHex(),
"SetPermCommand": s.Identity.SetPermCommand(),
"RevokeCommand": s.Identity.RevokePermCommand(),
"Defaults": s.Cfg.DefaultRadio,
"Defaults": defaultPreset(),
"Presets": radioPresets,
"DefaultPresetID": defaultPresetID(s.Cfg.DefaultRadio),
"DefaultPresetID": defaultPresetID,
"Error": r.URL.Query().Get("error"),
})
}
@@ -190,7 +190,7 @@ func (s *Handlers) pageEditRepeater(w http.ResponseWriter, r *http.Request) {
s.Render(w, r, "edit_repeater.html", map[string]any{
"Repeater": rep,
"Presets": radioPresets,
"SelectedPreset": defaultPresetID(config.RadioDefaults{FreqHz: uint32(rep.RadioFreqHz), BwHz: uint32(rep.RadioBwHz), SF: uint8(rep.RadioSF), CR: uint8(rep.RadioCR)}),
"SelectedPreset": presetIDFor(config.RadioDefaults{FreqHz: uint32(rep.RadioFreqHz), BwHz: uint32(rep.RadioBwHz), SF: uint8(rep.RadioSF), CR: uint8(rep.RadioCR)}),
"RevokeCommand": s.Identity.RevokePermCommand(),
"Error": r.URL.Query().Get("error"),
})