Files
groundstation/internal/config/config.go
T

117 lines
3.2 KiB
Go

package config
import (
"fmt"
"os"
"git.piksar.eu/piotr/groundstation/internal/osd"
"git.piksar.eu/piotr/groundstation/internal/protocol"
"gopkg.in/yaml.v3"
)
// DisplayConfig configures the independent kiosk canvas process.
type DisplayConfig struct {
Enabled bool `yaml:"enabled"`
Command string `yaml:"command"`
User string `yaml:"user"`
Display string `yaml:"display"`
Xauthority string `yaml:"xauthority"`
}
// OSDConfig configures the supervisor's flight OSD snapshot publication.
type OSDConfig struct {
// StatePath is the snapshot state file the display process reads.
StatePath string `yaml:"state_path"`
// ArmIndex and ArmedThreshold derive the arm state from the final RC
// channels: armed while that channel stays above the threshold.
ArmIndex int `yaml:"arm_index"`
ArmedThreshold int16 `yaml:"armed_threshold"`
// VtxTableGen and VtxTableHash are the operator-selected VTX power
// table identifiers. Zero means no table is selected yet; a static
// selection is operator intent and never applied VTX state.
VtxTableGen uint32 `yaml:"vtx_table_gen"`
VtxTableHash uint64 `yaml:"vtx_table_hash"`
}
type Config struct {
Display DisplayConfig `yaml:"display"`
Serial struct {
Path string `yaml:"path"`
} `yaml:"serial"`
Video struct {
Enabled bool `yaml:"enabled"`
AuxIndex int `yaml:"aux_index"`
Threshold int16 `yaml:"threshold"`
Command string `yaml:"command"`
} `yaml:"video"`
OSD OSDConfig `yaml:"osd"`
Logging struct {
Enabled bool `yaml:"enabled"`
Path string `yaml:"path"`
} `yaml:"logging"`
}
func Default() Config {
var c Config
c.Display = DefaultDisplay()
c.Serial.Path = "/dev/ttyS0"
c.Video.Enabled = true
c.Video.AuxIndex = 5 // AUX2, zero-based
c.Video.Threshold = -512
c.Video.Command = "fpv-video"
c.OSD = DefaultOSD()
c.Logging.Enabled = true
return c
}
// DefaultDisplay starts the kiosk canvas before video on the desktop X
// server owned by the unprivileged display user.
func DefaultDisplay() DisplayConfig {
return DisplayConfig{
Enabled: true,
Command: "/usr/local/bin/groundstation-display",
User: "orangepi",
Display: ":0",
Xauthority: "/home/orangepi/.Xauthority",
}
}
// DefaultOSD publishes snapshots to the default tmpfs path and derives the
// arm state from CH5/AUX1, armed above the midpoint.
func DefaultOSD() OSDConfig {
return OSDConfig{
StatePath: osd.DefaultStatePath,
ArmIndex: 4,
ArmedThreshold: 0,
}
}
func Load(path string) (Config, error) {
config := Default()
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return config, config.validate()
}
return config, err
}
if err := yaml.Unmarshal(data, &config); err != nil {
return config, err
}
return config, config.validate()
}
// validate rejects channel indexes outside the final RC channel range: an
// out-of-range index would panic on the first decoded frame.
func (c Config) validate() error {
for name, index := range map[string]int{
"video.aux_index": c.Video.AuxIndex,
"osd.arm_index": c.OSD.ArmIndex,
} {
if index < 0 || index >= protocol.Channels {
return fmt.Errorf("%s %d outside the RC channel range 0..%d", name, index, protocol.Channels-1)
}
}
return nil
}