This commit is contained in:
2026-01-10 16:04:38 +01:00
parent f172678560
commit 0ce18d87bc
13 changed files with 779 additions and 226 deletions

View File

@@ -34,6 +34,13 @@ type DeviceManager struct {
updateInterval time.Duration
stopChan chan struct{}
// Auto frequency tracking
freqThreshold int // Threshold for triggering update (Hz)
autoTrackEnabled bool
ultrabeamDirection int // User-selected direction (0=normal, 1=180, 2=bi-dir)
lastFreqUpdateTime time.Time // Last time we sent frequency update
freqUpdateCooldown time.Duration // Minimum time between updates
}
type SystemStatus struct {
@@ -50,10 +57,14 @@ type SystemStatus struct {
func NewDeviceManager(cfg *config.Config, hub *Hub) *DeviceManager {
return &DeviceManager{
config: cfg,
hub: hub,
updateInterval: 1 * time.Second, // Update status every second
stopChan: make(chan struct{}),
config: cfg,
hub: hub,
updateInterval: 1 * time.Second, // Update status every second
stopChan: make(chan struct{}),
freqThreshold: 25000, // 25 kHz default
autoTrackEnabled: true, // Enabled by default
ultrabeamDirection: 0, // Normal direction by default
freqUpdateCooldown: 2 * time.Second, // Wait 2 seconds between updates
}
}
@@ -232,10 +243,68 @@ func (dm *DeviceManager) updateStatus() {
// Ultrabeam
if ubStatus, err := dm.ultrabeam.GetStatus(); err == nil {
status.Ultrabeam = ubStatus
// Sync direction with Ultrabeam if not yet set (first time or after restart)
// This prevents auto-track from using wrong direction before user changes it
if dm.ultrabeamDirection == 0 && ubStatus.Direction != 0 {
dm.ultrabeamDirection = ubStatus.Direction
log.Printf("Auto-track: Initialized direction from Ultrabeam: %d", dm.ultrabeamDirection)
}
} else {
log.Printf("Ultrabeam error: %v", err)
}
// Auto frequency tracking: Update Ultrabeam when TunerGenius frequency differs from Ultrabeam
if dm.autoTrackEnabled && status.TunerGenius != nil && status.TunerGenius.Connected && status.Ultrabeam != nil && status.Ultrabeam.Connected {
tunerFreqKhz := int(status.TunerGenius.FreqA) // TunerGenius frequency is already in kHz
ultrabeamFreqKhz := status.Ultrabeam.Frequency // Ultrabeam frequency in kHz
// Ignore invalid frequencies or out of Ultrabeam range (40M-6M)
// This prevents retraction when slice is closed (FreqA becomes 0)
// Ultrabeam VL2.3 only covers 7000-54000 kHz (40M to 6M)
if tunerFreqKhz < 7000 || tunerFreqKhz > 54000 {
return // Out of range, skip auto-track
}
freqDiff := tunerFreqKhz - ultrabeamFreqKhz
if freqDiff < 0 {
freqDiff = -freqDiff
}
// Convert diff to Hz for comparison with threshold (which is in Hz)
freqDiffHz := freqDiff * 1000
// Don't send command if motors are already moving
if status.Ultrabeam.MotorsMoving != 0 {
// Motors moving - wait for them to finish
return
}
if freqDiffHz >= dm.freqThreshold {
// Use current Ultrabeam direction if user hasn't explicitly set one
directionToUse := dm.ultrabeamDirection
if directionToUse == 0 && status.Ultrabeam.Direction != 0 {
directionToUse = status.Ultrabeam.Direction
}
// Check cooldown to prevent rapid fire commands
timeSinceLastUpdate := time.Since(dm.lastFreqUpdateTime)
if timeSinceLastUpdate < dm.freqUpdateCooldown {
log.Printf("Auto-track: Cooldown active (%v remaining), skipping update", dm.freqUpdateCooldown-timeSinceLastUpdate)
} else {
log.Printf("Auto-track: Frequency differs by %d kHz, updating Ultrabeam to %d kHz (direction=%d)", freqDiff, tunerFreqKhz, directionToUse)
// Send to Ultrabeam with saved or current direction
if err := dm.ultrabeam.SetFrequency(tunerFreqKhz, directionToUse); err != nil {
log.Printf("Auto-track: Failed to update Ultrabeam: %v (will retry)", err)
} else {
log.Printf("Auto-track: Successfully sent frequency to Ultrabeam")
dm.lastFreqUpdateTime = time.Now() // Update cooldown timer
}
}
}
}
// Solar Data (fetched every 15 minutes, cached)
if solarData, err := dm.solarClient.GetSolarData(); err == nil {
status.Solar = solarData
@@ -298,3 +367,13 @@ func (dm *DeviceManager) RotatorGenius() *rotatorgenius.Client {
func (dm *DeviceManager) Ultrabeam() *ultrabeam.Client {
return dm.ultrabeam
}
func (dm *DeviceManager) SetAutoTrack(enabled bool, thresholdHz int) {
dm.autoTrackEnabled = enabled
dm.freqThreshold = thresholdHz
}
func (dm *DeviceManager) SetUltrabeamDirection(direction int) {
dm.ultrabeamDirection = direction
log.Printf("Ultrabeam direction set to: %d", direction)
}

View File

@@ -57,6 +57,7 @@ func (s *Server) SetupRoutes() *http.ServeMux {
// Ultrabeam endpoints
mux.HandleFunc("/api/ultrabeam/frequency", s.handleUltrabeamFrequency)
mux.HandleFunc("/api/ultrabeam/retract", s.handleUltrabeamRetract)
mux.HandleFunc("/api/ultrabeam/autotrack", s.handleUltrabeamAutoTrack)
// Tuner endpoints
mux.HandleFunc("/api/tuner/operate", s.handleTunerOperate)
@@ -435,6 +436,27 @@ func (s *Server) handleUltrabeamRetract(w http.ResponseWriter, r *http.Request)
s.sendJSON(w, map[string]string{"status": "ok"})
}
func (s *Server) handleUltrabeamAutoTrack(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req struct {
Enabled bool `json:"enabled"`
Threshold int `json:"threshold"` // kHz
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
s.deviceManager.SetAutoTrack(req.Enabled, req.Threshold*1000) // Convert kHz to Hz
s.sendJSON(w, map[string]string{"status": "ok"})
}
func (s *Server) sendJSON(w http.ResponseWriter, data interface{}) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)