Contact Graph Routing for time-varying satellite networks
0
fork

Configure Feed

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

cgr: Add NASA HDTN test vectors

Import test scenarios from NASA's HDTN (High-rate Delay Tolerant Network):
- cgrTutorial: 5-node network from pyCGR tutorial (16 contacts)
- contactPlan_RoutingTest: 6-node routing scenario (8 contacts)

Test cases verify:
- Multi-hop routing with OWLT (1->3->4->5 path)
- Contact timing constraints (waiting for contact windows)
- Unidirectional links (no reverse path)
- Path selection (choosing faster route via node 2 vs 3)

Reference: https://github.com/nasa/HDTN

+168
+168
test/test_cgr.ml
··· 213 213 "capacity is bottleneck" true 214 214 (float_eq 25000. (Route.capacity route)) 215 215 216 + (* ========================================================================== 217 + Test vectors from NASA HDTN (High-rate Delay Tolerant Network) 218 + https://github.com/nasa/HDTN 219 + ========================================================================== *) 220 + 221 + (* pyCGR Tutorial scenario from HDTN cgrTutorial.json 222 + 5 nodes, 16 contacts (bidirectional pairs), 1 Mbps, 1 second OWLT 223 + 224 + Network topology: 225 + 1 -- 2 -- 3 -- 4 -- 5 226 + | | 227 + +---------+ (intermittent: 1-5 from t=10-20, 4-5 at t=0-10,30-40,50-60) 228 + *) 229 + let hdtn_cgr_tutorial_contacts = 230 + let n1 = Node.v "1" 231 + and n2 = Node.v "2" 232 + and n3 = Node.v "3" 233 + and n4 = Node.v "4" 234 + and n5 = Node.v "5" in 235 + let owlt = 1. in 236 + let rate = 1_000_000. in 237 + [ 238 + (* 1 <-> 2: t=0-60 *) 239 + Contact.v ~from:n1 ~to_:n2 ~start:0. ~stop:60. ~rate ~owlt (); 240 + Contact.v ~from:n2 ~to_:n1 ~start:0. ~stop:60. ~rate ~owlt (); 241 + (* 2 <-> 3: t=0-60 *) 242 + Contact.v ~from:n2 ~to_:n3 ~start:0. ~stop:60. ~rate ~owlt (); 243 + Contact.v ~from:n3 ~to_:n2 ~start:0. ~stop:60. ~rate ~owlt (); 244 + (* 1 <-> 3: t=0-60 *) 245 + Contact.v ~from:n1 ~to_:n3 ~start:0. ~stop:60. ~rate ~owlt (); 246 + Contact.v ~from:n3 ~to_:n1 ~start:0. ~stop:60. ~rate ~owlt (); 247 + (* 3 <-> 4: t=0-30 *) 248 + Contact.v ~from:n3 ~to_:n4 ~start:0. ~stop:30. ~rate ~owlt (); 249 + Contact.v ~from:n4 ~to_:n3 ~start:0. ~stop:30. ~rate ~owlt (); 250 + (* 1 <-> 5: t=10-20 *) 251 + Contact.v ~from:n1 ~to_:n5 ~start:10. ~stop:20. ~rate ~owlt (); 252 + Contact.v ~from:n5 ~to_:n1 ~start:10. ~stop:20. ~rate ~owlt (); 253 + (* 4 <-> 5: t=0-10 *) 254 + Contact.v ~from:n4 ~to_:n5 ~start:0. ~stop:10. ~rate ~owlt (); 255 + Contact.v ~from:n5 ~to_:n4 ~start:0. ~stop:10. ~rate ~owlt (); 256 + (* 4 <-> 5: t=30-40 *) 257 + Contact.v ~from:n4 ~to_:n5 ~start:30. ~stop:40. ~rate ~owlt (); 258 + Contact.v ~from:n5 ~to_:n4 ~start:30. ~stop:40. ~rate ~owlt (); 259 + (* 4 <-> 5: t=50-60 *) 260 + Contact.v ~from:n4 ~to_:n5 ~start:50. ~stop:60. ~rate ~owlt (); 261 + Contact.v ~from:n5 ~to_:n4 ~start:50. ~stop:60. ~rate ~owlt (); 262 + ] 263 + 264 + (* Test: HDTN pyCGR tutorial - route from node 1 to node 5 at t=0 265 + Expected path: 1 -> 3 -> 4 -> 5 (3 hops) 266 + - Contact 1->3: start at t=0, arrive at 3 at t=1 (owlt=1) 267 + - Contact 3->4: start at t=1, arrive at 4 at t=2 268 + - Contact 4->5: start at t=2, arrive at 5 at t=3 269 + Expected arrival time: 3 seconds *) 270 + let test_hdtn_cgr_tutorial () = 271 + let n1 = Node.v "1" and n3 = Node.v "3" and n5 = Node.v "5" in 272 + let plan = Contact_plan.of_list hdtn_cgr_tutorial_contacts in 273 + let route = find_route plan ~src:n1 ~dst:n5 ~time:0. in 274 + Alcotest.(check bool) "route exists" true (Option.is_some route); 275 + let route = Option.get route in 276 + Alcotest.(check int) "3 hops" 3 (List.length (Route.hops route)); 277 + (* Verify the path goes through node 3 *) 278 + let first_hop = List.hd (Route.hops route) in 279 + Alcotest.(check node) "first hop to node 3" n3 (Contact.to_ first_hop); 280 + (* Arrival time: 3 seconds (1 second OWLT per hop) *) 281 + Alcotest.(check bool) 282 + "arrival at 3.0" true 283 + (float_eq 3. (Route.arrival_time route)) 284 + 285 + (* Test: HDTN pyCGR tutorial - no path when querying at wrong time 286 + At t=25, the 3->4 contact (t=0-30) is still active, but 287 + the 4->5 contact is in a gap (not active from t=10-30) *) 288 + let test_hdtn_cgr_tutorial_timing () = 289 + let n1 = Node.v "1" and n5 = Node.v "5" in 290 + let plan = Contact_plan.of_list hdtn_cgr_tutorial_contacts in 291 + (* At t=25: 1->3 active, 3->4 active (ends at 30), but 4->5 gap until t=30 *) 292 + let route = find_route plan ~src:n1 ~dst:n5 ~time:25. in 293 + Alcotest.(check bool) "route exists" true (Option.is_some route); 294 + let route = Option.get route in 295 + (* Path: 1->3 (arrive 26), 3->4 (arrive 27), wait for 4->5 at t=30, arrive 31 *) 296 + Alcotest.(check bool) 297 + "arrival at 31.0" true 298 + (float_eq 31. (Route.arrival_time route)) 299 + 300 + (* HDTN contactPlan_RoutingTest.json 301 + Nodes: 100, 1, 2, 3, 4, 200 302 + 8 contacts with 1 Gbps and 1 second OWLT *) 303 + let hdtn_routing_test_contacts = 304 + let n100 = Node.v "100" 305 + and n1 = Node.v "1" 306 + and n2 = Node.v "2" 307 + and n3 = Node.v "3" 308 + and n4 = Node.v "4" 309 + and n200 = Node.v "200" in 310 + let owlt = 1. in 311 + let rate = 1_000_000_000. in 312 + (* 1 Gbps *) 313 + [ 314 + Contact.v ~from:n100 ~to_:n1 ~start:0. ~stop:120. ~rate ~owlt (); 315 + Contact.v ~from:n1 ~to_:n2 ~start:0. ~stop:120. ~rate ~owlt (); 316 + Contact.v ~from:n2 ~to_:n4 ~start:11. ~stop:150. ~rate ~owlt (); 317 + Contact.v ~from:n4 ~to_:n200 ~start:0. ~stop:150. ~rate ~owlt (); 318 + Contact.v ~from:n1 ~to_:n3 ~start:11. ~stop:150. ~rate ~owlt (); 319 + Contact.v ~from:n1 ~to_:n2 ~start:17. ~stop:200. ~rate ~owlt (); 320 + Contact.v ~from:n2 ~to_:n4 ~start:20. ~stop:250. ~rate ~owlt (); 321 + Contact.v ~from:n3 ~to_:n4 ~start:25. ~stop:300. ~rate ~owlt (); 322 + ] 323 + 324 + (* Test: HDTN routing test - route from node 1 to node 4 at t=0 325 + Expected path: 1 -> 2 -> 4 (2 hops) 326 + - Contact 1->2: start at t=0, arrive at 2 at t=1 327 + - Contact 2->4: starts at t=11, so wait, start tx at t=11, arrive at 4 at t=12 328 + Expected arrival time: 12 seconds *) 329 + let test_hdtn_routing_basic () = 330 + let n1 = Node.v "1" and n2 = Node.v "2" and n4 = Node.v "4" in 331 + let plan = Contact_plan.of_list hdtn_routing_test_contacts in 332 + let route = find_route plan ~src:n1 ~dst:n4 ~time:0. in 333 + Alcotest.(check bool) "route exists" true (Option.is_some route); 334 + let route = Option.get route in 335 + Alcotest.(check int) "2 hops" 2 (List.length (Route.hops route)); 336 + (* Verify path goes through node 2 *) 337 + let first_hop = List.hd (Route.hops route) in 338 + Alcotest.(check node) "first hop to node 2" n2 (Contact.to_ first_hop); 339 + (* Arrival: arrive at 2 at t=1, wait for contact at t=11, arrive at 4 at t=12 *) 340 + Alcotest.(check bool) 341 + "arrival at 12.0" true 342 + (float_eq 12. (Route.arrival_time route)) 343 + 344 + (* Test: HDTN routing test - no reverse path (node 4 to node 1) 345 + The contacts are unidirectional, so 4->1 has no path *) 346 + let test_hdtn_routing_no_reverse () = 347 + let n1 = Node.v "1" and n4 = Node.v "4" in 348 + let plan = Contact_plan.of_list hdtn_routing_test_contacts in 349 + let route = find_route plan ~src:n4 ~dst:n1 ~time:0. in 350 + Alcotest.(check bool) "no reverse route" true (Option.is_none route) 351 + 352 + (* Test: HDTN routing test - alternative path via node 3 353 + From node 1 at t=15, the 1->2 contact at t=0-120 is active 354 + but 2->4 at t=11-150 would give arrival at t=17 355 + Alternative: 1->3 at t=11-150 (wait until 15), then 3->4 at t=25-300 356 + Path via 2: arrive at 2 at t=16, arrive at 4 at t=17 357 + Path via 3: arrive at 3 at t=16, wait until t=25, arrive at 4 at t=26 358 + So path via 2 is faster *) 359 + let test_hdtn_routing_choose_faster () = 360 + let n1 = Node.v "1" and n2 = Node.v "2" and n4 = Node.v "4" in 361 + let plan = Contact_plan.of_list hdtn_routing_test_contacts in 362 + let route = find_route plan ~src:n1 ~dst:n4 ~time:15. in 363 + Alcotest.(check bool) "route exists" true (Option.is_some route); 364 + let route = Option.get route in 365 + (* Should choose 1->2->4 path *) 366 + let first_hop = List.hd (Route.hops route) in 367 + Alcotest.(check node) "chose path via 2" n2 (Contact.to_ first_hop); 368 + (* Arrival: start at 15, arrive at 2 at 16, arrive at 4 at 17 *) 369 + Alcotest.(check bool) 370 + "arrival at 17.0" true 371 + (float_eq 17. (Route.arrival_time route)) 372 + 216 373 (* Suite *) 217 374 218 375 let suite = ··· 231 388 ( "contact plan", 232 389 [ Alcotest.test_case "operations" `Quick test_contact_plan ] ); 233 390 ("route", [ Alcotest.test_case "capacity" `Quick test_route_capacity ]); 391 + ( "hdtn", 392 + [ 393 + Alcotest.test_case "cgr tutorial" `Quick test_hdtn_cgr_tutorial; 394 + Alcotest.test_case "cgr tutorial timing" `Quick 395 + test_hdtn_cgr_tutorial_timing; 396 + Alcotest.test_case "routing basic" `Quick test_hdtn_routing_basic; 397 + Alcotest.test_case "routing no reverse" `Quick 398 + test_hdtn_routing_no_reverse; 399 + Alcotest.test_case "routing choose faster" `Quick 400 + test_hdtn_routing_choose_faster; 401 + ] ); 234 402 ]