corrected autotrack still working when deactivated
This commit is contained in:
@@ -45,9 +45,6 @@ type DeviceManager struct {
|
||||
lastFreqUpdateTime time.Time // Last time we sent frequency update
|
||||
freqUpdateCooldown time.Duration // Minimum time between updates
|
||||
|
||||
// Cached Ultrabeam state for FlexRadio interlock (avoid mutex contention)
|
||||
ultrabeamMotorsMoving int
|
||||
ultrabeamStateMu sync.RWMutex
|
||||
}
|
||||
|
||||
type SystemStatus struct {
|
||||
@@ -67,7 +64,7 @@ func NewDeviceManager(cfg *config.Config, hub *Hub) *DeviceManager {
|
||||
return &DeviceManager{
|
||||
config: cfg,
|
||||
hub: hub,
|
||||
updateInterval: 1 * time.Second, // Update status every second
|
||||
updateInterval: 200 * time.Millisecond, // Update status every second
|
||||
stopChan: make(chan struct{}),
|
||||
freqThreshold: 25000, // 25 kHz default
|
||||
autoTrackEnabled: true, // Enabled by default
|
||||
@@ -89,12 +86,14 @@ func (dm *DeviceManager) Initialize() error {
|
||||
)
|
||||
|
||||
// Initialize Tuner Genius
|
||||
log.Printf("Initializing TunerGenius: host=%s port=%d", dm.config.Devices.TunerGenius.Host, dm.config.Devices.TunerGenius.Port)
|
||||
dm.tunerGenius = tunergenius.New(
|
||||
dm.config.Devices.TunerGenius.Host,
|
||||
dm.config.Devices.TunerGenius.Port,
|
||||
)
|
||||
|
||||
// Initialize Antenna Genius
|
||||
log.Printf("Initializing AntennaGenius: host=%s port=%d", dm.config.Devices.AntennaGenius.Host, dm.config.Devices.AntennaGenius.Port)
|
||||
dm.antennaGenius = antennagenius.New(
|
||||
dm.config.Devices.AntennaGenius.Host,
|
||||
dm.config.Devices.AntennaGenius.Port,
|
||||
@@ -123,18 +122,6 @@ func (dm *DeviceManager) Initialize() error {
|
||||
dm.config.Devices.FlexRadio.InterlockName,
|
||||
)
|
||||
|
||||
// Set callback to check if transmit is allowed (based on Ultrabeam motors)
|
||||
// Use cached state to avoid mutex contention with update loop
|
||||
dm.flexRadio.SetTransmitCheckCallback(func() bool {
|
||||
dm.ultrabeamStateMu.RLock()
|
||||
motorsMoving := dm.ultrabeamMotorsMoving
|
||||
dm.ultrabeamStateMu.RUnlock()
|
||||
// Block transmit if motors are moving
|
||||
allowed := motorsMoving == 0
|
||||
log.Printf("FlexRadio PTT check: motorsMoving=%d, transmit=%v", motorsMoving, allowed)
|
||||
return allowed
|
||||
})
|
||||
|
||||
// Set callback for immediate frequency changes (no waiting for update cycle)
|
||||
dm.flexRadio.SetFrequencyChangeCallback(func(freqMHz float64) {
|
||||
dm.handleFrequencyChange(freqMHz)
|
||||
@@ -213,6 +200,11 @@ func (dm *DeviceManager) Start() error {
|
||||
// This provides instant auto-track response instead of waiting for updateStatus cycle
|
||||
func (dm *DeviceManager) handleFrequencyChange(freqMHz float64) {
|
||||
// Check if ultrabeam is initialized
|
||||
// Check if auto-track is enabled
|
||||
if !dm.autoTrackEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
if dm.ultrabeam == nil {
|
||||
return
|
||||
}
|
||||
@@ -359,34 +351,6 @@ func (dm *DeviceManager) updateStatus() {
|
||||
dm.ultrabeamDirection = ubStatus.Direction
|
||||
}
|
||||
|
||||
// Cache motors state for FlexRadio interlock callback
|
||||
dm.ultrabeamStateMu.Lock()
|
||||
previousMotors := dm.ultrabeamMotorsMoving
|
||||
dm.ultrabeamMotorsMoving = ubStatus.MotorsMoving
|
||||
dm.ultrabeamStateMu.Unlock()
|
||||
|
||||
// Proactively update FlexRadio interlock when motor state changes
|
||||
if previousMotors != ubStatus.MotorsMoving {
|
||||
if ubStatus.MotorsMoving > 0 {
|
||||
log.Printf("Ultrabeam: Motors STARTED (bitmask=%d)", ubStatus.MotorsMoving)
|
||||
// PROACTIVELY block transmit - don't wait for PTT_REQUESTED
|
||||
log.Printf("DEBUG: About to call ForceInterlockState(false), flexRadio=%v", dm.flexRadio != nil)
|
||||
if dm.flexRadio != nil {
|
||||
dm.flexRadio.ForceInterlockState(false)
|
||||
} else {
|
||||
log.Printf("DEBUG: FlexRadio is nil, cannot force interlock")
|
||||
}
|
||||
} else {
|
||||
log.Printf("Ultrabeam: Motors STOPPED")
|
||||
// PROACTIVELY allow transmit again
|
||||
log.Printf("DEBUG: About to call ForceInterlockState(true), flexRadio=%v", dm.flexRadio != nil)
|
||||
if dm.flexRadio != nil {
|
||||
dm.flexRadio.ForceInterlockState(true)
|
||||
} else {
|
||||
log.Printf("DEBUG: FlexRadio is nil, cannot force interlock")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("Ultrabeam error: %v", err)
|
||||
}
|
||||
@@ -402,88 +366,86 @@ func (dm *DeviceManager) updateStatus() {
|
||||
}
|
||||
|
||||
// Auto frequency tracking: Update Ultrabeam when radio frequency differs from Ultrabeam
|
||||
// Priority: FlexRadio (fast) > TunerGenius (slow backup)
|
||||
var radioFreqKhz int
|
||||
var radioSource string
|
||||
if dm.autoTrackEnabled {
|
||||
// TunerGenius tracking (FlexRadio uses immediate callback)
|
||||
var radioFreqKhz int
|
||||
var radioSource string
|
||||
|
||||
if dm.flexRadio != nil && status.FlexRadio != nil && status.FlexRadio.Connected && status.FlexRadio.Frequency > 0 {
|
||||
// Use FlexRadio frequency (in MHz, convert to kHz)
|
||||
radioFreqKhz = int(status.FlexRadio.Frequency * 1000)
|
||||
radioSource = "FlexRadio"
|
||||
} else if dm.autoTrackEnabled && status.TunerGenius != nil && status.TunerGenius.Connected {
|
||||
// Fallback to TunerGenius frequency (already in kHz)
|
||||
radioFreqKhz = int(status.TunerGenius.FreqA)
|
||||
radioSource = "TunerGenius"
|
||||
}
|
||||
if status.TunerGenius != nil && status.TunerGenius.Connected {
|
||||
// Fallback to TunerGenius frequency (already in kHz)
|
||||
radioFreqKhz = int(status.TunerGenius.FreqA)
|
||||
radioSource = "TunerGenius"
|
||||
}
|
||||
|
||||
if radioFreqKhz > 0 && status.Ultrabeam != nil && status.Ultrabeam.Connected {
|
||||
ultrabeamFreqKhz := status.Ultrabeam.Frequency // Ultrabeam frequency in kHz
|
||||
if radioFreqKhz > 0 && status.Ultrabeam != nil && status.Ultrabeam.Connected {
|
||||
ultrabeamFreqKhz := status.Ultrabeam.Frequency // Ultrabeam frequency in kHz
|
||||
|
||||
// Only do auto-track if frequency is in Ultrabeam range (40M-6M: 7000-54000 kHz)
|
||||
// This prevents retraction when slice is closed (FreqA becomes 0) or on out-of-range bands
|
||||
if radioFreqKhz >= 7000 && radioFreqKhz <= 54000 {
|
||||
freqDiff := radioFreqKhz - ultrabeamFreqKhz
|
||||
if freqDiff < 0 {
|
||||
freqDiff = -freqDiff
|
||||
}
|
||||
// Only do auto-track if frequency is in Ultrabeam range (40M-6M: 7000-54000 kHz)
|
||||
// This prevents retraction when slice is closed (FreqA becomes 0) or on out-of-range bands
|
||||
if radioFreqKhz >= 7000 && radioFreqKhz <= 54000 {
|
||||
freqDiff := radioFreqKhz - ultrabeamFreqKhz
|
||||
if freqDiff < 0 {
|
||||
freqDiff = -freqDiff
|
||||
}
|
||||
|
||||
// Convert diff to Hz for comparison with threshold (which is in Hz)
|
||||
freqDiffHz := freqDiff * 1000
|
||||
// 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 {
|
||||
if freqDiffHz >= dm.freqThreshold {
|
||||
// Use user's explicitly set direction, or fallback to current Ultrabeam direction
|
||||
directionToUse := dm.ultrabeamDirection
|
||||
if !dm.ultrabeamDirectionSet && status.Ultrabeam.Direction != 0 {
|
||||
directionToUse = status.Ultrabeam.Direction
|
||||
}
|
||||
// Don't send command if motors are already moving
|
||||
if status.Ultrabeam.MotorsMoving == 0 {
|
||||
if freqDiffHz >= dm.freqThreshold {
|
||||
// Use user's explicitly set direction, or fallback to current Ultrabeam direction
|
||||
directionToUse := dm.ultrabeamDirection
|
||||
if !dm.ultrabeamDirectionSet && 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 (%s): Frequency differs by %d kHz, updating Ultrabeam to %d kHz (direction=%d)", radioSource, freqDiff, radioFreqKhz, directionToUse)
|
||||
|
||||
// Send to Ultrabeam with saved or current direction
|
||||
if err := dm.ultrabeam.SetFrequency(radioFreqKhz, directionToUse); err != nil {
|
||||
log.Printf("Auto-track: Failed to update Ultrabeam: %v (will retry)", err)
|
||||
// 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: Successfully sent frequency to Ultrabeam")
|
||||
dm.lastFreqUpdateTime = time.Now() // Update cooldown timer
|
||||
log.Printf("Auto-track (%s): Frequency differs by %d kHz, updating Ultrabeam to %d kHz (direction=%d)", radioSource, freqDiff, radioFreqKhz, directionToUse)
|
||||
|
||||
// Send to Ultrabeam with saved or current direction
|
||||
if err := dm.ultrabeam.SetFrequency(radioFreqKhz, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If out of range, simply skip auto-track but continue with status broadcast
|
||||
}
|
||||
// If out of range, simply skip auto-track but continue with status broadcast
|
||||
|
||||
// Solar Data (fetched every 15 minutes, cached)
|
||||
if solarData, err := dm.solarClient.GetSolarData(); err == nil {
|
||||
status.Solar = solarData
|
||||
} else {
|
||||
log.Printf("Solar data error: %v", err)
|
||||
}
|
||||
|
||||
// Weather Data (fetched every 10 minutes, cached)
|
||||
if weatherData, err := dm.weatherClient.GetWeatherData(); err == nil {
|
||||
status.Weather = weatherData
|
||||
} else {
|
||||
log.Printf("Weather data error: %v", err)
|
||||
}
|
||||
|
||||
// Update cached status
|
||||
dm.statusMu.Lock()
|
||||
dm.lastStatus = status
|
||||
dm.statusMu.Unlock()
|
||||
|
||||
// Broadcast to all connected clients
|
||||
if dm.hub != nil {
|
||||
dm.hub.BroadcastStatusUpdate(status)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Solar Data (fetched every 15 minutes, cached)
|
||||
if solarData, err := dm.solarClient.GetSolarData(); err == nil {
|
||||
status.Solar = solarData
|
||||
} else {
|
||||
log.Printf("Solar data error: %v", err)
|
||||
}
|
||||
|
||||
// Weather Data (fetched every 10 minutes, cached)
|
||||
if weatherData, err := dm.weatherClient.GetWeatherData(); err == nil {
|
||||
status.Weather = weatherData
|
||||
} else {
|
||||
log.Printf("Weather data error: %v", err)
|
||||
}
|
||||
|
||||
// Update cached status
|
||||
dm.statusMu.Lock()
|
||||
dm.lastStatus = status
|
||||
dm.statusMu.Unlock()
|
||||
|
||||
// Broadcast to all connected clients
|
||||
if dm.hub != nil {
|
||||
dm.hub.BroadcastStatusUpdate(status)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (dm *DeviceManager) GetStatus() *SystemStatus {
|
||||
|
||||
Reference in New Issue
Block a user