@@ -143,8 +143,8 @@ ParseDate(const Hash* hash, const string& name, double& jd)
143143 * SemiMajorAxis or PericenterDistance is in kilometers.
144144 */
145145static std::unique_ptr<celestia::ephem::Orbit>
146- CreateEllipticalOrbit (const Hash* orbitData,
147- bool usePlanetUnits)
146+ CreateKeplerianOrbit (const Hash* orbitData,
147+ bool usePlanetUnits)
148148{
149149
150150 // default units for planets are AU and years, otherwise km and days
@@ -153,78 +153,85 @@ CreateEllipticalOrbit(const Hash* orbitData,
153153 double timeScale;
154154 GetDefaultUnits (usePlanetUnits, distanceScale, timeScale);
155155
156+ astro::KeplerElements elements;
157+
158+ elements.eccentricity = orbitData->getNumber <double >(" Eccentricity" ).value_or (0.0 );
159+ if (elements.eccentricity < 0.0 )
160+ {
161+ GetLogger ()->error (" Negative eccentricity is invalid.\n " );
162+ return nullptr ;
163+ }
164+ else if (elements.eccentricity == 1.0 )
165+ {
166+ GetLogger ()->error (" Parabolic orbits are not supported.\n " );
167+ return nullptr ;
168+ }
169+
156170 // SemiMajorAxis and Period are absolutely required; everything
157171 // else has a reasonable default.
158- double pericenterDistance = 0.0 ;
159- std::optional<double > semiMajorAxis = orbitData->getLength <double >(" SemiMajorAxis" , 1.0 , distanceScale);
160- if (!semiMajorAxis.has_value ())
172+ if (auto semiMajorAxisValue = orbitData->getLength <double >(" SemiMajorAxis" , 1.0 , distanceScale); semiMajorAxisValue.has_value ())
161173 {
162- if (auto pericenter = orbitData->getLength <double >(" PericenterDistance" , 1.0 , distanceScale); pericenter.has_value ())
163- {
164- pericenterDistance = *pericenter;
165- }
166- else
167- {
168- GetLogger ()->error (" SemiMajorAxis/PericenterDistance missing! Skipping planet . . .\n " );
169- return nullptr ;
170- }
174+ elements.semimajorAxis = *semiMajorAxisValue;
175+ }
176+ else if (auto pericenter = orbitData->getLength <double >(" PericenterDistance" , 1.0 , distanceScale); pericenter.has_value ())
177+ {
178+ elements.semimajorAxis = *pericenter / (1.0 - elements.eccentricity );
179+ }
180+ else
181+ {
182+ GetLogger ()->error (" SemiMajorAxis/PericenterDistance missing from orbit definition.\n " );
183+ return nullptr ;
171184 }
172185
173- double period = 0.0 ;
174186 if (auto periodValue = orbitData->getTime <double >(" Period" , 1.0 , timeScale); periodValue.has_value ())
175187 {
176- period = *periodValue;
188+ elements.period = *periodValue;
189+ if (elements.period == 0.0 )
190+ {
191+ GetLogger ()->error (" Period cannot be zero.\n " );
192+ return nullptr ;
193+ }
177194 }
178195 else
179196 {
180- GetLogger ()->error (" Period missing! Skipping planet . . .\n " );
197+ GetLogger ()->error (" Period must be specified in EllipticalOrbit .\n " );
181198 return nullptr ;
182199 }
183200
184- auto eccentricity = orbitData->getNumber <double >(" Eccentricity " ).value_or (0.0 );
201+ elements. inclination = orbitData->getAngle <double >(" Inclination " ).value_or (0.0 );
185202
186- auto inclination = orbitData->getAngle <double >(" Inclination " ).value_or (0.0 );
203+ elements. longAscendingNode = orbitData->getAngle <double >(" AscendingNode " ).value_or (0.0 );
187204
188- double ascendingNode = orbitData->getAngle <double >(" AscendingNode" ).value_or (0.0 );
189-
190- double argOfPericenter = 0.0 ;
191205 if (auto argPeri = orbitData->getAngle <double >(" ArgOfPericenter" ); argPeri.has_value ())
192206 {
193- argOfPericenter = *argPeri;
207+ elements. argPericenter = *argPeri;
194208 }
195209 else if (auto longPeri = orbitData->getAngle <double >(" LongOfPericenter" ); longPeri.has_value ())
196210 {
197- argOfPericenter = *longPeri - ascendingNode ;
211+ elements. argPericenter = *longPeri - elements. longAscendingNode ;
198212 }
199213
200214 double epoch = astro::J2000;
201215 ParseDate (orbitData, " Epoch" , epoch);
202216
203217 // Accept either the mean anomaly or mean longitude--use mean anomaly
204218 // if both are specified.
205- double anomalyAtEpoch = 0.0 ;
206219 if (auto meanAnomaly = orbitData->getAngle <double >(" MeanAnomaly" ); meanAnomaly.has_value ())
207- {
208- anomalyAtEpoch = *meanAnomaly;
209- }
220+ elements.meanAnomaly = *meanAnomaly;
210221 else if (auto meanLongitude = orbitData->getAngle <double >(" MeanLongitude" ); meanLongitude.has_value ())
222+ elements.meanAnomaly = *meanLongitude - (elements.argPericenter + elements.longAscendingNode );
223+
224+ elements.inclination = celmath::degToRad (elements.inclination );
225+ elements.longAscendingNode = celmath::degToRad (elements.longAscendingNode );
226+ elements.argPericenter = celmath::degToRad (elements.argPericenter );
227+ elements.meanAnomaly = celmath::degToRad (elements.meanAnomaly );
228+
229+ if (elements.eccentricity < 1.0 )
211230 {
212- anomalyAtEpoch = *meanLongitude - (argOfPericenter + ascendingNode );
231+ return std::make_unique<celestia::ephem::EllipticalOrbit>(elements, epoch );
213232 }
214233
215- // If we read the semi-major axis, use it to compute the pericenter
216- // distance.
217- if (semiMajorAxis.has_value ())
218- pericenterDistance = *semiMajorAxis * (1.0 - eccentricity);
219-
220- return std::make_unique<celestia::ephem::EllipticalOrbit>(pericenterDistance,
221- eccentricity,
222- degToRad (inclination),
223- degToRad (ascendingNode),
224- degToRad (argOfPericenter),
225- degToRad (anomalyAtEpoch),
226- period,
227- epoch);
234+ return std::make_unique<celestia::ephem::HyperbolicOrbit>(elements, epoch);
228235}
229236
230237
@@ -310,7 +317,7 @@ CreateFixedPosition(const Hash* trajData, const Selection& centralObject, bool u
310317 {
311318 if (centralObject.getType () != SelectionType::Body)
312319 {
313- GetLogger ()->error (" FixedPosition planetographic coordinates aren't valid for stars.\n " );
320+ GetLogger ()->error (" FixedPosition planetographic coordinates are not valid for stars.\n " );
314321 return nullptr ;
315322 }
316323
@@ -770,7 +777,7 @@ CreateOrbit(const Selection& centralObject,
770777 return nullptr ;
771778 }
772779
773- return CreateEllipticalOrbit (orbitData, usePlanetUnits).release ();
780+ return CreateKeplerianOrbit (orbitData, usePlanetUnits).release ();
774781 }
775782
776783 // Create an 'orbit' that places the object at a fixed point in its
0 commit comments