Skip to content

Commit 886563b

Browse files
committed
feat: address feedback and add tests
1 parent 8ad7d2d commit 886563b

File tree

5 files changed

+274
-17
lines changed
  • bindings/python
  • include/OpenSpaceToolkit/Astrodynamics/Trajectory
  • src/OpenSpaceToolkit/Astrodynamics/Trajectory
  • test/OpenSpaceToolkit/Astrodynamics/Trajectory

5 files changed

+274
-17
lines changed

bindings/python/src/OpenSpaceToolkitAstrodynamicsPy/Trajectory/State.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,9 @@ inline void OpenSpaceToolkitAstrodynamicsPy_Trajectory_State(pybind11::module& a
279279

280280
.def(
281281
"is_near",
282-
&State::isNear,
282+
overload_cast<const State&, const std::unordered_map<Shared<const CoordinateSubset>, Real>&>(
283+
&State::isNear, const_
284+
),
283285
R"doc(
284286
Check if the state is near another state.
285287
@@ -288,12 +290,30 @@ inline void OpenSpaceToolkitAstrodynamicsPy_Trajectory_State(pybind11::module& a
288290
tolerance_map (dict[CoordinateSubset, float]): The tolerance map for the comparison.
289291
290292
Returns:
291-
bool: True if the states are near, False otherwise.
293+
dict[CoordinateSubset, bool]: The map of coordinate subsets and whether or not the state is near the other state.
292294
)doc",
293295
arg("state"),
294296
arg("tolerance_map")
295297
)
296298

299+
.def(
300+
"is_near",
301+
overload_cast<const State&, const std::unordered_map<Shared<const CoordinateSubset>, VectorXd>&>(
302+
&State::isNear, const_
303+
),
304+
R"doc(
305+
Check if the state is near another state.
306+
307+
Args:
308+
state (State): The state to compare to.
309+
tolerance_array_map (dict[CoordinateSubset, np.ndarray]): The tolerance array map for the comparison.
310+
311+
Returns:
312+
dict[CoordinateSubset, list[bool]]: The map of coordinate subsets and whether or not the state is near the other state.
313+
)doc",
314+
arg("state"),
315+
arg("tolerance_array_map")
316+
)
297317
.def_static(
298318
"undefined",
299319
&State::Undefined,

bindings/python/test/trajectory/test_state.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,3 +592,39 @@ def test_extract_coordinates(
592592
)
593593
assert len(pv_coordinates) == 6
594594
assert (pv_coordinates == state.get_coordinates()).all()
595+
596+
def test_is_near(
597+
self,
598+
state: State,
599+
):
600+
tolerance_map: dict[CoordinateSubset, float] = {
601+
CartesianPosition.default(): 1e-10,
602+
CartesianVelocity.default(): 1e-10,
603+
}
604+
is_near_result: dict[CoordinateSubset, bool] = state.is_near(
605+
state=state,
606+
tolerance_map=tolerance_map,
607+
)
608+
expected_is_near_result: dict[CoordinateSubset, bool] = {
609+
CartesianPosition.default(): True,
610+
CartesianVelocity.default(): True,
611+
}
612+
assert is_near_result == expected_is_near_result
613+
614+
def test_is_near_element_wise(
615+
self,
616+
state: State,
617+
):
618+
tolerance_array_map: dict[CoordinateSubset, np.ndarray] = {
619+
CartesianPosition.default(): np.array([1e-10, 1e-10, 1e-10]),
620+
CartesianVelocity.default(): np.array([1e-10, 1e-10, 1e-10]),
621+
}
622+
is_near_result: dict[CoordinateSubset, list[bool]] = state.is_near(
623+
state=state,
624+
tolerance_array_map=tolerance_array_map,
625+
)
626+
expected_is_near_result: dict[CoordinateSubset, bool] = {
627+
CartesianPosition.default(): [True, True, True],
628+
CartesianVelocity.default(): [True, True, True],
629+
}
630+
assert is_near_result == expected_is_near_result

include/OpenSpaceToolkit/Astrodynamics/Trajectory/State.hpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#define __OpenSpaceToolkit_Astrodynamics_Trajectory_State__
55

66
#include <OpenSpaceToolkit/Core/Container/Array.hpp>
7+
#include <OpenSpaceToolkit/Core/Type/Real.hpp>
78
#include <OpenSpaceToolkit/Core/Type/Shared.hpp>
89
#include <OpenSpaceToolkit/Core/Type/Size.hpp>
910

@@ -26,6 +27,7 @@ namespace trajectory
2627
{
2728

2829
using ostk::core::container::Array;
30+
using ostk::core::type::Real;
2931
using ostk::core::type::Shared;
3032
using ostk::core::type::Size;
3133

@@ -239,12 +241,23 @@ class State
239241
/// @return The transformed State
240242
State inFrame(const Shared<const Frame>& aFrameSPtr) const;
241243

242-
/// @brief Check if the State is within a given tolerance per subset of another State.
244+
/// @brief Check if the State is within a given tolerance of another State per subset.
243245
///
244246
/// @param aState The State to compare to
245247
/// @param aToleranceMap The tolerance map for each coordinate subset
246-
/// @return True if the States are within the tolerance, false otherwise
247-
bool isNear(const State& aState, const std::unordered_map<CoordinateSubset, Real>& aToleranceMap) const;
248+
/// @return The map of coordinate subsets and whether or not the State is within the tolerance
249+
std::unordered_map<Shared<const CoordinateSubset>, bool> isNear(
250+
const State& aState, const std::unordered_map<Shared<const CoordinateSubset>, Real>& aToleranceMap
251+
) const;
252+
253+
/// @brief Check if the State is within a given tolerance of another State per coordinate of each subset.
254+
///
255+
/// @param aState The State to compare to
256+
/// @param aToleranceArrayMap The tolerance array map for each coordinate subset
257+
/// @return The map of coordinate subsets and whether or not the State is within the tolerance
258+
std::unordered_map<Shared<const CoordinateSubset>, Array<bool>> isNear(
259+
const State& aState, const std::unordered_map<Shared<const CoordinateSubset>, VectorXd>& aToleranceArrayMap
260+
) const;
248261

249262
/// @brief Print the State to an output stream.
250263
///

src/OpenSpaceToolkit/Astrodynamics/Trajectory/State.cpp

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include <OpenSpaceToolkit/Mathematics/Geometry/3D/Transformation/Rotation/Quaternion.hpp>
77

88
#include <OpenSpaceToolkit/Astrodynamics/Trajectory/State.hpp>
9-
#include <OpenSpaceToolkit/Astrodynamics/Trajectory/State/CoordinateSubset.hpp>
109
#include <OpenSpaceToolkit/Astrodynamics/Trajectory/State/CoordinateSubset/AngularVelocity.hpp>
1110
#include <OpenSpaceToolkit/Astrodynamics/Trajectory/State/CoordinateSubset/AttitudeQuaternion.hpp>
1211
#include <OpenSpaceToolkit/Astrodynamics/Trajectory/State/CoordinateSubset/CartesianPosition.hpp>
@@ -23,7 +22,6 @@ using ostk::core::type::Index;
2322

2423
using ostk::mathematics::geometry::d3::transformation::rotation::Quaternion;
2524

26-
using ostk::astrodynamics::trajectory::state::CoordinateSubset;
2725
using ostk::astrodynamics::trajectory::state::coordinatesubset::AngularVelocity;
2826
using ostk::astrodynamics::trajectory::state::coordinatesubset::AttitudeQuaternion;
2927
using ostk::astrodynamics::trajectory::state::coordinatesubset::CartesianPosition;
@@ -499,29 +497,75 @@ State State::inFrame(const Shared<const Frame>& aFrameSPtr) const
499497
};
500498
}
501499

