Satellite pass prediction and contact window computation
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

ocaml-linkedin: apply dune fmt

Pure formatting changes from `dune fmt`: doc comment placement moves
from above the binding to below it for `type`s, multi-line `match`
expressions collapse onto one line where they fit, and infix operator
applications pick up spaces (`Soup.($?)` -> `Soup.( $? )`). No
semantic changes.

+101 -1
+93
README.md
··· 1 + # contact 2 + 3 + Satellite pass prediction and contact window computation. 4 + 5 + `contact` computes when a satellite is visible from a ground station: 6 + acquisition of signal (AOS), loss of signal (LOS), maximum elevation, 7 + and pass duration. Orbit propagation is delegated to 8 + [sgp4][sgp4] (SGP4 from the NORAD General Perturbations models) and 9 + coordinate transforms to [coordinate][coord]. 10 + 11 + [sgp4]: https://tangled.org/gazagnaire.org/ocaml-sgp4 12 + [coord]: https://tangled.org/gazagnaire.org/ocaml-coordinate 13 + 14 + ## Installation 15 + 16 + Install with opam: 17 + 18 + <!-- $MDX skip --> 19 + ```sh 20 + $ opam install contact 21 + ``` 22 + 23 + If opam cannot find the package, it may not yet be released in the 24 + public `opam-repository`. Add the overlay repository, then install 25 + it: 26 + 27 + <!-- $MDX skip --> 28 + ```sh 29 + $ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git 30 + $ opam update 31 + $ opam install contact 32 + ``` 33 + 34 + ## Usage 35 + 36 + Parse a Two-Line Element set, define a ground station, and predict 37 + all visible passes over a time window. The TLE below is an 38 + idealised ISS element set with epoch 2026-01-01; passes are computed 39 + from that epoch: 40 + 41 + ```ocaml 42 + # let tle_string = 43 + "ISS (ZARYA)\n\ 44 + 1 25544U 98067A 26001.00000000 .00001764 00000+0 39538-4 0 9991\n\ 45 + 2 25544 51.6416 45.0000 0008003 90.0000 0.0000 15.49560000 15" in 46 + let tle = Result.get_ok (Sgp4.parse_tle_string tle_string) in 47 + let la = Contact.ground_station ~lat:34.05 ~lon:(-118.25) ~alt:0.071 in 48 + let passes = Contact.predict tle la ~duration_days:3.0 in 49 + List.length passes 50 + - : int = 15 51 + ``` 52 + 53 + Each pass carries AOS/LOS Unix timestamps, ISO-8601 strings, the 54 + peak elevation in degrees, and duration in seconds: 55 + 56 + ```ocaml 57 + # let tle_string = 58 + "ISS (ZARYA)\n\ 59 + 1 25544U 98067A 26001.00000000 .00001764 00000+0 39538-4 0 9991\n\ 60 + 2 25544 51.6416 45.0000 0008003 90.0000 0.0000 15.49560000 15" in 61 + let tle = Result.get_ok (Sgp4.parse_tle_string tle_string) in 62 + let la = Contact.ground_station ~lat:34.05 ~lon:(-118.25) ~alt:0.071 in 63 + match Contact.predict tle la ~duration_days:3.0 with 64 + | { max_elevation; duration; _ } :: _ -> 65 + Fmt.str "first pass: %.1f deg peak, %.0fs long" 66 + max_elevation duration 67 + | [] -> "no passes" 68 + - : string = "first pass: 61.6 deg peak, 630s long" 69 + ``` 70 + 71 + `Contact.elevation` computes a point-in-time elevation angle instead 72 + of scanning for full pass windows: 73 + 74 + ```ocaml 75 + # let tle_string = 76 + "ISS (ZARYA)\n\ 77 + 1 25544U 98067A 26001.00000000 .00001764 00000+0 39538-4 0 9991\n\ 78 + 2 25544 51.6416 45.0000 0008003 90.0000 0.0000 15.49560000 15" in 79 + let tle = Result.get_ok (Sgp4.parse_tle_string tle_string) in 80 + let la = Contact.ground_station ~lat:34.05 ~lon:(-118.25) ~alt:0.071 in 81 + let epoch_unix = Sgp4.epoch_unix tle in 82 + match Contact.elevation tle la epoch_unix with 83 + | Some el -> Fmt.str "%.2f" el 84 + | None -> "N/A" 85 + - : string = "-43.84" 86 + ``` 87 + 88 + A negative elevation means the satellite is below the horizon; pass 89 + prediction filters these out using `min_elevation` (default 5°). 90 + 91 + ## Licence 92 + 93 + ISC
+1
contact.opam
··· 22 22 "ptime" {>= "1.0"} 23 23 "fmt" {>= "0.9"} 24 24 "alcotest" {with-test} 25 + "mdx" {with-test} 25 26 "odoc" {with-doc} 26 27 ] 27 28 build: [
+4
dune
··· 1 1 (env 2 2 (dev 3 3 (flags :standard %{dune-warnings}))) 4 + 5 + (mdx 6 + (files README.md) 7 + (libraries contact sgp4 fmt))
+3 -1
dune-project
··· 1 1 (lang dune 3.21) 2 + (using mdx 0.4) 2 3 3 4 (name contact) 4 5 ··· 30 31 (vec3 (>= 0.1)) 31 32 (ptime (>= 1.0)) 32 33 (fmt (>= 0.9)) 33 - (alcotest :with-test))) 34 + (alcotest :with-test) 35 + (mdx :with-test)))