pat/cfg/config.go
Martin Hebnes Pedersen 98f9968bd5 Remove some deprecated config fields
- gpsd_addr: Use gpsd.addr instead.
- serial_tnc.baudrate: Use serial_tnc.hbaud instead.
2026-01-30 19:17:25 +01:00

445 lines
13 KiB
Go

// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
// Use of this source code is governed by the MIT-license that can be
// found in the LICENSE file.
package cfg
import (
"encoding/json"
"fmt"
"net"
"strconv"
"strings"
"github.com/la5nta/wl2k-go/transport/ardop"
)
const (
PlaceholderMycall = "{mycall}"
)
type AuxAddr struct {
Address string
Password *string
}
func (a AuxAddr) MarshalJSON() ([]byte, error) {
if a.Password == nil {
return json.Marshal(a.Address)
}
return json.Marshal(a.Address + ":" + *a.Password)
}
func (a *AuxAddr) UnmarshalJSON(p []byte) error {
var str string
if err := json.Unmarshal(p, &str); err != nil {
return err
}
parts := strings.SplitN(str, ":", 2)
a.Address = parts[0]
if len(parts) > 1 {
a.Password = &parts[1]
}
return nil
}
type Config struct {
// This station's callsign.
MyCall string `json:"mycall"`
// Secure login password used when a secure login challenge is received.
//
// The user is prompted if this is undefined.
SecureLoginPassword string `json:"secure_login_password"`
// Auxiliary callsigns to fetch email on behalf of.
//
// Passwords can optionally be specified by appending :MYPASS (e.g. EMCOMM-1:MyPassw0rd).
// If no password is specified, the SecureLoginPassword is used.
AuxAddrs []AuxAddr `json:"auxiliary_addresses"`
// Maidenhead grid square (e.g. JP20qe).
Locator string `json:"locator"`
// Message size limit (in bytes) for automatic download of pending messages.
//
// When receiving messages larger than this limit, the user will be prompted
// with the option to defer the download to a later session.
//
// Negative value means no limit.
AutoDownloadSizeLimit int `json:"auto_download_size_limit"`
// List of service codes for rmslist (defaults to PUBLIC)
ServiceCodes []string `json:"service_codes"`
// Default HTTP listen address (for web UI).
//
// Use ":8080" to listen on any device, port 8080.
HTTPAddr string `json:"http_addr"`
// Handshake comment lines sent to remote node on incoming connections.
//
// Example: ["QTH: Hagavik, Norway. Operator: Martin", "Rig: FT-897 with Signalink USB"]
MOTD []string `json:"motd"`
// Connect aliases
//
// Example: {"LA1B-10": "ax25:///LD5GU/LA1B-10", "LA1B": "ardop://LA3F?freq=5350"}
// Any occurrence of the substring "{mycall}" will be replaced with user's callsign.
ConnectAliases map[string]string `json:"connect_aliases"`
// Methods to listen for incoming P2P connections by default.
//
// Example: ["ax25", "telnet", "ardop"]
Listen []string `json:"listen"`
// Hamlib rigs available (with reference name) for ptt and frequency control.
HamlibRigs map[string]HamlibConfig `json:"hamlib_rigs"`
AX25 AX25Config `json:"ax25"` // See AX25Config.
AX25Linux AX25LinuxConfig `json:"ax25_linux"` // See AX25LinuxConfig.
AGWPE AGWPEConfig `json:"agwpe"` // See AGWPEConfig.
SerialTNC SerialTNCConfig `json:"serial-tnc"` // See SerialTNCConfig.
Ardop ArdopConfig `json:"ardop"` // See ArdopConfig.
Pactor PactorConfig `json:"pactor"` // See PactorConfig.
Telnet TelnetConfig `json:"telnet"` // See TelnetConfig.
VaraHF VaraConfig `json:"varahf"` // See VaraConfig.
VaraFM VaraConfig `json:"varafm"` // See VaraConfig.
// See GPSdConfig.
GPSd GPSdConfig `json:"gpsd"`
// See PredictionConfig.
Prediction PredictionConfig `json:"prediction,omitzero"`
// Command schedule (cron-like syntax).
//
// Examples:
// # Connect to telnet once every hour
// "@hourly": "connect telnet"
//
// # Change ardop listen frequency based on hour of day
// "00 10 * * *": "freq ardop:7350.000", # 40m from 10:00
// "00 18 * * *": "freq ardop:5347.000", # 60m from 18:00
// "00 22 * * *": "freq ardop:3602.000" # 80m from 22:00
Schedule map[string]string `json:"schedule"`
// By default, Pat posts your callsign and running version to the Winlink CMS Web Services
//
// Set to true if you don't want your information sent.
VersionReportingDisabled bool `json:"version_reporting_disabled"`
}
func (c *Config) UnmarshalJSON(b []byte) error {
type aliased Config
tmp := struct {
aliased
// Default to -1 if omitted (legacy configs)
AutoDownloadSizeLimit *int `json:"auto_download_size_limit"`
}{}
if err := json.Unmarshal(b, &tmp); err != nil {
return err
}
if ptr := tmp.AutoDownloadSizeLimit; ptr == nil {
tmp.aliased.AutoDownloadSizeLimit = -1
} else {
tmp.aliased.AutoDownloadSizeLimit = *tmp.AutoDownloadSizeLimit
}
*c = Config(tmp.aliased)
return nil
}
type PredictionConfig struct {
// engine sets the HF propagation prediction engine to be used.
// Valid options are:
// - empty string (autodetect)
// - voacap
// - voacap-api
// - disabled
// If empty, autodetection is attempted.
Engine PredictionEngine `json:"engine"`
// See VOACAPConfig.
VOACAP VOACAPConfig `json:"voacap,omitzero"`
// See VOACAPAPIConfig.
VOACAPAPI VOACAPAPIConfig `json:"voacap_api,omitzero"`
}
func (p PredictionConfig) IsZero() bool { return p == (PredictionConfig{}) }
type VOACAPConfig struct {
// Executable overrides the default executable for VOACAP.
// Default is c:\itshfbc\bin_win\voacapw.exe on Windows and voacapl for other platforms.
Executable string `json:"executable"`
// DataDir overrides the default data directory for VOACAP.
// Default is c:\itshfbc on Windows and $HOME/itshfbc for other platforms.
DataDir string `json:"data_dir"`
}
func (v VOACAPConfig) IsZero() bool { return v == (VOACAPConfig{}) }
type VOACAPAPIConfig struct {
// BaseURL is the base URL for the VOACAP API.
// E.g. http://localhost:3000
BaseURL string `json:"base_url"`
}
func (v VOACAPAPIConfig) IsZero() bool { return v == (VOACAPAPIConfig{}) }
type HamlibConfig struct {
// The network type ("serial" or "tcp"). Use 'tcp' for rigctld (default).
//
// (For serial support: build with "-tags libhamlib".)
Network string `json:"network,omitempty"`
// The rig address.
//
// For tcp (rigctld): "address:port" (e.g. localhost:4532).
// For serial: "/path/to/tty?model=&baudrate=" (e.g. /dev/ttyS0?model=123&baudrate=4800).
Address string `json:"address,omitempty"`
// The rig's VFO to control ("A" or "B"). If empty, the current active VFO is used.
VFO string `json:"VFO"`
}
type ArdopConfig struct {
// Network address of the Ardop TNC (e.g. localhost:8515).
Addr string `json:"addr"`
// Default/listen ARQ bandwidth (200/500/1000/2000 MAX/FORCED).
ARQBandwidth ardop.Bandwidth `json:"arq_bandwidth"`
// Number of connect frames to transmit when dialing.
//
// Some stations may require mutliple attempts before responding.
ConnectRequests int `json:"connect_requests"`
// (optional) Reference name to the Hamlib rig to control frequency and ptt.
Rig string `json:"rig"`
// Set to true if hamlib should control PTT (SignaLink=false, most rigexpert=true).
PTTControl bool `json:"ptt_ctrl"`
// (optional) Send ID frame at a regular interval when the listener is active (unit is seconds)
BeaconInterval int `json:"beacon_interval"`
// Send FSK CW ID after an ID frame.
CWID bool `json:"cwid_enabled"`
}
type VaraConfig struct {
// Network host of the VARA modem (defaults to localhost:8300).
Addr string `json:"addr"`
// Default/listen bandwidth (HF: 500/2300/2750 Hz).
Bandwidth int `json:"bandwidth,omitempty"`
// (optional) Reference name to the Hamlib rig to control frequency and ptt.
Rig string `json:"rig"`
// Set to true if hamlib should control PTT (SignaLink=false, most rigexpert=true).
PTTControl bool `json:"ptt_ctrl"`
}
// UnmarshalJSON implements VaraConfig JSON unmarshalling with support for legacy format.
func (v *VaraConfig) UnmarshalJSON(b []byte) error {
type newFormat VaraConfig
legacy := struct {
newFormat
Host string `json:"host"`
CmdPort int `json:"cmdPort"`
DataPort int `json:"dataPort"`
}{}
if err := json.Unmarshal(b, &legacy); err != nil {
return err
}
if legacy.newFormat.Addr == "" && legacy.Host != "" {
legacy.newFormat.Addr = fmt.Sprintf("%s:%d", legacy.Host, legacy.CmdPort)
}
*v = VaraConfig(legacy.newFormat)
if !v.IsZero() && v.CmdPort() <= 0 {
return fmt.Errorf("invalid addr format")
}
return nil
}
func (v VaraConfig) IsZero() bool { return v == (VaraConfig{}) }
func (v VaraConfig) Host() string {
host, _, _ := net.SplitHostPort(v.Addr)
return host
}
func (v VaraConfig) CmdPort() int {
_, portStr, _ := net.SplitHostPort(v.Addr)
port, _ := strconv.Atoi(portStr)
return port
}
func (v VaraConfig) DataPort() int { return v.CmdPort() + 1 }
type PactorConfig struct {
// Path/port to TNC device (e.g. /dev/ttyUSB0 or COM1).
Path string `json:"path"`
// Baudrate for the serial port (e.g. 57600).
Baudrate int `json:"baudrate"`
// (optional) Reference name to the Hamlib rig for frequency control.
Rig string `json:"rig"`
// (optional) Path to custom TNC initialization script.
InitScript string `json:"custom_init_script"`
}
type TelnetConfig struct {
// Network address (and port) to listen for telnet-p2p connections (e.g. :8774).
ListenAddr string `json:"listen_addr"`
// Telnet-p2p password.
Password string `json:"password"`
}
type SerialTNCConfig struct {
// Serial port (e.g. /dev/ttyUSB0 or COM1).
Path string `json:"path"`
// SerialBaud is the serial port's baudrate (e.g. 57600).
SerialBaud int `json:"serial_baud"`
// HBaud is the the packet connection's baudrate (1200 or 9600).
HBaud int `json:"hbaud"`
// Type of TNC (currently only 'kenwood').
Type string `json:"type"`
}
type AGWPEConfig struct {
// The TCP address of the TNC.
Addr string `json:"addr"`
// The AGWPE "radio port" (0-3).
RadioPort int `json:"radio_port"`
}
type AX25Config struct {
// The AX.25 engine to be used.
//
// Valid options are:
// - linux
// - agwpe
// - serial-tnc
Engine AX25Engine `json:"engine"`
// (optional) Reference name to the Hamlib rig for frequency control.
Rig string `json:"rig"`
// DEPRECATED: See AX25Linux.Port.
AXPort string `json:"port,omitempty"`
// Optional beacon when listening for incoming packet-p2p connections.
Beacon BeaconConfig `json:"beacon"`
}
type AX25LinuxConfig struct {
// axport to use (as defined in /etc/ax25/axports). Only applicable to ax25 engine 'linux'.
Port string `json:"port"`
}
type BeaconConfig struct {
// Beacon interval in seconds (e.g. 3600 for once every 1 hour)
Every int `json:"every"` // (seconds)
// Beacon data/message
Message string `json:"message"`
// Beacon destination (e.g. IDENT)
Destination string `json:"destination"`
}
type GPSdConfig struct {
// Enable GPSd proxy for HTTP (web GUI)
//
// Caution: Your GPS position will be accessible to any network device able to access Pat's HTTP interface.
EnableHTTP bool `json:"enable_http"`
// Allow Winlink forms to use GPSd for aquiring your position.
//
// Caution: Your current GPS position will be automatically injected, without your explicit consent, into forms requesting such information.
AllowForms bool `json:"allow_forms"`
// Use server time instead of timestamp provided by GPSd (e.g for older GPS device with week roll-over issue).
UseServerTime bool `json:"use_server_time"`
// Automatically update the locator field in-memory by polling GPSd every hour.
//
// Note: This only updates the locator in-memory. The config file is not modified.
// On startup, the config's locator value will be used until the first position is received from GPSd.
UpdateLocator bool `json:"update_locator"`
// Address and port of GPSd server (e.g. localhost:2947).
Addr string `json:"addr"`
}
var DefaultConfig = Config{
MOTD: []string{"Open source Winlink client - getpat.io"},
AuxAddrs: []AuxAddr{},
ServiceCodes: []string{"PUBLIC"},
AutoDownloadSizeLimit: -1,
ConnectAliases: map[string]string{
"telnet": "telnet://{mycall}:CMSTelnet@cms.winlink.org:8772/wl2k",
},
Listen: []string{},
HTTPAddr: "localhost:8080",
AX25: AX25Config{
Engine: DefaultAX25Engine(),
Beacon: BeaconConfig{
Every: 3600,
Message: "Winlink P2P",
Destination: "IDENT",
},
},
AX25Linux: AX25LinuxConfig{
Port: "wl2k",
},
SerialTNC: SerialTNCConfig{
Path: "/dev/ttyUSB0",
SerialBaud: 9600,
HBaud: 1200,
Type: "kenwood",
},
AGWPE: AGWPEConfig{
Addr: "localhost:8000",
RadioPort: 0,
},
Ardop: ArdopConfig{
Addr: "localhost:8515",
ARQBandwidth: ardop.Bandwidth500Max,
ConnectRequests: ardop.DefaultConnectRequests,
CWID: true,
},
Pactor: PactorConfig{
Path: "/dev/ttyUSB0",
Baudrate: 57600,
},
Telnet: TelnetConfig{
ListenAddr: ":8774",
Password: "",
},
VaraHF: VaraConfig{
Addr: "localhost:8300",
Bandwidth: 2300,
},
VaraFM: VaraConfig{
Addr: "localhost:8300",
},
GPSd: GPSdConfig{
EnableHTTP: false, // Default to false to help protect privacy of unknowing users (see github.com//issues/146)
AllowForms: false, // Default to false to help protect location privacy of unknowing users
UseServerTime: false,
UpdateLocator: false,
Addr: "localhost:2947", // Default listen address for GPSd
},
Schedule: map[string]string{},
HamlibRigs: map[string]HamlibConfig{},
}