From e35d9f8064976293a5d92ccd96d703739fcf6964 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 8 Apr 2026 08:22:26 +0200 Subject: [PATCH] control: stabilize and polish control ui --- internal/control/control.go | 52 +- internal/control/ui.html | 4033 ++++++++--------------------------- 2 files changed, 967 insertions(+), 3118 deletions(-) diff --git a/internal/control/control.go b/internal/control/control.go index 3618463..1672874 100644 --- a/internal/control/control.go +++ b/internal/control/control.go @@ -112,20 +112,26 @@ func isJSONContentType(r *http.Request) bool { } type ConfigPatch struct { - FrequencyMHz *float64 `json:"frequencyMHz,omitempty"` - OutputDrive *float64 `json:"outputDrive,omitempty"` - StereoEnabled *bool `json:"stereoEnabled,omitempty"` - PilotLevel *float64 `json:"pilotLevel,omitempty"` - RDSInjection *float64 `json:"rdsInjection,omitempty"` - RDSEnabled *bool `json:"rdsEnabled,omitempty"` - ToneLeftHz *float64 `json:"toneLeftHz,omitempty"` - ToneRightHz *float64 `json:"toneRightHz,omitempty"` - ToneAmplitude *float64 `json:"toneAmplitude,omitempty"` - PS *string `json:"ps,omitempty"` - RadioText *string `json:"radioText,omitempty"` - PreEmphasisTauUS *float64 `json:"preEmphasisTauUS,omitempty"` - LimiterEnabled *bool `json:"limiterEnabled,omitempty"` - LimiterCeiling *float64 `json:"limiterCeiling,omitempty"` + FrequencyMHz *float64 `json:"frequencyMHz,omitempty"` + OutputDrive *float64 `json:"outputDrive,omitempty"` + StereoEnabled *bool `json:"stereoEnabled,omitempty"` + PilotLevel *float64 `json:"pilotLevel,omitempty"` + RDSInjection *float64 `json:"rdsInjection,omitempty"` + RDSEnabled *bool `json:"rdsEnabled,omitempty"` + ToneLeftHz *float64 `json:"toneLeftHz,omitempty"` + ToneRightHz *float64 `json:"toneRightHz,omitempty"` + ToneAmplitude *float64 `json:"toneAmplitude,omitempty"` + PS *string `json:"ps,omitempty"` + RadioText *string `json:"radioText,omitempty"` + PreEmphasisTauUS *float64 `json:"preEmphasisTauUS,omitempty"` + LimiterEnabled *bool `json:"limiterEnabled,omitempty"` + LimiterCeiling *float64 `json:"limiterCeiling,omitempty"` + AudioGain *float64 `json:"audioGain,omitempty"` + PI *string `json:"pi,omitempty"` + PTY *int `json:"pty,omitempty"` + BS412Enabled *bool `json:"bs412Enabled,omitempty"` + BS412ThresholdDBr *float64 `json:"bs412ThresholdDBr,omitempty"` + MpxGain *float64 `json:"mpxGain,omitempty"` } type IngestSaveRequest struct { @@ -528,12 +534,21 @@ func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) { if patch.ToneAmplitude != nil { next.Audio.ToneAmplitude = *patch.ToneAmplitude } + if patch.AudioGain != nil { + next.Audio.Gain = *patch.AudioGain + } if patch.PS != nil { next.RDS.PS = *patch.PS } if patch.RadioText != nil { next.RDS.RadioText = *patch.RadioText } + if patch.PI != nil { + next.RDS.PI = *patch.PI + } + if patch.PTY != nil { + next.RDS.PTY = *patch.PTY + } if patch.PreEmphasisTauUS != nil { next.FM.PreEmphasisTauUS = *patch.PreEmphasisTauUS } @@ -555,6 +570,15 @@ func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) { if patch.RDSInjection != nil { next.FM.RDSInjection = *patch.RDSInjection } + if patch.BS412Enabled != nil { + next.FM.BS412Enabled = *patch.BS412Enabled + } + if patch.BS412ThresholdDBr != nil { + next.FM.BS412ThresholdDBr = *patch.BS412ThresholdDBr + } + if patch.MpxGain != nil { + next.FM.MpxGain = *patch.MpxGain + } if err := next.Validate(); err != nil { s.mu.Unlock() http.Error(w, err.Error(), http.StatusBadRequest) diff --git a/internal/control/ui.html b/internal/control/ui.html index f63de10..ccb6dc3 100644 --- a/internal/control/ui.html +++ b/internal/control/ui.html @@ -6,1067 +6,258 @@ fm-rds-tx
-
-
-

FM-RDS-TX Control Plane

-
Overview first, controls second, diagnostics when needed.
-
-
Backend--
-
ModeControl Plane
-
Live Config--
-
-
-
-
-
connecting
+
+
+

FM-RDS-TX Control Plane

+
Operate confidently: tune fast, inspect state instantly, diagnose only when needed.
+
+
Backend--
+
ModeControl Plane
+
Live Config--
- - -
- - - - - +
+
+
connecting
- -
-
-
-
+
+
+ + + + + + +
+
+ +
+
+
-
+
Carrier
---.-MHz
@@ -1074,535 +265,372 @@ input.input-error { Desired: --
-
-
IDLE
-
Awaiting runtime data
+
Waiting for runtime telemetry
-
-
-
Chunks
-
--
-
-
-
Samples
-
--
-
-
-
Underruns
-
--
-
-
-
Uptime
-
--
-
-
-
Rate
-
--
-
+
Chunks
--
+
Samples
--
+
Underruns
--
+
Uptime
--
+
Rate
--
-
-
-
-
Audio Buffer
-
--
-
-
- -
-
-
-
Stream Health
-
--
-
-
- -
-
-
-
TX Activity
-
--
-
-
- -
+
Audio Buffer
--
+
Stream Health
--
+
TX Activity
--
- - -
-
- +
+
-
+
+
+
-
-
-
+ +
+
+
+
-
-
-

Frequency

-
Live-tunable
- -
+

Frequency

Live + Saved
-
Tune the RF carrier without restarting the control plane. Draft values stay local until you apply them.
- +
Tune without restarting — when TX is running, the change takes effect at the next chunk boundary (~50ms). The desired frequency is also written into config.
- - - - - + + + + +
-
-
- TX Freq - Valid range 65–110 MHz -
-
- - - MHz -
+
TX Freq65–110 MHz
+
MHzlive
-
- - -
+
- - -
-
-
-

Switches

-
Live
- -
+ +
+

Audio & Drive

Mixed Apply Modes
-
These switches apply immediately and show a busy state while the request is in flight.
- -
-
-
Stereo
-
19 kHz pilot + 38 kHz DSB-SC
-
-
-
-
--
-
+
Output Drive and Limiter Ceiling apply live. Pre-emphasis and Input Gain are saved to config and require TX restart to affect the DSP path.
+
+
Output Drive0 – 10
+
--live
- -
-
-
RDS
-
57 kHz subcarrier encoder
-
-
-
-
--
-
+
+
Limiter Ceiling0.5 – 2.0
+
--live
- -
-
-
Limiter
-
MPX peak protection
-
-
-
-
--
-
+
+
Pre-emphasisRegion standard
+
restart
+
+
Input Gain0 – 4
+
--restart
+
+
+
Live fields update immediately. Restart-tagged fields become effective after TX restart.
- - -
-
-
-

RDS Text

-
PS + RT
- -
+ +
+

Test Tones

Diagnostic
-
Edit Program Service and RadioText without losing in-progress typing when the page refreshes itself.
- -
- - - - +
Tone settings are saved to config for the generator path. They do not hot-apply to a running TX engine; restart TX after saving to hear the change. Set amplitude to 0 to disable.
+
+
Left (Hz)
+
Hzrestart
- -
-
- Program Service (PS) - -
0/8
-
-
-
- RadioText (RT) - -
0/64
-
-
+
+
Right (Hz)
+
Hzrestart
-
- - +
+
Amplitude0 – 1.0
+
--restart
+
- -
-
-
-
-

Shortcuts

-
keyboard
- -
+
+
+ +
+

Switches

Live
-
Fast control reference. Shortcuts stay out of the main operator path.
-
-
-
Start TXt
-
Stop TXShiftt
-
Refreshr
-
-
-
Next Freq Preset]
-
Prev Freq Preset[
-
Apply DraftEnter
-
+
+
Stereo
19 kHz pilot + 38 kHz DSB-SC
+
--
+
+
+
Limiter
MPX peak protection
+
--
- - -
-
-

Danger Zone

-
tx control
- -
+ +
+

MPX Compliance

BS.412
-
Fast emergency controls. Nothing hidden here — just clearer separation from normal controls.
-
- - - - +
ITU-R BS.412 limits total MPX power. Mandatory for licensed FM in EU/CH. Changes require TX restart.
+
+
BS.412 Limiter
60 s rolling power window  restart
+
--
-
- Reset Fault moves the runtime back to DEGRADED while the queue settles before running again. +
+
ThresholddBr — 0 = standard
+
--dBrrestart
+
+
+
MPX GainHardware calibration
+
--restart
+
+
Compliance changes are persisted to config and require TX restart before they affect the modulation chain.
- - + +
+

Danger Zone

emergency
+
+
+
Reset Fault moves the runtime back to DEGRADED while the queue settles.
-
- - -
-
-
-
+ +
+
+
+ +
+

Station Identity

Restart required
-
Edit ingest source settings, save to config file, then force a hard reload so the runtime restarts with the new ingest path.
- -
-
-
- Ingest Kind -
-
- -
-
- -
-
- Prebuffer - ms -
-
- -
-
- -
-
- Stall Timeout - ms -
-
- -
-
- -
-
- Reconnect -
-
- -
-
- -
-
- Backoff Initial - ms -
-
- -
-
- -
-
- Backoff Max - ms -
-
- -
-
+
RDS enable applies live. PI and PTY are saved to config and take effect after the next TX restart.
+
+
Enable RDS57 kHz subcarrier
+
--
live
- -
-
Icecast
-
-
-
URL
-
-
-
-
Decoder
-
- -
-
-
-
RadioText Relay
-
- -
-
-
-
RT Prefix
-
-
-
-
RT MaxLen
-
-
-
-
RT Only On Change
-
- -
+
+
PI CodeProgramme Identifier (hex)
+
+
+ +
0x----
+ restart
+
- -
-
SRT
-
-
-
URL
-
-
-
-
Mode
-
- -
-
-
-
Sample Rate
-
-
-
-
Channels
-
-
+
+
Programme TypePTY 0–31
+
+ + restart
- -
-
AES67
-
-
-
SDP Path
-
-
-
-
SDP Inline
-
-
-
-
Multicast Group
-
-
-
-
Port
-
-
-
-
Payload Type
-
-
-
-
Sample Rate
-
-
-
-
Channels
-
-
-
-
Encoding
-
-
-
-
Packet Time
-
-
-
-
Jitter Depth
-
-
-
-
Read Buffer
-
-
-
-
Discovery
-
-
-
-
Discovery Name
-
-
-
-
Discovery Timeout
-
-
-
-
SAP Group
-
-
-
-
SAP Port
-
-
+
+
Identity settings persist immediately in config, but PI / PTY changes appear on-air only after TX restart.
+
+
+ +
+

On-Air Text

Live + Saved
+
+
PS and RadioText apply at the next RDS group boundary (~88ms). Edits stay local until you apply, then update the live encoder and config snapshot together.
+
+ + + + +
+
+
+
Program Service (PS)live
+ +
0/8
+
+
+
+
RadioText (RT)live
+ +
0/64
+
- -
-
- - +
+
+
+
+
+ +
+

Injection Levels

Live + Saved
+
+
Fixed percentages of ±75 kHz deviation. ITU standard: pilot 9%, RDS 4%. When TX is running, changes hot-apply; they are also written back into config.
+
+
Pilot Level19 kHz, 0–20%
+
--%devlive
+
+
+
RDS Injection57 kHz, 0–15%
+
--%devlive
+
- - + + -
- -
-
-
- +
+
+ + +
+
+ +
+

Ingest Config

Saved + Hard Reload
+
+
Changes are saved to the config file and take effect only after a hard reload of the service.
+
+
Kind
+
Prebufferms
+
Reconnect
+
Backoff Initialms
+
Backoff Maxms
+
+
+
Icecast
+
+
URL
+
Decoder
+
RT Relay
+
RT Prefix
+
RT MaxLen
+
Only On Change
+
+
+
+
SRT
+
+
URL
+
Mode
+
Sample Rate
+
Channels
+
+
+
+
AES67
+
+
Multicast Group
+
Port
+
SDP Path
+
Sample Rate
+
Channels
+
Encoding
+
Jitter Depth
+
Discovery
+
Stream Name
+
+
+
+
+
Ingest changes are not hot-applied. Saving writes config and schedules a hard service reload.
+
+
+
+
+ + +
+
+
+ - + -
-
+
+
+
-
-

Transition History

-
recent state shifts
- -
-
-
Keeps runtime escalations visible without scrolling the activity log.
-
-
No transitions yet.
-
-
+

Transition History

state shifts
+
No state transitions recorded yet.
- -
-
-

Fault History

-
recent faults
- -
-
-
Recent fault events for quick ops situational awareness.
-
-
No faults yet.
-
-
-
- - -
-
-
- -
-
-
-
-
-

Activity Log

-
recent events
- -
-
-
- -
-
No events yet.
-
-
-
+

Fault History

recent faults
+
No faults recorded yet.
-
+
-
- + + + +
+
+
+

Activity Log

recent events
+
+
+
No activity recorded yet.
+
+
+
+
+
+
- -