Bladeren bron

feat: integrate cli config and control scaffolding

tags/v0.3.0-pre
Jan Svabenik 1 maand geleden
bovenliggende
commit
1bb5692417
8 gewijzigde bestanden met toevoegingen van 238 en 0 verwijderingen
  1. +31
    -0
      cmd/fmrtx/main.go
  2. +17
    -0
      docs/README.md
  3. +31
    -0
      docs/config.sample.json
  4. +7
    -0
      go.mod
  5. +105
    -0
      internal/config/config.go
  6. +37
    -0
      internal/control/control.go
  7. +8
    -0
      scripts/check.ps1
  8. +2
    -0
      scripts/run.ps1

+ 31
- 0
cmd/fmrtx/main.go Bestand weergeven

@@ -0,0 +1,31 @@
package main

import (
"flag"
"fmt"
"log"
"net/http"

cfgpkg "github.com/jan/fm-rds-tx/internal/config"
ctrlpkg "github.com/jan/fm-rds-tx/internal/control"
)

func main() {
configPath := flag.String("config", "", "path to JSON config")
printConfig := flag.Bool("print-config", false, "print effective config and exit")
flag.Parse()

cfg, err := cfgpkg.Load(*configPath)
if err != nil {
log.Fatalf("load config: %v", err)
}

if *printConfig {
fmt.Printf("backend=%s freq=%.1fMHz stereo=%t rds=%t listen=%s\n", cfg.Backend.Kind, cfg.FM.FrequencyMHz, cfg.FM.StereoEnabled, cfg.RDS.Enabled, cfg.Control.ListenAddress)
return
}

srv := ctrlpkg.NewServer(cfg)
log.Printf("fm-rds-tx listening on %s", cfg.Control.ListenAddress)
log.Fatal(http.ListenAndServe(cfg.Control.ListenAddress, srv.Handler()))
}

+ 17
- 0
docs/README.md Bestand weergeven

@@ -0,0 +1,17 @@
# fm-rds-tx docs

## Build & Test

### Root CLI
- `go test ./...`
- `go run ./cmd/fmrtx -print-config`
- `go run ./cmd/fmrtx -config docs/config.sample.json`

### Internal DSP module
- `cd internal`
- `go test ./...`

### Examples module
- `cd examples`
- `go test ./...`
- `go run ./soapy_simulated`

+ 31
- 0
docs/config.sample.json Bestand weergeven

@@ -0,0 +1,31 @@
{
"audio": {
"inputPath": "",
"sampleRate": 48000,
"gain": 1.0
},
"rds": {
"enabled": true,
"pi": "1234",
"ps": "FMRTX",
"radioText": "fm-rds-tx example config",
"pty": 0
},
"fm": {
"frequencyMHz": 100.0,
"stereoEnabled": true,
"pilotLevel": 0.1,
"rdsInjection": 0.03,
"preEmphasisUS": false,
"outputDrive": 0.5,
"compositeRateHz": 228000
},
"backend": {
"kind": "file",
"device": "",
"outputPath": "build/out/composite.f32"
},
"control": {
"listenAddress": "127.0.0.1:8088"
}
}

+ 7
- 0
go.mod Bestand weergeven

@@ -0,0 +1,7 @@
module github.com/jan/fm-rds-tx

go 1.24.0

require github.com/jan/fm-rds-tx/internal v0.0.0

replace github.com/jan/fm-rds-tx/internal => ./internal

+ 105
- 0
internal/config/config.go Bestand weergeven

@@ -0,0 +1,105 @@
package config

import (
"encoding/json"
"fmt"
"os"
)

type Config struct {
Audio AudioConfig `json:"audio"`
RDS RDSConfig `json:"rds"`
FM FMConfig `json:"fm"`
Backend BackendConfig `json:"backend"`
Control ControlConfig `json:"control"`
}

type AudioConfig struct {
InputPath string `json:"inputPath"`
SampleRate int `json:"sampleRate"`
Gain float64 `json:"gain"`
}

type RDSConfig struct {
Enabled bool `json:"enabled"`
PI string `json:"pi"`
PS string `json:"ps"`
RadioText string `json:"radioText"`
PTY int `json:"pty"`
}