502-
bool State::isNear(const State& aState, const std::unordered_map<CoordinateSubset, Real>& aToleranceMap) const
500+
std::unordered_map<Shared<const CoordinateSubset>, bool> State::isNear(
501+
const State& aState, const std::unordered_map<Shared<const CoordinateSubset>, Real>& aToleranceMap
502+
) const
503503
{
504-
if (aToleranceMap.size() != coordinatesBrokerSPtr_->accessSubsets()->getSize())
504+
if (aToleranceMap.size() != coordinatesBrokerSPtr_->accessSubsets().getSize())
505505
{
506506
throw ostk::core::error::runtime::Wrong("Tolerance Map");
507507
}
508508

509509
const State deltaState = aState - *this;
510510

511-
bool isNear = true;
512-
for (const auto& [subset, tolerance] : aToleranceMap) {
513-
if (!coordinatesBrokerSPtr_->hasSubset(subset)) {
511+
std::unordered_map<Shared<const CoordinateSubset>, bool> isNearMap;
512+
for (const auto& [subsetSPtr, tolerance] : aToleranceMap)
513+
{
514+
if (!coordinatesBrokerSPtr_->hasSubset(subsetSPtr))
515+
{
516+
throw ostk::core::error::runtime::Wrong("Tolerance Map Coordinate Subset");
517+
}
518+
if (tolerance <= 0.0)
519+
{
520+
throw ostk::core::error::runtime::Wrong("Tolerance");
521+
}
522+
523+
const VectorXd deltaCoordinates = deltaState.extractCoordinate(subsetSPtr);
524+
525+
isNearMap[subsetSPtr] = (deltaCoordinates.norm() <= tolerance);
526+
}
527+
return isNearMap;
528+
}
529+
530+
std::unordered_map<Shared<const CoordinateSubset>, Array<bool>> State::isNear(
531+
const State& aState, const std::unordered_map<Shared<const CoordinateSubset>, VectorXd>& aToleranceArrayMap
532+
) const
533+
{
534+
if (aToleranceArrayMap.size() != coordinatesBrokerSPtr_->accessSubsets().getSize())
535+
{
536+
throw ostk::core::error::runtime::Wrong("Tolerance Map");
537+
}
538+
539+
const State deltaState = aState - *this;
540+
541+
std::unordered_map<Shared<const CoordinateSubset>, Array<bool>> isNearMap;
542+
for (const auto& [subsetSPtr, toleranceArray] : aToleranceArrayMap)
543+
{
544+
if (!coordinatesBrokerSPtr_->hasSubset(subsetSPtr))
545+
{
514546
throw ostk::core::error::runtime::Wrong("Tolerance Map Coordinate Subset");
515547
}
516548

517-
const VectorXd deltaCoordinates = deltaState.extractCoordinate(subset);
549+
const VectorXd deltaCoordinates = deltaState.extractCoordinate(subsetSPtr);
518550

519-
if (deltaCoordinates.norm() > tolerance) {
520-
isNear = false;
521-
break;
551+
if (toleranceArray.size() != deltaCoordinates.size())
552+
{
553+
throw ostk::core::error::runtime::Wrong("Tolerance Array Size");
554+
}
555+
if ((toleranceArray.array() <= 0.0).any())
556+
{
557+
throw ostk::core::error::runtime::Wrong("Tolerance Array");
558+
}
559+
560+
Array<bool> isNearArray;
561+
isNearArray.reserve(deltaCoordinates.size());
562+
for (int i = 0; i < deltaCoordinates.size(); ++i)
563+
{
564+
isNearArray.add(std::abs(deltaCoordinates[i]) <= toleranceArray[i]);
522565
}
566+
isNearMap[subsetSPtr] = isNearArray;
523567
}
524-
return isNear;
568+
return isNearMap;
525569
}
526570

527571
void State::print(std::ostream& anOutputStream, bool displayDecorator) const

test/OpenSpaceToolkit/Astrodynamics/Trajectory/State.test.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <Global.test.hpp>
1212

1313
using ostk::core::container::Array;
14+
using ostk::core::type::Real;
1415
using ostk::core::type::Shared;
1516

1617
using ostk::mathematics::geometry::d3::transformation::rotation::Quaternion;
@@ -1323,6 +1324,149 @@ TEST(OpenSpaceToolkit_Astrodynamics_Trajectory_State, InFrame)
13231324
}
13241325
}
13251326

1327+
TEST(OpenSpaceToolkit_Astrodynamics_Trajectory_State, isNear)
1328+
{
1329+
const Instant instant = Instant::DateTime(DateTime(2018, 1, 1, 0, 0, 0), Scale::UTC);
1330+
Shared<const Frame> frameSPtr = Frame::GCRF();
1331+
VectorXd coordinates(4);
1332+
coordinates << 0.0, 0.0, 0.0, 0.0;
1333+
VectorXd otherCoordinates(4);
1334+
otherCoordinates << -1.0, 0.0, 0.0, 0.1;
1335+
const Array<Shared<const CoordinateSubset>> subsets = {CartesianPosition::Default(), CoordinateSubset::Mass()};
1336+
1337+
const State state = {instant, coordinates, frameSPtr, subsets};
1338+
const State otherState = {instant, otherCoordinates, frameSPtr, subsets};
1339+
1340+
// Test success
1341+
{
1342+
const std::unordered_map<Shared<const CoordinateSubset>, Real> toleranceMap = {
1343+
{CartesianPosition::Default(), 2.0}, {CoordinateSubset::Mass(), 0.05}
1344+
};
1345+
1346+
const std::unordered_map<Shared<const CoordinateSubset>, bool> isNearMap = {
1347+
{CartesianPosition::Default(), true}, {CoordinateSubset::Mass(), false}
1348+
};
1349+
EXPECT_EQ(state.isNear(otherState, toleranceMap), isNearMap);
1350+
}
1351+
// Test failure wrong tolerance map
1352+
{
1353+
const std::unordered_map<Shared<const CoordinateSubset>, Real> toleranceMap = {
1354+
{CartesianPosition::Default(), 2.0}
1355+
};
1356+
1357+
EXPECT_ANY_THROW(state.isNear(otherState, toleranceMap));
1358+
}
1359+
1360+
// Test failure wrong tolerance map coordinate subset
1361+
{
1362+
const std::unordered_map<Shared<const CoordinateSubset>, Real> toleranceMap = {
1363+
{CartesianPosition::Default(), 2.0}, {CoordinateSubset::SurfaceArea(), 0.05}
1364+
};
1365+
1366+
EXPECT_ANY_THROW(state.isNear(otherState, toleranceMap));
1367+
}
1368+
1369+
// Test failure negative tolerance
1370+
{
1371+
const std::unordered_map<Shared<const CoordinateSubset>, Real> toleranceMap = {
1372+
{CartesianPosition::Default(), -2.0}, {CoordinateSubset::Mass(), -0.05}
1373+
};
1374+
1375+
EXPECT_ANY_THROW(state.isNear(otherState, toleranceMap));
1376+
}
1377+
}
1378+
1379+
TEST(OpenSpaceToolkit_Astrodynamics_Trajectory_State, isNearElementWise)
1380+
{
1381+
const Instant instant = Instant::DateTime(DateTime(2018, 1, 1, 0, 0, 0), Scale::UTC);
1382+
Shared<const Frame> frameSPtr = Frame::GCRF();
1383+
VectorXd coordinates(4);
1384+
coordinates << 0.0, 0.0, 0.0, 0.0;
1385+
VectorXd otherCoordinates(4);
1386+
otherCoordinates << -1.0, 2.0, 0.0, 0.1;
1387+
const Array<Shared<const CoordinateSubset>> subsets = {CartesianPosition::Default(), CoordinateSubset::Mass()};
1388+
1389+
const State state = {instant, coordinates, frameSPtr, subsets};
1390+
const State otherState = {instant, otherCoordinates, frameSPtr, subsets};
1391+
1392+
// Test success
1393+
{
1394+
VectorXd positionTolerance(3);
1395+
positionTolerance << 2.0, 0.5, 0.5;
1396+
1397+
VectorXd massTolerance(1);
1398+
massTolerance << 0.05;
1399+
1400+
const std::unordered_map<Shared<const CoordinateSubset>, VectorXd> toleranceArrayMap = {
1401+
{CartesianPosition::Default(), positionTolerance}, {CoordinateSubset::Mass(), massTolerance}
1402+
};
1403+
1404+
const std::unordered_map<Shared<const CoordinateSubset>, Array<bool>> isNearArrayMap = {
1405+
{CartesianPosition::Default(), Array<bool>({true, false, true})},
1406+
{CoordinateSubset::Mass(), Array<bool>({false})}
1407+
};
1408+
1409+
EXPECT_EQ(state.isNear(otherState, toleranceArrayMap), isNearArrayMap);
1410+
}
1411+
1412+
// Test failure wrong tolerance map
1413+
{
1414+
VectorXd positionTolerance(3);
1415+
positionTolerance << 2.0, 0.5, 0.5;
1416+
1417+
const std::unordered_map<Shared<const CoordinateSubset>, VectorXd> toleranceArrayMap = {
1418+
{CartesianPosition::Default(), positionTolerance}
1419+
};
1420+
1421+
EXPECT_ANY_THROW(state.isNear(otherState, toleranceArrayMap));
1422+
}
1423+
1424+
// Test failure wrong tolerance map coordinate subset
1425+
{
1426+
VectorXd positionTolerance(3);
1427+
positionTolerance << 2.0, 0.5, 0.5;
1428+
1429+
VectorXd areaTolerance(1);
1430+
areaTolerance << 0.05;
1431+
1432+
const std::unordered_map<Shared<const CoordinateSubset>, VectorXd> toleranceArrayMap = {
1433+
{CartesianPosition::Default(), positionTolerance}, {CoordinateSubset::SurfaceArea(), areaTolerance}
1434+
};
1435+
1436+
EXPECT_ANY_THROW(state.isNear(otherState, toleranceArrayMap));
1437+
}
1438+
1439+
// Test failure wrong tolerance array size
1440+
{
1441+
VectorXd positionTolerance(2);
1442+
positionTolerance << 2.0, 0.5;
1443+
1444+
VectorXd massTolerance(1);
1445+
massTolerance << 0.05;
1446+
1447+
const std::unordered_map<Shared<const CoordinateSubset>, VectorXd> toleranceArrayMap = {
1448+
{CartesianPosition::Default(), positionTolerance}, {CoordinateSubset::Mass(), massTolerance}
1449+
};
1450+
1451+
EXPECT_ANY_THROW(state.isNear(otherState, toleranceArrayMap));
1452+
}
1453+
1454+
// Test failure negative tolerance
1455+
{
1456+
VectorXd positionTolerance(3);
1457+
positionTolerance << -2.0, 0.5, 0.5;
1458+
1459+
VectorXd massTolerance(1);
1460+
massTolerance << 0.05;
1461+
1462+
const std::unordered_map<Shared<const CoordinateSubset>, VectorXd> toleranceArrayMap = {
1463+
{CartesianPosition::Default(), positionTolerance}, {CoordinateSubset::Mass(), massTolerance}
1464+
};
1465+
1466+
EXPECT_ANY_THROW(state.isNear(otherState, toleranceArrayMap));
1467+
}
1468+
}
1469+
13261470
TEST(OpenSpaceToolkit_Astrodynamics_Trajectory_State, Undefined)
13271471
{
13281472
{

0 commit comments

Comments
 (0)