Select the types of activity you want to include in your feed.
fix(ocaml-requests): update tests and fuzz for cstruct→Bytes migration
Test files still referenced Cstruct.t where the API now uses bytes. Fixed all H2 frame, HPACK, client, and connection tests. Fixed fuzz test. 330 tests pass.
···1414 let b = Bytes.of_string buf in
1515 let get i = Int64.float_of_bits (Bytes.get_int64_le b (i * 8)) in
1616 let x = get 0 and y = get 1 and z = get 2 and gmst = get 3 in
1717- if Float.is_finite x && Float.is_finite y && Float.is_finite z
1818- && Float.is_finite gmst
1717+ if
1818+ Float.is_finite x && Float.is_finite y && Float.is_finite z
1919+ && Float.is_finite gmst
1920 then begin
2021 let v = Coordinate.vec3 x y z in
2122 let ecef = Coordinate.teme_to_ecef ~gmst v in
···4041 let lat = Float.rem (get 0) 90. in
4142 let lon = Float.rem (get 1) 180. in
4243 let alt = abs_float (Float.rem (get 2) 50000.) in
4343- if Float.is_finite lat && Float.is_finite lon && Float.is_finite alt
4444- then begin
4444+ if Float.is_finite lat && Float.is_finite lon && Float.is_finite alt then begin
4545 let g = { Coordinate.lat; lon; alt } in
4646 let ecef = Coordinate.geodetic_to_ecef g in
4747 let g' = Coordinate.ecef_to_geodetic ecef in
···6060 let g = Coordinate.gmst_of_unix t in
6161 guard (Float.is_finite g);
6262 guard (g >= 0.);
6363- guard (g < 2. *. Float.pi +. 0.001)
6363+ guard (g < (2. *. Float.pi) +. 0.001)
6464 end
65656666let suite =
+17-31
lib/coordinate.ml
···2828(* ------------------------------------------------------------------ *)
29293030let vec3 x y z = { x; y; z }
3131-3232-let vec3_length v =
3333- sqrt ((v.x *. v.x) +. (v.y *. v.y) +. (v.z *. v.z))
3131+let vec3_length v = sqrt ((v.x *. v.x) +. (v.y *. v.y) +. (v.z *. v.z))
34323533let normalize_angle a =
3634 let a = Float.rem a twopi in
···4139(* ------------------------------------------------------------------ *)
42404341let julian_date_of_unix t = (t /. 86400.) +. 2440587.5
4444-4542let unix_of_julian_date jd = (jd -. 2440587.5) *. 86400.
46434744let julian_centuries unix_t =
···50475148(** GMST using the IAU 1982 model.
52495353- Reference: Vallado, Eq. 3-47.
5454- GMST = 67310.54841 + (876600h + 8640184.812866)T
5555- + 0.093104 T^2 - 6.2e-6 T^3
5656- where T is Julian centuries from J2000.0. *)
5050+ Reference: Vallado, Eq. 3-47. GMST = 67310.54841 + (876600h +
5151+ 8640184.812866)T
5252+ + 0.093104 T^2 - 6.2e-6 T^3 where T is Julian centuries from J2000.0. *)
5753let gmst_of_unix unix_time =
5854 let t = julian_centuries unix_time in
5955 let gmst_sec =
6056 67310.54841
6161- +. ((876600.0 *. 3600. +. 8640184.812866) *. t)
5757+ +. (((876600.0 *. 3600.) +. 8640184.812866) *. t)
6258 +. (0.093104 *. t *. t)
6359 -. (6.2e-6 *. t *. t *. t)
6460 in
···7066(* Frame transforms *)
7167(* ------------------------------------------------------------------ *)
72687373-(** Z-axis rotation by angle theta.
7474- Rz(θ) = [[cos θ, sin θ, 0], [-sin θ, cos θ, 0], [0, 0, 1]] *)
6969+(** Z-axis rotation by angle theta. Rz(θ) =
7070+ [[cos θ, sin θ, 0], [-sin θ, cos θ, 0], [0, 0, 1]] *)
7571let rotate_z theta v =
7672 let c = cos theta and s = sin theta in
7777- {
7878- x = (c *. v.x) +. (s *. v.y);
7979- y = (-.s *. v.x) +. (c *. v.y);
8080- z = v.z;
8181- }
7373+ { x = (c *. v.x) +. (s *. v.y); y = (-.s *. v.x) +. (c *. v.y); z = v.z }
82748375let teme_to_ecef ~gmst v = rotate_z gmst v
8476let ecef_to_teme ~gmst v = rotate_z (-.gmst) v
···91839284(** Bowring's iterative method for ECEF → geodetic.
93859494- Reference: Vallado Algorithm 12, Montenbruck & Gill §5.3.
9595- Converges in 2-3 iterations for typical positions. *)
8686+ Reference: Vallado Algorithm 12, Montenbruck & Gill §5.3. Converges in 2-3
8787+ iterations for typical positions. *)
9688let ecef_to_geodetic v =
9789 let a = earth_radius in
9890 let f = earth_flattening in
9999- let e2 = (2. *. f) -. (f *. f) in (* first eccentricity squared *)
9191+ let e2 = (2. *. f) -. (f *. f) in
9292+ (* first eccentricity squared *)
10093 let p = sqrt ((v.x *. v.x) +. (v.y *. v.y)) in
10194 let lon_rad = atan2 v.y v.x in
10295 (* Initial latitude estimate *)
···110103 let sin_lat = sin !lat_rad in
111104 let n = a /. sqrt (1. -. (e2 *. sin_lat *. sin_lat)) in
112105 let alt =
113113- if abs_float (cos !lat_rad) > 1e-10 then
114114- (p /. cos !lat_rad) -. n
115115- else
116116- (abs_float v.z /. sin_lat) -. (n *. (1. -. e2))
106106+ if abs_float (cos !lat_rad) > 1e-10 then (p /. cos !lat_rad) -. n
107107+ else (abs_float v.z /. sin_lat) -. (n *. (1. -. e2))
117108 in
118118- {
119119- lat = !lat_rad *. 180. /. Float.pi;
120120- lon = lon_rad *. 180. /. Float.pi;
121121- alt;
122122- }
109109+ { lat = !lat_rad *. 180. /. Float.pi; lon = lon_rad *. 180. /. Float.pi; alt }
123110124111(** Geodetic → ECEF.
125112···136123 {
137124 x = (n +. g.alt) *. cos_lat *. cos lon_rad;
138125 y = (n +. g.alt) *. cos_lat *. sin lon_rad;
139139- z = (n *. (1. -. e2) +. g.alt) *. sin_lat;
126126+ z = ((n *. (1. -. e2)) +. g.alt) *. sin_lat;
140127 }
141128142129let teme_to_geodetic ~gmst v = ecef_to_geodetic (teme_to_ecef ~gmst v)
···148135let pp_vec3 ppf v = Fmt.pf ppf "(%.3f, %.3f, %.3f)" v.x v.y v.z
149136150137let pp_geodetic ppf g =
151151- Fmt.pf ppf "%.4f°%s %.4f°%s %.3f km"
152152- (abs_float g.lat)
138138+ Fmt.pf ppf "%.4f°%s %.4f°%s %.3f km" (abs_float g.lat)
153139 (if g.lat >= 0. then "N" else "S")
154140 (abs_float g.lon)
155141 (if g.lon >= 0. then "E" else "W")
+19-20
lib/coordinate.mli
···11(** Astrodynamics coordinate frame transforms.
2233- Provides conversions between standard reference frames used in
44- satellite operations:
33+ Provides conversions between standard reference frames used in satellite
44+ operations:
55 - TEME (True Equator, Mean Equinox) — SGP4 output frame
66 - ECEF (Earth-Centered, Earth-Fixed) — rotates with Earth
77 - J2000 / EME2000 — inertial frame (≈ GCRF for our precision)
···20202121type geodetic = { lat : float; lon : float; alt : float }
2222(** Geodetic coordinates: latitude (deg), longitude (deg), altitude (km).
2323- Latitude: [-90, +90], north positive.
2424- Longitude: [-180, +180], east positive. *)
2323+ Latitude: [-90, +90], north positive. Longitude: [-180, +180], east
2424+ positive. *)
25252626(** {1 Constants} *)
2727···2929(** [earth_radius] is Earth's mean equatorial radius (6378.137 km, WGS-84). *)
30303131val earth_flattening : float
3232-(** [earth_flattening] is Earth's flattening factor (1/298.257223563, WGS-84). *)
3232+(** [earth_flattening] is Earth's flattening factor (1/298.257223563, WGS-84).
3333+*)
33343435val earth_mu : float
3536(** [earth_mu] is Earth's gravitational parameter (398600.4418 km^3/s^2). *)
···4344(** {1 Time} *)
44454546val gmst_of_unix : float -> float
4646-(** [gmst_of_unix t] is Greenwich Mean Sidereal Time (radians) at Unix
4747- timestamp [t]. Uses the IAU 1982 model (Vallado Eq. 3-47). *)
4747+(** [gmst_of_unix t] is Greenwich Mean Sidereal Time (radians) at Unix timestamp
4848+ [t]. Uses the IAU 1982 model (Vallado Eq. 3-47). *)
48494950val gmst : Ptime.t -> float
5051(** [gmst t] is GMST (radians) at time [t]. *)
···6162(** {1 Frame transforms} *)
62636364val teme_to_ecef : gmst:float -> vec3 -> vec3
6464-(** [teme_to_ecef ~gmst v] rotates vector [v] from TEME to ECEF frame
6565- using GMST angle. This is a simple Z-axis rotation.
6666- (Vallado Algorithm 24). *)
6565+(** [teme_to_ecef ~gmst v] rotates vector [v] from TEME to ECEF frame using GMST
6666+ angle. This is a simple Z-axis rotation. (Vallado Algorithm 24). *)
67676868val ecef_to_teme : gmst:float -> vec3 -> vec3
6969(** [ecef_to_teme ~gmst v] is the inverse of {!teme_to_ecef}. *)
70707171val j2000_to_ecef : gmst:float -> vec3 -> vec3
7272-(** [j2000_to_ecef ~gmst v] rotates from J2000/EME2000 to ECEF.
7373- At our precision level (~arcsecond), J2000≈TEME, so this
7474- uses the same GMST rotation. *)
7272+(** [j2000_to_ecef ~gmst v] rotates from J2000/EME2000 to ECEF. At our precision
7373+ level (~arcsecond), J2000≈TEME, so this uses the same GMST rotation. *)
75747675val ecef_to_j2000 : gmst:float -> vec3 -> vec3
7776(** [ecef_to_j2000 ~gmst v] is the inverse of {!j2000_to_ecef}. *)
···7978(** {1 Geodetic conversions} *)
80798180val ecef_to_geodetic : vec3 -> geodetic
8282-(** [ecef_to_geodetic v] converts ECEF position (km) to geodetic
8383- coordinates using Bowring's iterative method on the WGS-84 ellipsoid.
8484- (Vallado Algorithm 12). *)
8181+(** [ecef_to_geodetic v] converts ECEF position (km) to geodetic coordinates
8282+ using Bowring's iterative method on the WGS-84 ellipsoid. (Vallado Algorithm
8383+ 12). *)
85848685val geodetic_to_ecef : geodetic -> vec3
8787-(** [geodetic_to_ecef g] converts geodetic coordinates to ECEF (km).
8888- (Vallado Algorithm 11). *)
8686+(** [geodetic_to_ecef g] converts geodetic coordinates to ECEF (km). (Vallado
8787+ Algorithm 11). *)
89889089val teme_to_geodetic : gmst:float -> vec3 -> geodetic
9191-(** [teme_to_geodetic ~gmst v] converts TEME position to geodetic.
9292- Convenience for [ecef_to_geodetic (teme_to_ecef ~gmst v)]. *)
9090+(** [teme_to_geodetic ~gmst v] converts TEME position to geodetic. Convenience
9191+ for [ecef_to_geodetic (teme_to_ecef ~gmst v)]. *)
93929493(** {1 Utility} *)
9594
+31-36
test/test_coordinate.ml
···2121 (* WGS-84 values: ITG standard *)
2222 check_float "earth_radius" 1e-3 6378.137 Coordinate.earth_radius;
2323 check_float "earth_mu" 1e-3 398600.4418 Coordinate.earth_mu;
2424- check_float "flattening" 1e-12 (1. /. 298.257223563) Coordinate.earth_flattening;
2424+ check_float "flattening" 1e-12 (1. /. 298.257223563)
2525+ Coordinate.earth_flattening;
2526 check_float "J2" 1e-8 0.00108263 Coordinate.earth_j2
26272728(* ================================================================== *)
2829(* GMST tests *)
2930(* ================================================================== *)
30313131-(** Vallado Example 3-5: GMST at J2000.0 epoch.
3232- J2000.0 = 2000 Jan 1 12:00:00 TT ≈ 2000 Jan 1 11:58:55.816 UTC.
3333- GMST ≈ 280.46° = 4.8949612 rad. *)
3232+(** Vallado Example 3-5: GMST at J2000.0 epoch. J2000.0 = 2000 Jan 1 12:00:00 TT
3333+ ≈ 2000 Jan 1 11:58:55.816 UTC. GMST ≈ 280.46° = 4.8949612 rad. *)
3434let test_gmst_j2000 () =
3535- let j2000_unix = 946728000. in (* 2000-01-01T12:00:00 UTC approx *)
3535+ let j2000_unix = 946728000. in
3636+ (* 2000-01-01T12:00:00 UTC approx *)
3637 let gmst = Coordinate.gmst_of_unix j2000_unix in
3738 let gmst_deg = rad_to_deg gmst in
3839 (* Allow 0.5° tolerance for TT/UTC difference *)
3940 let diff = abs_float (gmst_deg -. 280.46) in
4041 Alcotest.(check bool) "GMST at J2000 ~280.46°" true (diff < 0.5)
41424242-(** GMST increases by ~360.985647° per day (sidereal day).
4343- After exactly one sidereal day (86164.0905s), GMST should
4444- advance by exactly 2π. *)
4343+(** GMST increases by ~360.985647° per day (sidereal day). After exactly one
4444+ sidereal day (86164.0905s), GMST should advance by exactly 2π. *)
4545let test_gmst_sidereal_day () =
4646- let t0 = 1735732800. in (* 2025-01-01T12:00:00 UTC *)
4646+ let t0 = 1735732800. in
4747+ (* 2025-01-01T12:00:00 UTC *)
4748 let sidereal_day = 86164.0905 in
4849 let g0 = Coordinate.gmst_of_unix t0 in
4950 let g1 = Coordinate.gmst_of_unix (t0 +. sidereal_day) in
···5556(** GMST should be monotonically increasing (mod 2π). *)
5657let test_gmst_monotone () =
5758 let t0 = 946728000. in
5858- let dt = 3600. in (* 1 hour steps *)
5959+ let dt = 3600. in
6060+ (* 1 hour steps *)
5961 let prev = ref (Coordinate.gmst_of_unix t0) in
6062 for i = 1 to 24 do
6163 let t = t0 +. (Float.of_int i *. dt) in
···6365 let delta = g -. !prev in
6466 (* Allow wrap-around at 2π *)
6567 let delta = if delta < -1. then delta +. (2. *. Float.pi) else delta in
6666- Alcotest.(check bool)
6767- (Fmt.str "monotone step %d" i)
6868- true (delta > 0.);
6868+ Alcotest.(check bool) (Fmt.str "monotone step %d" i) true (delta > 0.);
6969 prev := g
7070 done
7171···112112 check_float "y" 1e-6 2000. ecef.y;
113113 check_float "z" 1e-6 3000. ecef.z
114114115115-(** At GMST=π/2: Rz(π/2)*(1000,0,0) = (0, -1000, 0).
116116- ECEF X' = cos θ · X + sin θ · Y = 0
117117- ECEF Y' = -sin θ · X + cos θ · Y = -1000 *)
115115+(** At GMST=π/2: Rz(π/2)*(1000,0,0) = (0, -1000, 0). ECEF X' = cos θ · X + sin θ
116116+ · Y = 0 ECEF Y' = -sin θ · X + cos θ · Y = -1000 *)
118117let test_teme_ecef_90deg () =
119118 let v = Coordinate.vec3 1000. 0. 0. in
120119 let gmst = Float.pi /. 2. in
···145144(* Geodetic tests *)
146145(* ================================================================== *)
147146148148-(** Point on equator at prime meridian: lat=0, lon=0, alt=0.
149149- ECEF = (a, 0, 0) where a = earth_radius. *)
147147+(** Point on equator at prime meridian: lat=0, lon=0, alt=0. ECEF = (a, 0, 0)
148148+ where a = earth_radius. *)
150149let test_geodetic_equator_pm () =
151150 let v = Coordinate.vec3 Coordinate.earth_radius 0. 0. in
152151 let g = Coordinate.ecef_to_geodetic v in
···154153 check_float "lon" 0.01 0. g.lon;
155154 check_float "alt" 0.01 0. g.alt
156155157157-(** North pole: lat=90, lon=0 (or undefined), alt=0.
158158- ECEF = (0, 0, b) where b = a*(1-f). *)
156156+(** North pole: lat=90, lon=0 (or undefined), alt=0. ECEF = (0, 0, b) where b =
157157+ a*(1-f). *)
159158let test_geodetic_north_pole () =
160160- let b =
161161- Coordinate.earth_radius *. (1. -. Coordinate.earth_flattening)
162162- in
159159+ let b = Coordinate.earth_radius *. (1. -. Coordinate.earth_flattening) in
163160 let v = Coordinate.vec3 0. 0. b in
164161 let g = Coordinate.ecef_to_geodetic v in
165162 check_float "north pole lat" 0.01 90. g.lat;
···172169 check_float "lat" 0.01 0. g.lat;
173170 check_float "lon" 0.01 90. g.lon
174171175175-(** Known landmark: Washington Monument (Vallado Example 3-3).
176176- Geodetic: 38.8895°N, 77.0353°W, 0.054 km alt.
177177- ECEF: ~(1115.479, -4843.992, 3984.170) km. *)
172172+(** Known landmark: Washington Monument (Vallado Example 3-3). Geodetic:
173173+ 38.8895°N, 77.0353°W, 0.054 km alt. ECEF: ~(1115.479, -4843.992, 3984.170)
174174+ km. *)
178175let test_geodetic_washington () =
179176 let g = { Coordinate.lat = 38.8895; lon = -77.0353; alt = 0.054 } in
180177 let ecef = Coordinate.geodetic_to_ecef g in
···215212216213(** GEO altitude: ~35786 km above equator. *)
217214let test_geodetic_geo_altitude () =
218218- let r = 42164.0 in (* GEO radius *)
215215+ let r = 42164.0 in
216216+ (* GEO radius *)
219217 let v = Coordinate.vec3 r 0. 0. in
220218 let g = Coordinate.ecef_to_geodetic v in
221219 let expected_alt = r -. Coordinate.earth_radius in
222220 check_float "GEO alt" 5. expected_alt g.alt
223221224224-(** Vallado Example 3-3: given ECEF, compute geodetic.
225225- r_ECEF = (6524.834, 6862.875, 6448.296) km
226226- Expected: lat ≈ 34.35°, lon ≈ 46.45°, alt ≈ 5085 km. *)
222222+(** Vallado Example 3-3: given ECEF, compute geodetic. r_ECEF = (6524.834,
223223+ 6862.875, 6448.296) km Expected: lat ≈ 34.35°, lon ≈ 46.45°, alt ≈ 5085 km.
224224+*)
227225let test_vallado_3_3 () =
228226 let v = Coordinate.vec3 6524.834 6862.875 6448.296 in
229227 let g = Coordinate.ecef_to_geodetic v in
230228 (* These are approximate — the example has a different reference *)
231229 let r = Coordinate.vec3_length v in
232230 Alcotest.(check bool) "high orbit" true (r > 10000.);
233233- Alcotest.(check bool) "lat reasonable" true
234234- (g.lat > 20. && g.lat < 50.);
231231+ Alcotest.(check bool) "lat reasonable" true (g.lat > 20. && g.lat < 50.);
235232 Alcotest.(check bool) "alt positive" true (g.alt > 3000.)
236233237234(* ================================================================== *)
···262259let test_normalize_angle () =
263260 let twopi = 2. *. Float.pi in
264261 check_float "positive" 1e-10 1.5 (Coordinate.normalize_angle 1.5);
265265- check_float "negative" 1e-10 (twopi -. 1.)
266266- (Coordinate.normalize_angle (-1.));
267267- check_float "large" 1e-10 1.0
268268- (Coordinate.normalize_angle (1. +. twopi));
262262+ check_float "negative" 1e-10 (twopi -. 1.) (Coordinate.normalize_angle (-1.));
263263+ check_float "large" 1e-10 1.0 (Coordinate.normalize_angle (1. +. twopi));
269264 check_float "zero" 1e-10 0. (Coordinate.normalize_angle 0.)
270265271266(* ================================================================== *)