Skip to content

Commit 2851844

Browse files
authored
fix: fix starting lower bound when finding precise access AOS (#648)
* fix: fix precise crossing search start * chore: address comments
1 parent 668f536 commit 2851844

File tree

2 files changed

+95
-5
lines changed

2 files changed

+95
-5
lines changed

src/OpenSpaceToolkit/Astrodynamics/Access/Generator.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -849,10 +849,15 @@ Array<physics::time::Interval> Generator::computePreciseCrossings(
849849
// 35 | y | true <- end of analysisInterval
850850
//
851851
// At t=35, if we took a 10s step back to t=25, then both endpoints appear to be in an access, and the root
852-
// finder will fail because it doesn't bracket a zero-crossing. Instead, we should start from the end of the
853-
// previous Interval at t=20, and take a 10s step forward.
852+
// finder will fail because it doesn't bracket a zero-crossing.
853+
// In the general case, we should start from whichever is greater: the previous interval plus one step, or the
854+
// current interval minus one step.
855+
856+
const Instant intervalPreviousStep = interval.getStart() - this->step_;
857+
854858
const Instant lowerBoundPreviousInstant =
855-
i == 0 ? (interval.getStart() - this->step_) : (accessIntervals[i - 1].getEnd() + this->step_);
859+
i == 0 ? intervalPreviousStep
860+
: std::max(intervalPreviousStep, accessIntervals[i - 1].getEnd() + this->step_);
856861

857862
const Instant lowerBoundInstant = interval.getStart();
858863

test/OpenSpaceToolkit/Astrodynamics/Access/Generator.test.cpp

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
12381323
TEST_F(OpenSpaceToolkit_Astrodynamics_Access_Generator, SetStep)
12391324
{
12401325
{

0 commit comments

Comments
 (0)