@@ -42,50 +42,124 @@ const Array<Shared<const CoordinateSubset>> Maneuver::RequiredCoordinateSubsets
4242const Shared<const CoordinateSubset> Maneuver::DefaultAccelerationCoordinateSubsetSPtr = RequiredCoordinateSubsets[2 ];
4343
4444Maneuver::Maneuver (const Array<State>& aStateArray)
45- : states_(aStateArray)
4645{
47- // Sanitize the inputs
48- if (this ->states_ .isEmpty ())
46+ if (aStateArray.isEmpty ())
4947 {
5048 throw ostk::core::error::RuntimeError (" No states provided." );
5149 }
5250
53- for (const auto & coordinateSubset : RequiredCoordinateSubsets)
51+ // Check if the states are sorted and contain all required coordinate subsets
52+ for (Size k = 0 ; k < aStateArray.getSize (); ++k)
5453 {
55- if (!std::all_of (
56- states_.begin (),
57- states_.end (),
58- [&coordinateSubset](const State& aState)
59- {
60- return aState.hasSubset (coordinateSubset);
61- }
62- ))
54+ const State& state = aStateArray[k];
55+
56+ if (k < aStateArray.getSize () - 1 )
57+ {
58+ const State& nextState = aStateArray[k + 1 ];
59+ if (state.accessInstant () >= nextState.accessInstant ())
60+ {
61+ throw ostk::core::error::runtime::Wrong (
62+ " Unsorted or Duplicate State Array" ,
63+ String::Format (
64+ " Index {}: {} > Index {}: {}" ,
65+ k,
66+ state.accessInstant ().toString (),
67+ k + 1 ,
68+ nextState.accessInstant ().toString ()
69+ )
70+ );
71+ }
72+ }
73+
74+ for (const auto & coordinateSubset : RequiredCoordinateSubsets)
6375 {
64- throw ostk::core::error::RuntimeError (String::Format (" {} not found in states." , coordinateSubset->getName ())
65- );
76+ if (!state.hasSubset (coordinateSubset))
77+ {
78+ throw ostk::core::error::RuntimeError (String::Format (
79+ " Coordinate Subset {} not found in states at index {}." , coordinateSubset->getName (), k
80+ ));
81+ }
6682 }
6783 }
6884
69- const Duration maneuverDuration = states_.accessLast ().accessInstant () - states_.accessFirst ().accessInstant ();
85+ // Sanitize the states:
86+ // - Check there are no positive mass flow rate states
87+ // - Remove all leading zero mass flow rate states
88+ // - Allow a single trailing zero mass flow rate state
89+ Array<State> sanitizedStates = Array<State>::Empty ();
7090 Duration largestInterval = Duration::Zero ();
91+ bool maneuverStartFound = false ;
92+ bool maneuverEndFound = false ;
7193
72- for (Size k = 0 ; k < states_ .getSize () - 1 ; ++k )
94+ for (Size k = 0 ; k < aStateArray .getSize (); k++ )
7395 {
74- if (states_[k].accessInstant () >= states_[k + 1 ].accessInstant ())
96+ const State& state = aStateArray[k];
97+ const Real massFlowRate = state.extractCoordinate (RequiredCoordinateSubsets[3 ])[0 ];
98+
99+ // Check mass flow rate is not positive
100+ if (massFlowRate > 0.0 )
101+ {
102+ throw ostk::core::error::RuntimeError (String::Format (" Positive mass flow rate at index {}." , k));
103+ }
104+
105+ if (massFlowRate < 0.0 )
106+ {
107+ // check that once we register a zero, no more negative mass flow rate states are found afterwards
108+ if (maneuverEndFound)
109+ {
110+ throw ostk::core::error::RuntimeError (
111+ String::Format (" Negative mass flow rate at index {} after a zero mass flow rate." , k)
112+ );
113+ }
114+
115+ maneuverStartFound = true ;
116+ if (!sanitizedStates.isEmpty ())
117+ {
118+ largestInterval =
119+ std::max (largestInterval, state.accessInstant () - sanitizedStates.accessLast ().accessInstant ());
120+ }
121+ sanitizedStates.add (state);
122+ continue ;
123+ }
124+
125+ // (From here onwards it's only zero mass flow rate states)
126+
127+ // Don't add leading zero mass flow rate states
128+ if (!maneuverStartFound)
129+ {
130+ continue ;
131+ }
132+
133+ // Add only one trailing zero mass flow rate state
134+ if (!maneuverEndFound)
75135 {
76- throw ostk::core::error::runtime::Wrong (" Unsorted or Duplicate State Array" );
136+ maneuverEndFound = true ;
137+ if (!sanitizedStates.isEmpty ())
138+ {
139+ largestInterval =
140+ std::max (largestInterval, state.accessInstant () - sanitizedStates.accessLast ().accessInstant ());
141+ }
142+ sanitizedStates.add (state);
143+ continue ;
77144 }
145+ }
78146
79- largestInterval = std::max (largestInterval, states_[k + 1 ].accessInstant () - states_[k].accessInstant ());
147+ if (sanitizedStates.isEmpty ())
148+ {
149+ throw ostk::core::error::RuntimeError (" No states left after sanitization." );
80150 }
81151
152+ // Check the largest interval between states is within the recommended limits
82153 if (largestInterval > Maneuver::MaximumRecommendedInterpolationInterval)
83154 {
84155 std::cout << " WARNING: Some intervals between the instants defined for this Maneuver are larger than the "
85156 " maximum recommended interpolation interval of "
86157 << Maneuver::MaximumRecommendedInterpolationInterval.inSeconds () << " seconds." << std::endl;
87158 }
88159
160+ // Check the maneuver duration is within the recommended limits
161+ const Duration maneuverDuration =
162+ sanitizedStates.accessLast ().accessInstant () - sanitizedStates.accessFirst ().accessInstant ();
89163 if (maneuverDuration < Maneuver::MinimumRecommendedDuration)
90164 {
91165 std::cout
@@ -95,25 +169,7 @@ Maneuver::Maneuver(const Array<State>& aStateArray)
95169 << std::endl;
96170 }
97171
98- // Ensure that mass flow rate profile is expressed in strictly negative numbers
99- if (std::any_of (
100- states_.begin (),
101- states_.end () - 1 ,
102- [](const State& aState)
103- {
104- return aState.extractCoordinate (RequiredCoordinateSubsets[3 ])[0 ] >= 0.0 ;
105- }
106- ))
107- {
108- throw ostk::core::error::RuntimeError (
109- " Mass flow rate profile must have strictly negative values (except the last state which may be zero)."
110- );
111- }
112-
113- if (states_.accessLast ().extractCoordinate (RequiredCoordinateSubsets[3 ])[0 ] > 0.0 )
114- {
115- throw ostk::core::error::RuntimeError (" Last state must have non-positive mass flow rate." );
116- }
172+ states_ = sanitizedStates;
117173}
118174
119175bool Maneuver::operator ==(const Maneuver& aManeuver) const
0 commit comments