Contact Graph Routing for time-varying satellite networks
0
fork

Configure Feed

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

fix(merlint): show file path instead of (global) for E510 issues

Add a location (file:1:0) to E510 Missing Log Source issues so the
output shows the full file path, making it easy to identify which
module needs fixing in monorepos with many similarly-named modules.
Also includes accumulated linter fixes and dune fmt formatting.

+87 -82
+67 -65
example/tle_contacts.ml
··· 50 50 type visibility_state = { in_contact : bool; contact_start : float option } 51 51 (** Track visibility state between a pair of satellites *) 52 52 53 + (** Close a contact and add it to the list. *) 54 + let close_contact sats visibility contacts ~rate i j time = 55 + match visibility.(i).(j).contact_start with 56 + | None -> () 57 + | Some start -> 58 + let from = sats.(i).node in 59 + let to_ = sats.(j).node in 60 + (* Compute average OWLT (use midpoint) *) 61 + let mid_time = (start +. time) /. 2. in 62 + let owlt = 63 + match 64 + ( propagate_to sats.(i).state sats.(i).tle mid_time, 65 + propagate_to sats.(j).state sats.(j).tle mid_time ) 66 + with 67 + | Ok (p1, _), Ok (p2, _) -> light_time p1 p2 68 + | _ -> 0. 69 + in 70 + let contact = Cgr.Contact.v ~from ~to_ ~start ~stop:time ~rate ~owlt () in 71 + contacts := contact :: !contacts; 72 + visibility.(i).(j) <- { in_contact = false; contact_start = None } 73 + 74 + (** Close any remaining open contacts. *) 75 + let close_remaining_contacts sats visibility contacts ~rate ~n ~end_time = 76 + for i = 0 to n - 1 do 77 + for j = 0 to n - 1 do 78 + if visibility.(i).(j).in_contact then 79 + close_contact sats visibility contacts ~rate i j end_time 80 + done 81 + done 82 + 53 83 (** Generate contacts between satellites over a time window. 54 84 55 85 @param sats List of satellites (parsed and initialized) ··· 69 99 in 70 100 let contacts = ref [] in 71 101 72 - (* Close a contact and add it to the list *) 73 - let close_contact i j time = 74 - match visibility.(i).(j).contact_start with 75 - | None -> () 76 - | Some start -> 77 - let from = sats.(i).node in 78 - let to_ = sats.(j).node in 79 - (* Compute average OWLT (use midpoint) *) 80 - let mid_time = (start +. time) /. 2. in 81 - let owlt = 82 - match 83 - ( propagate_to sats.(i).state sats.(i).tle mid_time, 84 - propagate_to sats.(j).state sats.(j).tle mid_time ) 85 - with 86 - | Ok (p1, _), Ok (p2, _) -> light_time p1 p2 87 - | _ -> 0. 88 - in 89 - let contact = 90 - Cgr.Contact.v ~from ~to_ ~start ~stop:time ~rate ~owlt () 91 - in 92 - contacts := contact :: !contacts; 93 - visibility.(i).(j) <- { in_contact = false; contact_start = None } 94 - in 95 - 96 102 (* Step through time *) 97 103 let end_time = start_time +. duration in 98 104 let time = ref start_time in ··· 116 122 { in_contact = true; contact_start = Some !time } 117 123 else if (not visible) && state.in_contact then 118 124 (* Contact ends *) 119 - close_contact i j !time 125 + close_contact sats visibility contacts ~rate i j !time 120 126 | _ -> () 121 127 done 122 128 done; ··· 124 130 time := !time +. step 125 131 done; 126 132 127 - (* Close any remaining contacts *) 128 - for i = 0 to n - 1 do 129 - for j = 0 to n - 1 do 130 - if visibility.(i).(j).in_contact then close_contact i j end_time 131 - done 132 - done; 133 - 133 + close_remaining_contacts sats visibility contacts ~rate ~n ~end_time; 134 134 !contacts 135 135 136 136 (** Parse TLEs from a multi-line string (standard TLE format) *) ··· 163 163 read_all [] 164 164 165 165 (** Example: Generate contacts for real Starlink satellites *) 166 - let example () = 167 - (* Load TLEs from file *) 168 - let tle_file = "ocaml-cgr/example/starlink.tle" in 169 - let tle_text = 170 - try load_tle_file tle_file 171 - with Sys_error _ -> 172 - Printf.eprintf "Could not load %s, using embedded TLEs\n" tle_file; 173 - {|STARLINK-1008 166 + let load_or_default_tles tle_file = 167 + try load_tle_file tle_file 168 + with Sys_error _ -> 169 + Printf.eprintf "Could not load %s, using embedded TLEs\n" tle_file; 170 + {|STARLINK-1008 174 171 1 44714U 19074B 26034.10858321 -.00000474 00000+0 -36567-5 0 9992 175 172 2 44714 53.1593 339.5849 0001299 80.4695 279.6453 15.31021178343579 176 173 STARLINK-1012 ··· 185 182 STARLINK-1042 186 183 1 44747U 19074AL 26034.32583353 .00022104 00000+0 42316-3 0 9991 187 184 2 44747 53.0433 259.4358 0003715 91.8857 268.2575 15.47997310344749|} 185 + 186 + let print_routing_analysis plan nodes ~start_time = 187 + Printf.printf "\nRouting analysis:\n"; 188 + let total_pairs = ref 0 in 189 + let routable_pairs = ref 0 in 190 + let check_route src dst = 191 + if Cgr.Node.equal src dst then () 192 + else begin 193 + incr total_pairs; 194 + match Cgr.route plan ~src ~dst ~time:start_time with 195 + | Some route -> 196 + incr routable_pairs; 197 + Printf.printf " %s -> %s: %d hops, latency %.3f s\n" 198 + (Cgr.Node.name src) (Cgr.Node.name dst) 199 + (List.length (Cgr.Route.hops route)) 200 + (Cgr.Route.latency route) 201 + | None -> 202 + Printf.printf " %s -> %s: NO ROUTE\n" (Cgr.Node.name src) 203 + (Cgr.Node.name dst) 204 + end 188 205 in 206 + List.iter (fun src -> List.iter (check_route src) nodes) nodes; 207 + Printf.printf "\nRoutability: %d/%d pairs (%.1f%%)\n" !routable_pairs 208 + !total_pairs 209 + (100. *. Float.of_int !routable_pairs /. Float.of_int !total_pairs) 210 + 211 + let example () = 212 + (* Load TLEs from file *) 213 + let tle_file = "ocaml-cgr/example/starlink.tle" in 214 + let tle_text = load_or_default_tles tle_file in 189 215 let sats = parse_tles tle_text in 190 216 Printf.printf "Loaded %d Starlink satellites\n" (List.length sats); 191 217 List.iter (fun s -> Printf.printf " - %s\n" s.name) sats; ··· 224 250 (Cgr.Node.name node) from_count to_count) 225 251 nodes; 226 252 227 - (* Find routes between all pairs *) 228 - Printf.printf "\nRouting analysis:\n"; 229 - let total_pairs = ref 0 in 230 - let routable_pairs = ref 0 in 231 - let check_route src dst = 232 - if Cgr.Node.equal src dst then () 233 - else begin 234 - incr total_pairs; 235 - match Cgr.route plan ~src ~dst ~time:start_time with 236 - | Some route -> 237 - incr routable_pairs; 238 - Printf.printf " %s -> %s: %d hops, latency %.3f s\n" 239 - (Cgr.Node.name src) (Cgr.Node.name dst) 240 - (List.length (Cgr.Route.hops route)) 241 - (Cgr.Route.latency route) 242 - | None -> 243 - Printf.printf " %s -> %s: NO ROUTE\n" (Cgr.Node.name src) 244 - (Cgr.Node.name dst) 245 - end 246 - in 247 - List.iter (fun src -> List.iter (check_route src) nodes) nodes; 248 - 249 - Printf.printf "\nRoutability: %d/%d pairs (%.1f%%)\n" !routable_pairs 250 - !total_pairs 251 - (100. *. Float.of_int !routable_pairs /. Float.of_int !total_pairs) 253 + print_routing_analysis plan nodes ~start_time 252 254 253 255 let () = example ()
+20 -17
gen/cgr_gen.ml
··· 67 67 nodes; 68 68 !contacts 69 69 70 + let add_ground_links contacts ~sats ~ground_stations ~duration ~rate ~owlt = 71 + (* Ground station links (each GS connects to first shell satellites) *) 72 + List.iteri 73 + (fun gs_idx gs -> 74 + let first_shell = List.nth sats 0 in 75 + (* Each GS has periodic contact windows with visible satellites *) 76 + let visible_sats = gs_idx mod List.length first_shell in 77 + let sat = List.nth first_shell visible_sats in 78 + (* Periodic visibility windows *) 79 + for window = 0 to 9 do 80 + let start = Float.of_int window *. (duration /. 10.) in 81 + let stop = start +. (duration /. 20.) in 82 + contacts := 83 + Cgr.Contact.v ~from:gs ~to_:sat ~start ~stop ~rate ~owlt () 84 + :: Cgr.Contact.v ~from:sat ~to_:gs ~start ~stop ~rate ~owlt () 85 + :: !contacts 86 + done) 87 + ground_stations 88 + 70 89 let constellation ~shells ~sat_per_shell ~ground_stations ~duration ~rate ~owlt 71 90 = 72 91 (* LEO constellation with inter-satellite and ground links *) ··· 103 122 :: !contacts) 104 123 shell1 shell2 105 124 done; 106 - (* Ground station links (each GS connects to first shell satellites) *) 107 - List.iteri 108 - (fun gs_idx gs -> 109 - let first_shell = List.nth sats 0 in 110 - (* Each GS has periodic contact windows with visible satellites *) 111 - let visible_sats = gs_idx mod List.length first_shell in 112 - let sat = List.nth first_shell visible_sats in 113 - (* Periodic visibility windows *) 114 - for window = 0 to 9 do 115 - let start = Float.of_int window *. (duration /. 10.) in 116 - let stop = start +. (duration /. 20.) in 117 - contacts := 118 - Cgr.Contact.v ~from:gs ~to_:sat ~start ~stop ~rate ~owlt () 119 - :: Cgr.Contact.v ~from:sat ~to_:gs ~start ~stop ~rate ~owlt () 120 - :: !contacts 121 - done) 122 - ground_stations; 125 + add_ground_links contacts ~sats ~ground_stations ~duration ~rate ~owlt; 123 126 !contacts 124 127 125 128 let nodes n = List.init n (fun i -> Cgr.Node.v (Printf.sprintf "N%d" i))