@@ -1210,13 +1210,14 @@ TEST_F(OpenSpaceToolkit_Astrodynamics_Access_Generator, ComputeAccesses_7)
12101210 // Compute Accesses
12111211 // Choose the analysis interval and step size such that:
12121212 // - Exactly 1 coarse instant is out-of-access (at the orbitEpoch).
1213- // - The final coarse instant is less than one step after the previous, and is in-access.
1213+ // - The final coarse instant is less than one step after the previous coarse instant , and is in-access.
12141214 // - The final coarse instant, minus one step (2024-12-31 23:59:30), is in-access.
12151215 // 2024-12-31 23:57:00 [UTC] | inAccess: 1
12161216 // 2024-12-31 23:58:00 [UTC] | inAccess: 1
12171217 // 2024-12-31 23:59:00 [UTC] | inAccess: 1
1218+ // 2024-12-31 23:59:30 [UTC] | inAccess: 1 (not a coarse instant)
12181219 // 2025-01-01 00:00:00 [UTC] | inAccess: 0
1219- // 2025-01-01 00:00:30 [UTC] | inAccess: 1
1220+ // 2025-01-01 00:00:30 [UTC] | inAccess: 1 (final coarse instant)
12201221 const Interval accessAnalysisInterval =
12211222 Interval::Closed (orbitEpoch - Duration::Minutes (3.0 ), orbitEpoch + Duration::Seconds (30.0 ));
12221223
@@ -1235,6 +1236,90 @@ TEST_F(OpenSpaceToolkit_Astrodynamics_Access_Generator, ComputeAccesses_7)
12351236 }
12361237}
12371238
1239+ TEST_F (OpenSpaceToolkit_Astrodynamics_Access_Generator, ComputeAccesses_8)
1240+ {
1241+ // Ensure that accesses not found by the coarse search are not inadvertedly used when computing precise crossings.
1242+
1243+ {
1244+ // Create an orbit that is directly overhead the North Pole at its epoch, and flies "west to east" (270 -> 90
1245+ // degrees azimuth)
1246+ const Instant orbitEpoch = Instant::Parse (" 2025-01-01T00:00:00" , Scale::UTC, DateTime::Format::ISO8601);
1247+ const Position position = Position::Meters ({0.0 , 0.0 , 7e6 }, Frame::ITRF ()).inFrame (Frame::GCRF (), orbitEpoch);
1248+ const Velocity velocity =
1249+ Velocity::MetersPerSecond ({0.0 , 8e3 , 0.0 }, Frame::ITRF ()).inFrame (position, Frame::GCRF (), orbitEpoch);
1250+
1251+ const Orbit orbit = {
1252+ Kepler (
1253+ COE::Cartesian ({position, velocity}, defaultEarthSPtr_->getGravitationalParameter ()),
1254+ orbitEpoch,
1255+ defaultEarthSPtr_->getGravitationalParameter (),
1256+ defaultEarthSPtr_->getEquatorialRadius (),
1257+ EarthGravitationalModel::EGM2008.J2_ ,
1258+ EarthGravitationalModel::EGM2008.J4_ ,
1259+ Kepler::PerturbationType::None
1260+ ),
1261+ defaultEarthSPtr_
1262+ };
1263+
1264+ // Create an AccessTarget at the North Pole with an azimuth-elevation mask such that:
1265+ // - Access 1 (~orbitEpoch - 1 orbital period) is longer than the coarse step size
1266+ // - Access 2 (~orbitEpoch) is shorter than the coarse step size
1267+ // - Access 3 (~orbitEpoch + 1 orbital period) is longer than the coarse step size
1268+ // - The midpoint of [<coarse LOS of Access 1> + 1 step, <coarse AOS of Access 3>] falls within Access 2
1269+ const LLA lla = {
1270+ Angle::Degrees (90.0 ),
1271+ Angle::Degrees (0.0 ),
1272+ Length::Meters (0.0 ),
1273+ };
1274+
1275+ // - A "tall blockage" around 270 -> 90 azimuth makes Access 2 shorter, but doesn't affect Access 1 or 3
1276+ // - A "low point" around 295 azimuth makes Access 3's AOS earlier, which is needed so that the midpoint between
1277+ // the coarse LOS of Access 1 and coarse AOS of Access 3 falls within Access 2.
1278+ const ostk::core::container::Map<Real, Real> azimuthElevationMask = {
1279+ {1.0 , 5.0 },
1280+ {1.5 , 0.0 },
1281+ {2.0 , 0.0 },
1282+ {84.0 , 5.0 },
1283+ {85.0 , 55.0 },
1284+ {95.0 , 55.0 },
1285+ {96.0 , 5.0 },
1286+ {264.0 , 5.0 },
1287+ {265.0 , 87.0 },
1288+ {275.0 , 87.0 },
1289+ {276.0 , 5.0 },
1290+ {290.0 , -5.0 },
1291+ {300.0 , -5.0 },
1292+ {359.0 , 0.0 }
1293+ };
1294+ const ostk::mathematics::object::Interval<Real> rangeRange =
1295+ ostk::mathematics::object::Interval<Real>::Closed (0.0 , 10000e3 );
1296+
1297+ const VisibilityCriterion visibilityCriterion =
1298+ VisibilityCriterion::FromAERMask (azimuthElevationMask, rangeRange);
1299+
1300+ const AccessTarget accessTarget =
1301+ AccessTarget::FromPosition (visibilityCriterion, Position::FromLLA (lla, defaultEarthSPtr_));
1302+
1303+ // Compute Accesses
1304+ // Choose the analysis interval and step size such that Access 2 is not found in the coarse search.
1305+ const Interval accessAnalysisInterval = Interval::Closed (
1306+ orbitEpoch - Duration::Hours (2.0 ) + Duration::Minutes (1.0 ), orbitEpoch + Duration::Hours (2.0 )
1307+ );
1308+
1309+ const Generator generator =
1310+ Generator (defaultEnvironment_, Duration::Seconds (120.0 ), Duration::Seconds (5.0 ), {}, {});
1311+
1312+ const Array<Access> accesses = generator.computeAccesses (accessAnalysisInterval, accessTarget, orbit);
1313+
1314+ // Expect 2 accesses (1 and 3)
1315+ EXPECT_EQ (accesses.getSize (), 2 );
1316+
1317+ // Regression test; if Access 2 was found when computing precise crossings for Access 3, then Access 3 would be
1318+ // around 2 hours long.
1319+ EXPECT_LT (accesses[1 ].getInterval ().getDuration (), Duration::Minutes (20.0 ));
1320+ }
1321+ }
1322+
12381323TEST_F (OpenSpaceToolkit_Astrodynamics_Access_Generator, SetStep)
12391324{
12401325 {
0 commit comments