diff --git a/bindings/python/src/OpenSpaceToolkitAstrodynamicsPy/Trajectory/State.cpp b/bindings/python/src/OpenSpaceToolkitAstrodynamicsPy/Trajectory/State.cpp index 26a240824..9a81519f9 100644 --- a/bindings/python/src/OpenSpaceToolkitAstrodynamicsPy/Trajectory/State.cpp +++ b/bindings/python/src/OpenSpaceToolkitAstrodynamicsPy/Trajectory/State.cpp @@ -41,6 +41,7 @@ inline void OpenSpaceToolkitAstrodynamicsPy_Trajectory_State(pybind11::module& a arg("frame"), arg("coordinates_subsets") ) + .def(init(), arg("state")) .def(self == self) .def(self != self) diff --git a/bindings/python/test/trajectory/test_state.py b/bindings/python/test/trajectory/test_state.py index 753e5c18d..af6b5128a 100644 --- a/bindings/python/test/trajectory/test_state.py +++ b/bindings/python/test/trajectory/test_state.py @@ -105,6 +105,38 @@ def test_subsets_constructor( assert isinstance(state, State) assert state.is_defined() + def test_custom_state_class_generator( + self, + instant: Instant, + position: Position, + velocity: Velocity, + frame: Frame, + ): + state = State( + instant, + np.append(position.get_coordinates(), velocity.get_coordinates()), + frame, + [CartesianPosition.default(), CartesianVelocity.default()], + ) + + MySuperFunState: type = State.template( + frame, + [CartesianPosition.default(), CartesianVelocity.default()], + ) + + custom_state: MySuperFunState = MySuperFunState( + instant, + np.append(position.get_coordinates(), velocity.get_coordinates()), + ) + + assert custom_state is not None + assert isinstance(custom_state, MySuperFunState) + assert isinstance(state, State) + assert custom_state.is_defined() + + assert custom_state == state + assert custom_state is not state + def test_comparators(self, state: State): assert (state == state) is True assert (state != state) is False diff --git a/bindings/python/tools/python/ostk/astrodynamics/__init__.py b/bindings/python/tools/python/ostk/astrodynamics/__init__.py index c4197af1a..6d5030913 100755 --- a/bindings/python/tools/python/ostk/astrodynamics/__init__.py +++ b/bindings/python/tools/python/ostk/astrodynamics/__init__.py @@ -3,3 +3,9 @@ from ostk.physics import * from .OpenSpaceToolkitAstrodynamicsPy import * + +from .pytrajectory.pystate import State as PyState + +trajectory.State = ( + PyState # Override the pure c++ State class with the modified Python one +) diff --git a/bindings/python/tools/python/ostk/astrodynamics/pytrajectory/__init__.py b/bindings/python/tools/python/ostk/astrodynamics/pytrajectory/__init__.py new file mode 100644 index 000000000..8ee103fef --- /dev/null +++ b/bindings/python/tools/python/ostk/astrodynamics/pytrajectory/__init__.py @@ -0,0 +1 @@ +# Apache License 2.0 diff --git a/bindings/python/tools/python/ostk/astrodynamics/pytrajectory/pystate.py b/bindings/python/tools/python/ostk/astrodynamics/pytrajectory/pystate.py new file mode 100644 index 000000000..143d8d7f2 --- /dev/null +++ b/bindings/python/tools/python/ostk/astrodynamics/pytrajectory/pystate.py @@ -0,0 +1,38 @@ +# Apache License 2.0 + + +# Python-only State functionality +import numpy as np + +from ostk.physics.coordinate import Frame +from ostk.physics.time import Instant + +from ostk.astrodynamics.trajectory import State, StateBuilder + + +@staticmethod +def custom_class_generator(frame: Frame, coordinates_subsets: list) -> type: + """ + Emit a custom class type for States. This is meta-programming syntactic sugar on top of the StateBuilder class. + + StateType = State.template(frame, coordinates_subsets) + state = StateType(instant, coordinates) + + is equivalent to + + state_builder = StateBuilder(frame, coordinates_subsets) + state = state_builder.build(instant, coordinates) + """ + + class StateTemplateType(State): + state_builder: StateBuilder = StateBuilder(frame, coordinates_subsets) + + def __init__(self, instant: Instant, coordinates: np.ndarray): + super().__init__( + StateTemplateType.state_builder.build(instant, coordinates) + ) + + return StateTemplateType + + +State.template = custom_class_generator diff --git a/include/OpenSpaceToolkit/Astrodynamics/Trajectory/State.hpp b/include/OpenSpaceToolkit/Astrodynamics/Trajectory/State.hpp index 327ad22ca..0dd222a0d 100644 --- a/include/OpenSpaceToolkit/Astrodynamics/Trajectory/State.hpp +++ b/include/OpenSpaceToolkit/Astrodynamics/Trajectory/State.hpp @@ -83,6 +83,19 @@ class State State(const Instant& anInstant, const Position& aPosition, const Velocity& aVelocity); + /// @brief Copy constructor. + /// + /// @param [in] aState An existing State + + State(const State& aState); + + /// @brief Copy-assignment operator + /// + /// @param [in] aState The State to copy + /// @return The modified State + + State& operator=(const State& aState); + /// @brief Equality operator. /// /// @param [in] aState The State to compare to diff --git a/src/OpenSpaceToolkit/Astrodynamics/Trajectory/State.cpp b/src/OpenSpaceToolkit/Astrodynamics/Trajectory/State.cpp index 4b14e2746..1b7b511e4 100644 --- a/src/OpenSpaceToolkit/Astrodynamics/Trajectory/State.cpp +++ b/src/OpenSpaceToolkit/Astrodynamics/Trajectory/State.cpp @@ -84,6 +84,26 @@ State::State(const Instant& anInstant, const Position& aPosition, const Velocity this->coordinatesBrokerSPtr_ = coordinatesBrokerSPtr; } +State::State(const State& aState) + : instant_(aState.instant_), + coordinates_(aState.coordinates_), + frameSPtr_(aState.frameSPtr_), + coordinatesBrokerSPtr_(aState.coordinatesBrokerSPtr_) +{ +} + +State& State::operator=(const State& aState) +{ + if (this != &aState) + { + instant_ = aState.instant_; + coordinates_ = aState.coordinates_; + frameSPtr_ = aState.frameSPtr_; + coordinatesBrokerSPtr_ = aState.coordinatesBrokerSPtr_; + } + return *this; +} + bool State::operator==(const State& aState) const { if ((!this->isDefined()) || (!aState.isDefined())) diff --git a/test/OpenSpaceToolkit/Astrodynamics/Trajectory/State.test.cpp b/test/OpenSpaceToolkit/Astrodynamics/Trajectory/State.test.cpp index fc07a4e38..d89d37c34 100644 --- a/test/OpenSpaceToolkit/Astrodynamics/Trajectory/State.test.cpp +++ b/test/OpenSpaceToolkit/Astrodynamics/Trajectory/State.test.cpp @@ -104,6 +104,52 @@ TEST(OpenSpaceToolkit_Astrodynamics_Trajectory_State, Constructor) EXPECT_ANY_THROW(State state(instant, position, velocity);); } + + { + const Instant instant = Instant::DateTime(DateTime(2018, 1, 1, 0, 0, 0), Scale::UTC); + VectorXd coordinates(6); + coordinates << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0; + const Shared brokerSPtr = std::make_shared( + CoordinatesBroker({CartesianPosition::Default(), CartesianVelocity::Default()}) + ); + + State state(instant, coordinates, Frame::GCRF(), brokerSPtr); + + EXPECT_NO_THROW(State anotherState(state)); + + const State anotherState(state); + + EXPECT_EQ(state, anotherState); + } +} + +TEST(OpenSpaceToolkit_Astrodynamics_Trajectory_State, CopyAssignmentOperator) +{ + // Create a state + const Instant instant = Instant::DateTime(DateTime(2018, 1, 1, 0, 0, 0), Scale::UTC); + VectorXd coordinates(6); + coordinates << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0; + const Shared brokerSPtr = std::make_shared( + CoordinatesBroker({CartesianPosition::Default(), CartesianVelocity::Default()}) + ); + State aState = {instant, coordinates, Frame::GCRF(), brokerSPtr}; + + // Copy the state + State anotherState = State::Undefined(); + + // Check that the two states are no longer equal + EXPECT_NE(aState, anotherState); + + anotherState = aState; + + // Check that the two states are equal + EXPECT_EQ(aState, anotherState); + + // Modify the original state + aState = State::Undefined(); + + // Check that the two states are no longer equal + EXPECT_NE(aState, anotherState); } TEST(OpenSpaceToolkit_Astrodynamics_Trajectory_State, EqualToOperator)