feat: move stuff to numerics

This commit is contained in:
Anatoly Antonov 2026-05-30 06:38:38 +09:00
parent 465ad00f7b
commit b7fd7046ff
5 changed files with 119 additions and 29 deletions

View file

@ -1,7 +1,6 @@
package engine
import (
"math"
"sort"
"predictor-refactored/internal/numerics"
@ -19,10 +18,7 @@ func Sum(models ...Model) Model {
return func(t float64, s State) State {
var sum State
for _, m := range models {
d := m(t, s)
sum.Lat += d.Lat
sum.Lng += d.Lng
sum.Altitude += d.Altitude
sum = numerics.AddGeo(sum, m(t, s))
}
return sum
}
@ -44,9 +40,8 @@ func ConstantRate(rate float64) Model {
//
// using the NASA atmosphere model for rho. Equivalent to Tawhiri's drag_descent.
func ParachuteDescent(seaLevelRate float64) Model {
k := seaLevelRate * 1.1045
return func(_ float64, s State) State {
return State{Altitude: -k / math.Sqrt(numerics.NasaDensity(s.Altitude))}
return State{Altitude: numerics.DragTerminalVelocity(seaLevelRate, s.Altitude)}
}
}
@ -79,15 +74,13 @@ func Piecewise(segments []RateSegment) Model {
}
// WindTransport returns a model that moves laterally at the wind velocity
// sampled from field. Vertical component is zero. Wind components in m/s
// are converted to deg/s on Earth's surface using R = 6371009 m.
// sampled from field. The vertical component is zero. Sampling and the
// non-fatal "above_model" event live here (orchestration); the m/s → deg/s
// conversion is numerics.WindToGeoRate.
//
// If events is non-nil, an "above_model" event is emitted whenever the
// wind field reports altitude above the highest pressure level.
func WindTransport(field weather.WindField, events *EventSink) Model {
const earthR = 6371009.0
const piOver180 = math.Pi / 180.0
const degPerRad = 180.0 / math.Pi
return func(t float64, s State) State {
sample, err := field.Wind(t, s.Lat, s.Lng, s.Altitude)
if err != nil {
@ -97,10 +90,7 @@ func WindTransport(field weather.WindField, events *EventSink) Model {
events.Emit("above_model", t, s,
"altitude exceeded the highest pressure level of the wind dataset; samples extrapolated")
}
r := earthR + s.Altitude
return State{
Lat: degPerRad * sample.V / r,
Lng: degPerRad * sample.U / (r * math.Cos(s.Lat*piOver180)),
}
dLat, dLng := numerics.WindToGeoRate(sample.U, sample.V, s.Lat, s.Altitude)
return State{Lat: dLat, Lng: dLng}
}
}