package main import ( "encoding/json" "fmt" "math" "os" ) type Point struct { Datetime string `json:"datetime"` Latitude float64 `json:"latitude"` Longitude float64 `json:"longitude"` Altitude float64 `json:"altitude"` } type Stage struct { Stage string `json:"stage"` Trajectory []Point `json:"trajectory"` } type Prediction struct { Prediction []Stage `json:"prediction"` } func haversine(lat1, lon1, lat2, lon2 float64) float64 { R := 6371000.0 phi1, phi2 := lat1*math.Pi/180, lat2*math.Pi/180 dphi := (lat2 - lat1) * math.Pi / 180 dlam := (lon2 - lon1) * math.Pi / 180 a := math.Sin(dphi/2)*math.Sin(dphi/2) + math.Cos(phi1)*math.Cos(phi2)*math.Sin(dlam/2)*math.Sin(dlam/2) return R * 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) } func load(path string) Prediction { data, _ := os.ReadFile(path) var p Prediction json.Unmarshal(data, &p) return p } func main() { our := load("c:/tmp/our.json") taw := load("c:/tmp/tawhiri.json") // Find burst and landing points var ourBurst, ourLand, tawBurst, tawLand Point for _, s := range our.Prediction { t := s.Trajectory if s.Stage == "ascent" { ourBurst = t[len(t)-1] } if s.Stage == "descent" { ourLand = t[len(t)-1] } } for _, s := range taw.Prediction { t := s.Trajectory if s.Stage == "ascent" { tawBurst = t[len(t)-1] } if s.Stage == "descent" { tawLand = t[len(t)-1] } } fmt.Println("=== Burst Point ===") fmt.Printf(" Our: lat=%.4f, lon=%.4f, alt=%.0f, time=%s\n", ourBurst.Latitude, ourBurst.Longitude, ourBurst.Altitude, ourBurst.Datetime) fmt.Printf(" Tawhiri: lat=%.4f, lon=%.4f, alt=%.0f, time=%s\n", tawBurst.Latitude, tawBurst.Longitude, tawBurst.Altitude, tawBurst.Datetime) burstDist := haversine(ourBurst.Latitude, ourBurst.Longitude, tawBurst.Latitude, tawBurst.Longitude) fmt.Printf(" Distance: %.2f km\n", burstDist/1000) fmt.Println() fmt.Println("=== Landing Point ===") fmt.Printf(" Our: lat=%.4f, lon=%.4f, alt=%.0f, time=%s\n", ourLand.Latitude, ourLand.Longitude, ourLand.Altitude, ourLand.Datetime) fmt.Printf(" Tawhiri: lat=%.4f, lon=%.4f, alt=%.0f, time=%s\n", tawLand.Latitude, tawLand.Longitude, tawLand.Altitude, tawLand.Datetime) landDist := haversine(ourLand.Latitude, ourLand.Longitude, tawLand.Latitude, tawLand.Longitude) fmt.Printf(" Distance: %.2f km\n", landDist/1000) fmt.Println() fmt.Println("=== Trajectory Comparison (every 10 min) ===") ourPts := map[string]Point{} tawPts := map[string]Point{} for _, s := range our.Prediction { for _, p := range s.Trajectory { ourPts[p.Datetime] = p } } for _, s := range taw.Prediction { for _, p := range s.Trajectory { tawPts[p.Datetime] = p } } // Collect common times var common []string for _, s := range our.Prediction { for _, p := range s.Trajectory { if _, ok := tawPts[p.Datetime]; ok { common = append(common, p.Datetime) } } } for i, t := range common { if i%10 == 0 { o := ourPts[t] tw := tawPts[t] d := haversine(o.Latitude, o.Longitude, tw.Latitude, tw.Longitude) fmt.Printf(" %s: dist=%.2f km (our: %.3f,%.3f vs taw: %.3f,%.3f)\n", t, d/1000, o.Latitude, o.Longitude, tw.Latitude, tw.Longitude) } } }