first commit
This commit is contained in:
92
backend/doppler/calculator.go
Normal file
92
backend/doppler/calculator.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package doppler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"SatMaster/backend/propagator"
|
||||
)
|
||||
|
||||
const (
|
||||
SpeedOfLight = 299792.458 // km/s
|
||||
)
|
||||
|
||||
// Calculator computes Doppler-shifted frequencies.
|
||||
type Calculator struct {
|
||||
mu sync.RWMutex
|
||||
nominalDown float64 // Hz
|
||||
nominalUp float64 // Hz
|
||||
obsLat float64
|
||||
obsLon float64
|
||||
obsAlt float64
|
||||
}
|
||||
|
||||
func NewCalculator() *Calculator {
|
||||
return &Calculator{}
|
||||
}
|
||||
|
||||
func (c *Calculator) SetObserver(lat, lon, altM float64) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.obsLat = lat
|
||||
c.obsLon = lon
|
||||
c.obsAlt = altM
|
||||
}
|
||||
|
||||
func (c *Calculator) SetNominal(downHz, upHz float64) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.nominalDown = downHz
|
||||
c.nominalUp = upHz
|
||||
}
|
||||
|
||||
// Correct computes Doppler-corrected downlink and uplink frequencies.
|
||||
// Returns (downlinkHz, uplinkHz).
|
||||
func (c *Calculator) Correct(pos *propagator.SatPosition, obs propagator.Observer, _ time.Time) (float64, float64) {
|
||||
c.mu.RLock()
|
||||
nomDown := c.nominalDown
|
||||
nomUp := c.nominalUp
|
||||
c.mu.RUnlock()
|
||||
|
||||
if nomDown == 0 && nomUp == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
if pos == nil {
|
||||
return nomDown, nomUp
|
||||
}
|
||||
|
||||
// Range rate in km/s (positive = receding, negative = approaching)
|
||||
rr := pos.RangeRate
|
||||
|
||||
// Doppler factor: f_received = f_nominal * (1 - v/c)
|
||||
// For downlink: satellite is the transmitter
|
||||
dopplerFactor := 1.0 - rr/SpeedOfLight
|
||||
|
||||
correctedDown := nomDown * dopplerFactor
|
||||
// For uplink: we pre-correct in reverse so the satellite receives nominal
|
||||
correctedUp := nomUp / dopplerFactor
|
||||
|
||||
return correctedDown, correctedUp
|
||||
}
|
||||
|
||||
// ShiftHz returns the Doppler shift in Hz for a given nominal frequency.
|
||||
func ShiftHz(nominalHz, rangeRateKmS float64) float64 {
|
||||
return nominalHz * (-rangeRateKmS / SpeedOfLight)
|
||||
}
|
||||
|
||||
// RangeRateFromPositions computes range rate from two consecutive positions.
|
||||
func RangeRateFromPositions(prev, curr *propagator.SatPosition, dt float64) float64 {
|
||||
prevRange := prev.Range
|
||||
currRange := curr.Range
|
||||
return (currRange - prevRange) / dt
|
||||
}
|
||||
|
||||
// FormatShift formats a Doppler shift in Hz for display.
|
||||
func FormatShift(shiftHz float64) string {
|
||||
if math.Abs(shiftHz) >= 1000 {
|
||||
return fmt.Sprintf("%+.2f kHz", shiftHz/1000)
|
||||
}
|
||||
return fmt.Sprintf("%+.0f Hz", shiftHz)
|
||||
}
|
||||
Reference in New Issue
Block a user