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.
···11(** Collision probability computation for conjunction assessment.
2233- Implements the Foster 2D method, Chan series expansion, and Alfano
44- maximum Pc bound.
33+ Implements the Foster 2D method, Chan series expansion, and Alfano maximum
44+ Pc bound.
5566 {b Reference}: Foster (1992), Chan (2008), Alfano (2005). *)
77···1010(* {1 Types} *)
11111212type vec3 = Cdm.vec3 = { x : float; y : float; z : float }
1313-1413type sym2 = { a11 : float; a12 : float; a22 : float }
15141615type encounter = {
···8180 let r, t, n = rtn_basis state in
8281 (* M is the 3x3 rotation matrix [R T N] as columns.
8382 m[i][j] = i-th component of j-th basis vector. *)
8484- let m =
8585- [|
8686- [| r.x; t.x; n.x |];
8787- [| r.y; t.y; n.y |];
8888- [| r.z; t.z; n.z |];
8989- |]
9090- in
8383+ let m = [| [| r.x; t.x; n.x |]; [| r.y; t.y; n.y |]; [| r.z; t.z; n.z |] |] in
9184 let c =
9285 [|
9386 [| cov.cr_r; cov.ct_r; cov.cn_r |];
···10598 done;
10699 !s
107100 in
108108- { s11 = r_ij 0 0; s12 = r_ij 0 1; s13 = r_ij 0 2;
109109- s22 = r_ij 1 1; s23 = r_ij 1 2; s33 = r_ij 2 2 }
101101+ {
102102+ s11 = r_ij 0 0;
103103+ s12 = r_ij 0 1;
104104+ s13 = r_ij 0 2;
105105+ s22 = r_ij 1 1;
106106+ s23 = r_ij 1 2;
107107+ s33 = r_ij 2 2;
108108+ }
110109111110(* Add two symmetric 3x3 matrices. *)
112111let sym3_add a b =
113113- { s11 = a.s11 +. b.s11; s12 = a.s12 +. b.s12; s13 = a.s13 +. b.s13;
114114- s22 = a.s22 +. b.s22; s23 = a.s23 +. b.s23; s33 = a.s33 +. b.s33 }
112112+ {
113113+ s11 = a.s11 +. b.s11;
114114+ s12 = a.s12 +. b.s12;
115115+ s13 = a.s13 +. b.s13;
116116+ s22 = a.s22 +. b.s22;
117117+ s23 = a.s23 +. b.s23;
118118+ s33 = a.s33 +. b.s33;
119119+ }
115120116121(* {1 Conjunction Plane Projection}
117122···123128 z_hat = relative velocity direction (normal to plane).
124129 x_hat is chosen in the miss direction projected onto the plane. *)
125130let conjunction_plane_basis ~z_hat ~miss =
126126- let miss_proj =
127127- vec3_sub miss (vec3_scale (vec3_dot miss z_hat) z_hat)
128128- in
131131+ let miss_proj = vec3_sub miss (vec3_scale (vec3_dot miss z_hat) z_hat) in
129132 let miss_proj_norm = vec3_norm miss_proj in
130133 let x_hat =
131134 if miss_proj_norm > 1e-12 then vec3_scale (1.0 /. miss_proj_norm) miss_proj
···144147let project_covariance (c : sym3) x_hat y_hat =
145148 let proj ei ej =
146149 let cx =
147147- { x = c.s11 *. ej.x +. c.s12 *. ej.y +. c.s13 *. ej.z;
148148- y = c.s12 *. ej.x +. c.s22 *. ej.y +. c.s23 *. ej.z;
149149- z = c.s13 *. ej.x +. c.s23 *. ej.y +. c.s33 *. ej.z }
150150+ {
151151+ x = (c.s11 *. ej.x) +. (c.s12 *. ej.y) +. (c.s13 *. ej.z);
152152+ y = (c.s12 *. ej.x) +. (c.s22 *. ej.y) +. (c.s23 *. ej.z);
153153+ z = (c.s13 *. ej.x) +. (c.s23 *. ej.y) +. (c.s33 *. ej.z);
154154+ }
150155 in
151156 vec3_dot ei cx
152157 in
···172177 else (1.0, 0.0)
173178 in
174179 let miss_x = (ev_x *. mx) +. (ev_y *. my) in
175175- let miss_y = (-. ev_y *. mx) +. (ev_x *. my) in
180180+ let miss_y = (-.ev_y *. mx) +. (ev_x *. my) in
176181 (sigma_x, sigma_y, miss_x, miss_y)
177182178183let encounter_of_cdm ~hbr (cdm : Cdm.t) : encounter =
···203208 let m = (n + 1) / 2 in
204209 for i = 0 to m - 1 do
205210 (* Initial guess *)
206206- let mu = float_of_int (4 * i + 3) /. float_of_int (4 * n + 2) in
211211+ let mu = float_of_int ((4 * i) + 3) /. float_of_int ((4 * n) + 2) in
207212 let x = ref (cos (pi *. mu)) in
208213 (* Newton iterations *)
209214 for _ = 1 to 20 do
···211216 let p1 = ref !x in
212217 for j = 2 to n do
213218 let fj = float_of_int j in
214214- let p2 = ((((2.0 *. fj) -. 1.0) *. !x *. !p1) -. ((fj -. 1.0) *. !p0)) /. fj in
219219+ let p2 =
220220+ ((((2.0 *. fj) -. 1.0) *. !x *. !p1) -. ((fj -. 1.0) *. !p0)) /. fj
221221+ in
215222 p0 := !p1;
216223 p1 := p2
217224 done;
···226233 let p1 = ref !x in
227234 for j = 2 to n do
228235 let fj = float_of_int j in
229229- let p2 = ((((2.0 *. fj) -. 1.0) *. !x *. !p1) -. ((fj -. 1.0) *. !p0)) /. fj in
236236+ let p2 =
237237+ ((((2.0 *. fj) -. 1.0) *. !x *. !p1) -. ((fj -. 1.0) *. !p0)) /. fj
238238+ in
230239 p0 := !p1;
231240 p1 := p2
232241 done;
···269278 for i = 0 to n - 1 do
270279 (* Map [-1,1] → [0, 2π] *)
271280 let theta = pi *. (nodes_t.(i) +. 1.0) in
272272- let wt = weights_t.(i) *. pi in (* Jacobian: 2π/2 = π *)
281281+ let wt = weights_t.(i) *. pi in
282282+ (* Jacobian: 2π/2 = π *)
273283 let cos_t = cos theta in
274284 let sin_t = sin theta in
275285 for j = 0 to n - 1 do
276286 (* Map [-1,1] → [0, hbr] *)
277287 let r = hbr *. (nodes_r.(j) +. 1.0) /. 2.0 in
278278- let wr = weights_r.(j) *. hbr /. 2.0 in (* Jacobian: hbr/2 *)
288288+ let wr = weights_r.(j) *. hbr /. 2.0 in
289289+ (* Jacobian: hbr/2 *)
279290 let dx = (r *. cos_t) -. mx in
280291 let dy = (r *. sin_t) -. my in
281281- let exponent = -0.5 *. (((dx *. dx) /. sx2) +. ((dy *. dy) /. sy2)) in
292292+ let exponent = -0.5 *. ((dx *. dx /. sx2) +. (dy *. dy /. sy2)) in
282293 let f = r *. norm *. exp exponent in
283294 sum := !sum +. (wt *. wr *. f)
284295 done
···362373 let wr = weights_r.(j) *. hbr /. 2.0 in
363374 let dx = (r *. cos_t) -. mx in
364375 let dy = (r *. sin_t) -. my in
365365- let exponent = -0.5 *. (((dx *. dx) /. sx2) +. ((dy *. dy) /. sy2)) in
376376+ let exponent = -0.5 *. ((dx *. dx /. sx2) +. (dy *. dy /. sy2)) in
366377 let f = r *. norm *. exp exponent in
367378 sum := !sum +. (wt *. wr *. f)
368379 done
···403414(* {1 Pretty-printing} *)
404415405416let pp_sym2 ppf s =
406406- Format.fprintf ppf "| %.6e %.6e |@,| %.6e %.6e |" s.a11 s.a12 s.a12
407407- s.a22
417417+ Format.fprintf ppf "| %.6e %.6e |@,| %.6e %.6e |" s.a11 s.a12 s.a12 s.a22
408418409419let pp_encounter ppf e =
410420 Format.fprintf ppf
+21-22
lib/collision.mli
···11(** Collision probability computation for conjunction assessment.
2233- Computes the probability of collision (Pc) between two space objects
44- given their state vectors, position covariances, and hard-body radii.
33+ Computes the probability of collision (Pc) between two space objects given
44+ their state vectors, position covariances, and hard-body radii.
5566- Implements the standard 2D Foster method (NASA CARA), the Alfano maximum
77- Pc bound, and the Chan series expansion.
66+ Implements the standard 2D Foster method (NASA CARA), the Alfano maximum Pc
77+ bound, and the Chan series expansion.
8899 {b References}:
1010 - Foster, J.L., Estes, H.S., "A Parametric Analysis of Orbital Debris
···17171818type vec3 = Cdm.vec3 = { x : float; y : float; z : float }
19192020-(** 2x2 symmetric matrix (conjunction plane covariance). *)
2120type sym2 = {
2221 a11 : float; (** (1,1) element. *)
2322 a12 : float; (** (1,2) = (2,1) element. *)
2423 a22 : float; (** (2,2) element. *)
2524}
2525+(** 2x2 symmetric matrix (conjunction plane covariance). *)
26262727-(** Conjunction geometry projected onto the conjunction plane. *)
2827type encounter = {
2928 miss_x : float; (** Miss distance x-component in conjunction plane (km). *)
3029 miss_y : float; (** Miss distance y-component in conjunction plane (km). *)
···3231 sigma_y : float; (** 1-sigma uncertainty along y-axis (km). *)
3332 hbr : float; (** Combined hard-body radius (km). *)
3433}
3434+(** Conjunction geometry projected onto the conjunction plane. *)
35353636(** {1 Conjunction Plane Projection} *)
37373838val encounter_of_cdm : hbr:float -> Cdm.t -> encounter
3939-(** [encounter_of_cdm ~hbr cdm] projects a CDM's conjunction geometry
4040- onto the conjunction plane (perpendicular to relative velocity at TCA).
3939+(** [encounter_of_cdm ~hbr cdm] projects a CDM's conjunction geometry onto the
4040+ conjunction plane (perpendicular to relative velocity at TCA).
41414242- The combined covariance C = C1 + C2 is projected onto the conjunction
4343- plane and diagonalized. [hbr] is the combined hard-body radius in km
4444- (typically 0.005-0.020 km for LEO satellites). *)
4242+ The combined covariance C = C1 + C2 is projected onto the conjunction plane
4343+ and diagonalized. [hbr] is the combined hard-body radius in km (typically
4444+ 0.005-0.020 km for LEO satellites). *)
45454646(** {1 Probability of Collision} *)
47474848val pc_foster : ?n:int -> encounter -> float
4949-(** [pc_foster ?n enc] computes the 2D probability of collision using the
5050- Foster method (numerical integration via Gauss-Legendre quadrature).
4949+(** [pc_foster ?n enc] computes the 2D probability of collision using the Foster
5050+ method (numerical integration via Gauss-Legendre quadrature).
51515252- [n] is the number of quadrature points per dimension (default 64).
5353- This is the standard method used by NASA CARA and 18th SDS. *)
5252+ [n] is the number of quadrature points per dimension (default 64). This is
5353+ the standard method used by NASA CARA and 18th SDS. *)
54545555val pc_chan : ?terms:int -> encounter -> float
5656-(** [pc_chan ?terms enc] computes Pc using the Chan series expansion.
5757- [terms] is the number of series terms (default 50). *)
5656+(** [pc_chan ?terms enc] computes Pc using the Chan series expansion. [terms] is
5757+ the number of series terms (default 50). *)
58585959val pc_max : encounter -> float
6060-(** [pc_max enc] returns an upper bound on Pc:
6161- [Pc_max = hbr² / (2·σx·σy)].
6060+(** [pc_max enc] returns an upper bound on Pc: [Pc_max = hbr² / (2·σx·σy)].
62616363- This bounds the integral by the peak density times the disk area.
6464- If Pc_max is below threshold, the conjunction can be dismissed
6565- without computing the exact Pc. *)
6262+ This bounds the integral by the peak density times the disk area. If Pc_max
6363+ is below threshold, the conjunction can be dismissed without computing the
6464+ exact Pc. *)
66656766val pc : Cdm.t -> hbr:float -> float
6867(** [pc cdm ~hbr] is a convenience function that projects [cdm] onto the
+112-106
test/test_collision.ml
···9797 skip_if_no_data ();
9898 match data_dir with
9999 | None -> ()
100100- | Some _ ->
100100+ | Some _ -> (
101101 let path = data_path filename in
102102 let ic = open_in path in
103103 let result = f ic in
104104 close_in ic;
105105- (match result with
106106- | Ok () -> ()
107107- | Error e -> Alcotest.failf "parse error: %a" Cdm.pp_error e)
105105+ match result with
106106+ | Ok () -> ()
107107+ | Error e -> Alcotest.failf "parse error: %a" Cdm.pp_error e)
108108109109let with_spherical_csv f =
110110 with_csv "IVV_Releasable_Dataset_Spherical_DefaultHBR.csv" f
111111112112-let with_sfsh_csv f =
113113- with_csv "IVV_Releasable_Dataset_SFSH_DiscreteHBR.csv" f
112112+let with_sfsh_csv f = with_csv "IVV_Releasable_Dataset_SFSH_DiscreteHBR.csv" f
114113115114(* CSieve floors Pc at exactly 1e-10. *)
116115let is_clamped pc = Float.abs (pc -. 1e-10) < 1e-15
···124123 sqrt ((enc.miss_x *. enc.miss_x) +. (enc.miss_y *. enc.miss_y))
125124 in
126125 let cdm_miss = cdm.Cdm.min_range in
127127- cdm_miss > 0.01 && (our_miss /. cdm_miss > 5.0 || our_miss /. cdm_miss < 0.01)
126126+ cdm_miss > 0.01
127127+ && (our_miss /. cdm_miss > 5.0 || our_miss /. cdm_miss < 0.01)
128128129129(* Compute RTN local coordinates from J2000 state and compare to dataset. *)
130130let check_rtn_basis cdm =
···185185 if Sys.file_exists path then Some (load_hbr_table path) else None
186186187187let lookup_combined_hbr tbl (cdm : Cdm.t) =
188188- match Hashtbl.find_opt tbl cdm.obj1.id, Hashtbl.find_opt tbl cdm.obj2.id with
188188+ match
189189+ (Hashtbl.find_opt tbl cdm.obj1.id, Hashtbl.find_opt tbl cdm.obj2.id)
190190+ with
189191 | Some h1, Some h2 -> Some (h1 +. h2)
190192 | _ -> None
191193···201203}
202204203205let fresh_stats () =
204204- { checked = 0; within_1_order = 0; within_half_order = 0;
205205- exact_match = 0; total = 0; sum_log_err = 0.0 }
206206+ {
207207+ checked = 0;
208208+ within_1_order = 0;
209209+ within_half_order = 0;
210210+ exact_match = 0;
211211+ total = 0;
212212+ sum_log_err = 0.0;
213213+ }
206214207215let print_stats label stats =
208216 let n = max 1 stats.checked in
209217 let mean = stats.sum_log_err /. float_of_int n in
210218 Format.printf
211211- "@.%s (%d sampled of %d):@.\
212212- \ Within 1 order: %d/%d (%.1f%%)@.\
213213- \ Within 0.1 orders: %d/%d (%.1f%%)@.\
214214- \ Mean |log10 ratio|: %.3f@."
215215- label stats.checked stats.total
216216- stats.within_1_order n
219219+ "@.%s (%d sampled of %d):@. Within 1 order: %d/%d (%.1f%%)@. Within \
220220+ 0.1 orders: %d/%d (%.1f%%)@. Mean |log10 ratio|: %.3f@."
221221+ label stats.checked stats.total stats.within_1_order n
217222 (100.0 *. float_of_int stats.within_1_order /. float_of_int n)
218223 stats.exact_match n
219224 (100.0 *. float_of_int stats.exact_match /. float_of_int n)
···244249245250let test_tracss_pc_validation () =
246251 with_spherical_csv (fun ic ->
247247- let stats = fresh_stats () in
248248- let result =
249249- Cdm.fold_csv_channel ic
250250- (fun () cdm ->
251251- stats.total <- stats.total + 1;
252252- if stats.total mod 500 = 0 then compare_pc stats cdm)
253253- ()
254254- in
255255- match result with
256256- | Error e -> Error e
257257- | Ok () ->
258258- print_stats "TraCSS spherical Pc validation" stats;
259259- check_stats_pass stats;
260260- Ok ())
252252+ let stats = fresh_stats () in
253253+ let result =
254254+ Cdm.fold_csv_channel ic
255255+ (fun () cdm ->
256256+ stats.total <- stats.total + 1;
257257+ if stats.total mod 500 = 0 then compare_pc stats cdm)
258258+ ()
259259+ in
260260+ match result with
261261+ | Error e -> Error e
262262+ | Ok () ->
263263+ print_stats "TraCSS spherical Pc validation" stats;
264264+ check_stats_pass stats;
265265+ Ok ())
261266262267let test_encounter_projection () =
263268 with_spherical_csv (fun ic ->
264264- let bad = ref 0 in
265265- let n = ref 0 in
266266- let result =
267267- Cdm.fold_csv_channel ic
268268- (fun () cdm ->
269269- incr n;
270270- if !n <= 100 && check_encounter_sanity cdm then incr bad)
271271- ()
272272- in
273273- match result with
274274- | Error e -> Error e
275275- | Ok () ->
276276- Alcotest.(check int) "no bad projections" 0 !bad;
277277- Ok ())
269269+ let bad = ref 0 in
270270+ let n = ref 0 in
271271+ let result =
272272+ Cdm.fold_csv_channel ic
273273+ (fun () cdm ->
274274+ incr n;
275275+ if !n <= 100 && check_encounter_sanity cdm then incr bad)
276276+ ()
277277+ in
278278+ match result with
279279+ | Error e -> Error e
280280+ | Ok () ->
281281+ Alcotest.(check int) "no bad projections" 0 !bad;
282282+ Ok ())
278283279284let test_rtn_basis_verification () =
280285 with_spherical_csv (fun ic ->
281281- let n = ref 0 in
282282- let max_err = ref 0.0 in
283283- let checked = ref 0 in
284284- let result =
285285- Cdm.fold_csv_channel ic
286286- (fun () cdm ->
287287- incr n;
288288- if !n <= 200 then begin
289289- let err = check_rtn_basis cdm in
290290- max_err := Float.max !max_err err;
291291- incr checked
292292- end)
293293- ()
294294- in
295295- match result with
296296- | Error e -> Error e
297297- | Ok () ->
298298- Format.printf "@.RTN basis: %d rows, max err=%.6f km@." !checked
299299- !max_err;
300300- if !max_err > 1.0 then
301301- Alcotest.failf "RTN basis max error %.3f km > 1 km" !max_err;
302302- Ok ())
286286+ let n = ref 0 in
287287+ let max_err = ref 0.0 in
288288+ let checked = ref 0 in
289289+ let result =
290290+ Cdm.fold_csv_channel ic
291291+ (fun () cdm ->
292292+ incr n;
293293+ if !n <= 200 then begin
294294+ let err = check_rtn_basis cdm in
295295+ max_err := Float.max !max_err err;
296296+ incr checked
297297+ end)
298298+ ()
299299+ in
300300+ match result with
301301+ | Error e -> Error e
302302+ | Ok () ->
303303+ Format.printf "@.RTN basis: %d rows, max err=%.6f km@." !checked
304304+ !max_err;
305305+ if !max_err > 1.0 then
306306+ Alcotest.failf "RTN basis max error %.3f km > 1 km" !max_err;
307307+ Ok ())
303308304309let test_spot_check_first_row () =
305310 with_spherical_csv (fun ic ->
306306- let first = ref None in
307307- let result =
308308- Cdm.fold_csv_channel ic
309309- (fun () cdm -> if !first = None then first := Some cdm)
310310- ()
311311- in
312312- match result with
313313- | Error e -> Error e
314314- | Ok () ->
315315- (match !first with
316316- | None -> Alcotest.fail "no rows"
317317- | Some cdm ->
318318- let dataset_pc = Cdm.pc cdm in
319319- let enc = Collision.encounter_of_cdm ~hbr:default_hbr cdm in
320320- let our_pc = Collision.pc_foster enc in
321321- Format.printf
322322- "@.Spot-check (obj %d vs %d): dataset=%.3e ours=%.3e@."
323323- cdm.obj1.id cdm.obj2.id dataset_pc our_pc);
324324- Ok ())
311311+ let first = ref None in
312312+ let result =
313313+ Cdm.fold_csv_channel ic
314314+ (fun () cdm -> if !first = None then first := Some cdm)
315315+ ()
316316+ in
317317+ match result with
318318+ | Error e -> Error e
319319+ | Ok () ->
320320+ (match !first with
321321+ | None -> Alcotest.fail "no rows"
322322+ | Some cdm ->
323323+ let dataset_pc = Cdm.pc cdm in
324324+ let enc = Collision.encounter_of_cdm ~hbr:default_hbr cdm in
325325+ let our_pc = Collision.pc_foster enc in
326326+ Format.printf
327327+ "@.Spot-check (obj %d vs %d): dataset=%.3e ours=%.3e@."
328328+ cdm.obj1.id cdm.obj2.id dataset_pc our_pc);
329329+ Ok ())
325330326331(* Validate Pc against SFSH dataset using per-object HBR from
327332 screening volumes file. *)
328333let test_tracss_sfsh_pc_validation () =
329334 match hbr_table with
330330- | None ->
331331- Printf.printf "SKIP: screening volumes file not found\n"
335335+ | None -> Printf.printf "SKIP: screening volumes file not found\n"
332336 | Some tbl ->
333337 with_sfsh_csv (fun ic ->
334334- let stats = fresh_stats () in
335335- let result =
336336- Cdm.fold_csv_channel ic
337337- (fun () cdm ->
338338- stats.total <- stats.total + 1;
339339- if stats.total mod 200 = 0 then
340340- match lookup_combined_hbr tbl cdm with
341341- | Some hbr -> compare_pc_with_hbr stats hbr cdm
342342- | None -> ())
343343- ()
344344- in
345345- match result with
346346- | Error e -> Error e
347347- | Ok () ->
348348- print_stats "TraCSS SFSH Pc validation" stats;
349349- check_stats_pass stats;
350350- Ok ())
338338+ let stats = fresh_stats () in
339339+ let result =
340340+ Cdm.fold_csv_channel ic
341341+ (fun () cdm ->
342342+ stats.total <- stats.total + 1;
343343+ if stats.total mod 200 = 0 then
344344+ match lookup_combined_hbr tbl cdm with
345345+ | Some hbr -> compare_pc_with_hbr stats hbr cdm
346346+ | None -> ())
347347+ ()
348348+ in
349349+ match result with
350350+ | Error e -> Error e
351351+ | Ok () ->
352352+ print_stats "TraCSS SFSH Pc validation" stats;
353353+ check_stats_pass stats;
354354+ Ok ())
351355352356let suite =
353357 ( "collision",
···360364 Alcotest.test_case "RTN basis" `Slow test_rtn_basis_verification;
361365 Alcotest.test_case "encounter projection" `Slow test_encounter_projection;
362366 Alcotest.test_case "spot-check" `Slow test_spot_check_first_row;
363363- Alcotest.test_case "Pc validation (spherical)" `Slow test_tracss_pc_validation;
364364- Alcotest.test_case "Pc validation (SFSH)" `Slow test_tracss_sfsh_pc_validation;
367367+ Alcotest.test_case "Pc validation (spherical)" `Slow
368368+ test_tracss_pc_validation;
369369+ Alcotest.test_case "Pc validation (SFSH)" `Slow
370370+ test_tracss_sfsh_pc_validation;
365371 ] )