type FMConfig struct {
FrequencyMHz float64 `json:"frequencyMHz"`
StereoEnabled bool `json:"stereoEnabled"`
PilotLevel float64 `json:"pilotLevel"`
RDSInjection float64 `json:"rdsInjection"`
PreEmphasisUS bool `json:"preEmphasisUS"`
OutputDrive float64 `json:"outputDrive"`
CompositeRateHz int `json:"compositeRateHz"`
}

type BackendConfig struct {
Kind string `json:"kind"`
Device string `json:"device"`
OutputPath string `json:"outputPath"`
}

type ControlConfig struct {
ListenAddress string `json:"listenAddress"`
}

func Default() Config {
return Config{
Audio: AudioConfig{SampleRate: 48000, Gain: 1.0},
RDS: RDSConfig{Enabled: true, PI: "1234", PS: "FMRTX", RadioText: "fm-rds-tx", PTY: 0},
FM: FMConfig{FrequencyMHz: 100.0, StereoEnabled: true, PilotLevel: 0.1, RDSInjection: 0.03, OutputDrive: 0.5, CompositeRateHz: 228000},
Backend: BackendConfig{Kind: "file", OutputPath: "build/out/composite.f32"},
Control: ControlConfig{ListenAddress: "127.0.0.1:8088"},
}
}

func Load(path string) (Config, error) {
cfg := Default()
if path == "" {
return cfg, cfg.Validate()
}
data, err := os.ReadFile(path)
if err != nil {
return Config{}, err
}
if err := json.Unmarshal(data, &cfg); err != nil {
return Config{}, err
}
return cfg, cfg.Validate()
}

func (c Config) Validate() error {
if c.Audio.SampleRate < 8000 || c.Audio.SampleRate > 384000 {
return fmt.Errorf("audio.sampleRate out of range")
}
if c.Audio.Gain < 0 || c.Audio.Gain > 4 {
return fmt.Errorf("audio.gain out of range")
}
if c.FM.FrequencyMHz < 65 || c.FM.FrequencyMHz > 110 {
return fmt.Errorf("fm.frequencyMHz out of range")
}
if c.FM.PilotLevel < 0 || c.FM.PilotLevel > 0.2 {
return fmt.Errorf("fm.pilotLevel out of range")
}
if c.FM.RDSInjection < 0 || c.FM.RDSInjection > 0.1 {
return fmt.Errorf("fm.rdsInjection out of range")
}
if c.FM.OutputDrive < 0 || c.FM.OutputDrive > 1 {
return fmt.Errorf("fm.outputDrive out of range")
}
if c.FM.CompositeRateHz < 96000 || c.FM.CompositeRateHz > 1520000 {
return fmt.Errorf("fm.compositeRateHz out of range")
}
if c.Backend.Kind == "" {
return fmt.Errorf("backend.kind is required")
}
if c.Control.ListenAddress == "" {
return fmt.Errorf("control.listenAddress is required")
}
return nil
}

+ 37
- 0
internal/control/control.go Bestand weergeven

@@ -0,0 +1,37 @@
package control

import (
"encoding/json"
"net/http"

"github.com/jan/fm-rds-tx/internal/config"
)

type Server struct {
cfg config.Config
}

func NewServer(cfg config.Config) *Server { return &Server{cfg: cfg} }

func (s *Server) Handler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/healthz", s.handleHealth)
mux.HandleFunc("/status", s.handleStatus)
return mux
}

func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true})
}

func (s *Server) handleStatus(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"service": "fm-rds-tx",
"backend": s.cfg.Backend.Kind,
"frequencyMHz": s.cfg.FM.FrequencyMHz,
"stereoEnabled": s.cfg.FM.StereoEnabled,
"rdsEnabled": s.cfg.RDS.Enabled,
})
}

+ 8
- 0
scripts/check.ps1 Bestand weergeven

@@ -0,0 +1,8 @@
$ErrorActionPreference = "Stop"
go test ./...
Push-Location internal
go test ./...
Pop-Location
Push-Location examples
go test ./...
Pop-Location

+ 2
- 0
scripts/run.ps1 Bestand weergeven

@@ -0,0 +1,2 @@
$ErrorActionPreference = "Stop"
go run ./cmd/fmrtx -config docs/config.sample.json

Laden…
Annuleren
Opslaan