/** * Construct a planet_tle from two strings containing the two line elements * \param[in] line1 first line * \param[in] line2 second line */ tle::tle(const std::string& line1, const std::string& line2) try : base(), m_line1(line1), m_line2(line2), m_tle(Tle("TLE satellite", line1, line2)), m_sgp4_propagator(SGP4(m_tle)) { // We read the osculating elements of the satellite array6D keplerian_elements; double mu_central_body = kMU*1E09; // (m^3/s^2) double mean_motion = m_tle.MeanMotion() * 2 * kPI / kSECONDS_PER_DAY; // [rad/s] keplerian_elements[0] = std::pow(mu_central_body / (mean_motion*mean_motion),1./3); // a [m] keplerian_elements[1] = m_tle.Eccentricity(); // e keplerian_elements[2] = m_tle.Inclination(false); // i [rad] keplerian_elements[3] = m_tle.RightAscendingNode(false); // Om [rad] keplerian_elements[4] = m_tle.ArgumentPerigee(false); // om [rad] keplerian_elements[5] = m_tle.MeanAnomaly(false); // M [rad] std::string year_str = m_tle.IntDesignator().substr(0,2); int year_int = std::stoi(year_str); std::string rest = m_tle.IntDesignator().substr(2); int prefix = (year_int > 50)?(19):(20); std::string object_name(std::to_string(prefix)+year_str+std::string("-")+rest); set_mu_central_body(mu_central_body); set_name(object_name); m_ref_mjd2000 = epoch(m_tle.Epoch().ToJulian(),epoch::JD).mjd2000(); } catch (TleException& e) { std::cout << "TleException cought in planet_tle constructor" << std::endl; throw_value_error(e.what()); } catch (SatelliteException& e) { std::cout << "SatelliteException cought in planet_tle constructor" << std::endl; throw_value_error(e.what()); }
void TleGen( Vector6 randKepElem, std::string& SolverStatus, int& IterationCount ) { // grav. parameter 'mu' of earth const double muEarth = kMU*( pow( 10, 9 ) ); // unit m^3/s^2 // generate sets of cartesian elements corresponding to each of the psuedo random orbital element set. // The conversion from keplerian elements to the cartesian elements is done using the PyKep library from ESA. Vector3 CartPos( 3 ); // empty vector to store position coordinates Vector3 CartVel( 3 ); // empty vector to store velocity components // Real rangeMag = 0; // magnitude range // Real velocityMag = 0; // velocity magnitude kep_toolbox::par2ic( randKepElem, muEarth, CartPos, CartVel ); // verification of the conversion process using Ron Noomen's lecture notes from TUDelft // Document ID ae4878.basics.v4-16.pdf // Vector6 testKep = { 6787746.891, 0.000731104, sml::convertDegreesToRadians( 51.68714486 ), // sml::convertDegreesToRadians( 127.5486706 ), sml::convertDegreesToRadians( 74.21987137 ), sml::convertDegreesToRadians( 24.08317766 ) }; // Vector3 testPos( 3 ); // Vector3 testVel( 3 ); // kep_toolbox::par2ic( testKep, muEarth, testPos, testVel ); // std::cout << testPos[ 0 ] << std::endl; // std::cout << testPos[ 1 ] << std::endl; // std::cout << testPos[ 2 ] << std::endl; // std::cout << testVel[ 0 ] << std::endl; // std::cout << testVel[ 1 ] << std::endl; // std::cout << testVel[ 2 ] << std::endl; // convert the cartesian elements to the corresponding TLE format using the ATOM toolbox Vector6 cartesianState( 6 ); // cartesianState[ 0 ] = -7.1e3; // cartesianState[ 1 ] = 2.7e3; // cartesianState[ 2 ] = 1.3e3; // cartesianState[ 3 ] = -2.5; // cartesianState[ 4 ] = -5.5; // cartesianState[ 5 ] = 5.5; Tle convertedTle; Tle referenceTle = Tle(); // empty TLE for reference // std::cout << referenceTle << std::endl << std::endl; const Real absTol = 1.0e-10; // absolute tolerance const Real relTol = 1.0e-5; // relative tolerance const int maxItr = 100; // maximum allowed iterations per conversion run // important note, the atom function converting cartesian to TLEs takes in values in km and km/s. cartesianState[ 0 ] = CartPos[ 0 ]/1000; cartesianState[ 1 ] = CartPos[ 1 ]/1000; cartesianState[ 2 ] = CartPos[ 2 ]/1000; cartesianState[ 3 ] = CartVel[ 0 ]/1000; cartesianState[ 4 ] = CartVel[ 1 ]/1000; cartesianState[ 5 ] = CartVel[ 2 ]/1000; convertedTle = atom::convertCartesianStateToTwoLineElements< Real, Vector6 >( cartesianState, DateTime( ), SolverStatus, IterationCount, referenceTle, kMU, kXKMPER, absTol, relTol, maxItr ); }
namespace atom { const static int meanMotionIndex = astro::semiMajorAxisIndex; const static int meanAnomalyIndex = astro::trueAnomalyIndex; //! Convert Cartesian state to TLE (Two Line Elements). /*! * Converts a given Cartesian state (position, velocity) to an equivalent TLE. * * This function makes use of a root-finder to solve a non-linear system. Locating the root of the * non-linear system corresponds finding the TLE that, when evaluated at its epoch using the * SGP4/SDP4 propagator (Vallado, 2012), yields the target Cartesian state (within tolerance). * * Details of the underlying non-linear system and algorithm are catalogued by * Kumar, et al. (2014). * * @sa evaluateCartesianToTwoLineElementsSystem, DateTime * @tparam Real Type for reals * @tparam Vector6 Type for 6-vector of reals * @param cartesianState Cartesian state [km; km/s] * @param epoch Epoch associated with Cartesian state, stored in a * DateTime object * @param solverStatusSummary Status of non-linear solver printed as a table * @param numberOfIterations Number of iterations completed by solver * @param referenceTle Reference Two Line Elements. This is used as reference to * construct the effective TLE for the given Cartesian state * [default: 0-TLE]. * @param earthGravitationalParameter Earth gravitational parameter [km^3 s^-2] [default: mu_SGP] * @param earthMeanRadius Earth mean radius [km] [default: R_SGP] * @param absoluteTolerance Absolute tolerance used to check if root-finder has * converged [default: 1.0e-10] (see Kumar, et al. (2014) for * details on how convergence is tested) * @param relativeTolerance Relative tolerance used to check if root-finder has * converged [default: 1.0e-5] (see Kumar, et al. (2014) for * details on how convergence is tested) * @param maximumIterations Maximum number of solver iterations permitted. Once the * solver reaches this limit, the loop will be broken and the * solver status will report that it has not converged * [default: 100]. * @return TLE object that generates target Cartesian state when * propagated with SGP4 propagator to target epoch */ template< typename Real, typename Vector6 > const Tle convertCartesianStateToTwoLineElements( const Vector6& cartesianState, const DateTime& epoch, std::string& solverStatusSummary, int& numberOfIterations, const Tle& referenceTle = Tle( ), const Real earthGravitationalParameter = kMU, const Real earthMeanRadius = kXKMPER, const Real absoluteTolerance = 1.0e-10, const Real relativeTolerance = 1.0e-8, const int maximumIterations = 100 ); //! Convert Cartesian state to TLE (Two Line Elements). /*! * Converts a given Cartesian state (position, velocity) to an equivalent TLE. * * This function makes use of a root-finder to solve a non-linear system. Locating the root of the * non-linear system corresponds finding the TLE that, when evaluated at its epoch using the * SGP4/SDP4 propagator (Vallado, 2012), yields the target Cartesian state (within tolerance). * * Details of the underlying non-linear system and algorithm are catalogued by * Kumar, et al. (2014). * * This is a function overload to ensure that the user can opt to leave out solver summary status * string and number of iterations counter from the call (overload is necessary since non-const * references cannot be assigned default values in C++). * * @sa convertCartesianStateToTwoLineElements, evaluateCartesianToTwoLineElementsSystem, * DateTime * @tparam Real Type for reals * @tparam Vector6 Type for 6-vector of reals * @param cartesianState Cartesian state [km; km/s] * @param epoch Epoch associated with Cartesian state, stored in a * DateTime object * @return TLE object that generates target Cartesian state when * propagated with SGP4 propagator to target epoch */ template< typename Real, typename Vector6 > const Tle convertCartesianStateToTwoLineElements( const Vector6& cartesianState, const DateTime& epoch ); //! Compute residuals for converting Cartesian state to TLE. /*! * Evaluates system of non-linear equations and computes residuals to find TLE corresponding with * target Cartesian state. The residual function, \f$\bar{R}\f$ is computed as follows: * \f[ * \bar{R} = 0 = \begin{pmatrix} * \frac{\bar{r}_{new} - \bar{r}_{target}}{R_{Earth}}\\ * \frac{\bar{v}_{new} - \bar{v}_{target}}{V_{c,Earth}}\\ * \end{pmatrix} * \f] * where \f$\bar{r}_{new}\f$ and \f$\bar{v}_{new}\f$ are the new Cartesian position and velocity * vectors, computed by updating the TLE mean elements and propagating the TLE using the SGP4 * propagator, \f$\bar{r}_{target}\f$ anf \f$\bar{v}_{target}\f$ are the target Cartesian position * and velocity vectors, \f$R_{Earth}\f$ is the mean radius of the Earth, and \f$V_{c,Earth}\f$ is * the circular velocity at \f$R_{Earth}\f$. Note that the residuals are non-dimensional. They are * used to drive a root-finding process that uses the GSL library. * * @sa convertCartesianStateToTwoLineElements * @tparam Real Type for reals * @tparam Vector6 Type for 6-vector of reals * @param independentVariables Vector of independent variables used by the root-finder * @param parameters Parameters required to compute the objective function * @param residuals Vector of computed residuals * @return GSL flag indicating success or failure */ template< typename Real, typename Vector6 > int computeCartesianToTwoLineElementResiduals( const gsl_vector* independentVariables, void* parameters, gsl_vector* residuals ); //! Compute initial guess for TLE mean elements. /* * Computes initial guess for TLE mean elements by converting Keplerian elements provided by user to * TLE mean elements. The TLE mean elements generated are to floating-point precision, since the * TLE class stores the internal values as doubles. * * @sa Tle * @tparam Real Type for reals * @tparam Vector6 Type for 6-vector of reals * @param keplerianElements Keplerian elements to be used as initial guess * The order of Keplerian elements in the vector is: * - semi-major axis [km] * - eccentricity [-] * - inclination [rad] * - argument of periapsis [rad] * - right ascension of ascending node [rad] * - true anomaly [rad] * @param earthGravitationalParameter Earth gravitational parameter [km^3 s^-2] * @return 6-vector containing initial guess for TLE mean * elements * The order of mean TLE elements in the vector is: * - mean inclination [deg] * - mean right ascension of ascending node [deg] * - mean eccentricity [-] * - mean argument of perigee [deg] * - mean mean anomaly [deg] * - mean mean motion [rev/day] */ template< typename Real, typename Vector6 > const Vector6 computeInitialGuessTleMeanElements( const Vector6& keplerianElements, const Real earthGravitationalParameter ); //! Parameter struct used by Cartesian-to-TLE residual function. /*! * Data structure with parameters used to compute Cartesian-to-TLE residual function. * * @sa computeCartesianToTwoLineElementResiduals * @tparam Vector6 Type for 6-vector of reals */ template< typename Vector6 > struct CartesianToTwoLineElementsParameters; //! Convert Cartesian state to TLE (Two Line Elements). template< typename Real, typename Vector6 > const Tle convertCartesianStateToTwoLineElements( const Vector6& cartesianState, const DateTime& epoch, std::string& solverStatusSummary, int& numberOfIterations, const Tle& referenceTle, const Real earthGravitationalParameter, const Real earthMeanRadius, const Real absoluteTolerance, const Real relativeTolerance, const int maximumIterations ) { // Store reference TLE as the template TLE and update epoch. Tle templateTle = referenceTle; templateTle.updateEpoch( epoch ); // Set up parameters for residual function. CartesianToTwoLineElementsParameters< Vector6 > parameters( cartesianState, templateTle ); // Set up residual function. gsl_multiroot_function cartesianToTwoLineElementsFunction = { &computeCartesianToTwoLineElementResiduals< Real, Vector6 >, 6, ¶meters }; // Compute current state in Keplerian elements, for use as initial guess for the TLE mean // elements. const Vector6 initialKeplerianElements = astro::convertCartesianToKeplerianElements( parameters.targetState, earthGravitationalParameter ); // Compute initial guess for TLE mean elements. const Vector6 initialTleMeanElements = computeInitialGuessTleMeanElements( initialKeplerianElements, earthGravitationalParameter ); // Set initial guess. gsl_vector* initialGuessTleMeanElements = gsl_vector_alloc( 6 ); for ( int i = 0; i < 6; i++ ) { gsl_vector_set( initialGuessTleMeanElements, i, initialTleMeanElements[ i ] ); } // Set up solver type (derivative free). const gsl_multiroot_fsolver_type* solverType = gsl_multiroot_fsolver_hybrids; // Allocate memory for solver. gsl_multiroot_fsolver* solver = gsl_multiroot_fsolver_alloc( solverType, 6 ); // Set solver to use residual function with initial guess for TLE mean elements. gsl_multiroot_fsolver_set( solver, &cartesianToTwoLineElementsFunction, initialGuessTleMeanElements ); // Declare current solver status and iteration counter. int solverStatus = false; int counter = 0; // Set up buffer to store solver status summary table. std::ostringstream summary; // Print header for summary table to buffer. summary << printCartesianToTleSolverStateTableHeader( ); do { // Print current state of solver for summary table. summary << printCartesianToTleSolverState( counter, solver ); // Increment iteration counter. ++counter; // Execute solver iteration. solverStatus = gsl_multiroot_fsolver_iterate( solver ); // Check if solver is stuck; if it is stuck, break from loop. if ( solverStatus ) { solverStatusSummary = summary.str( ); throw std::runtime_error( "ERROR: Non-linear solver is stuck!" ); } // Check if root has been found (within tolerance). solverStatus = gsl_multiroot_test_delta( solver->dx, solver->x, absoluteTolerance, relativeTolerance ); } while ( solverStatus == GSL_CONTINUE && counter < maximumIterations ); // Save number of iterations. numberOfIterations = counter - 1; // Print final status of solver to buffer. summary << std::endl; summary << "Status of non-linear solver: " << gsl_strerror( solverStatus ) << std::endl; summary << std::endl; // Write buffer contents to solver status summary string. solverStatusSummary = summary.str( ); // Generate TLE with converged mean elements. Tle virtualTle = templateTle; Real convergedMeanEccentricity = gsl_vector_get( solver->x, 2 ); if ( convergedMeanEccentricity < 0.0 ) { convergedMeanEccentricity = std::fabs( gsl_vector_get( solver->x, 2 ) ); } if ( convergedMeanEccentricity > 0.999 ) { convergedMeanEccentricity = 0.99; } virtualTle.updateMeanElements( sml::computeModulo( std::fabs( gsl_vector_get( solver->x, 0 ) ), 180.0 ), sml::computeModulo( gsl_vector_get( solver->x, 1 ), 360.0 ), convergedMeanEccentricity, sml::computeModulo( gsl_vector_get( solver->x, 3 ), 360.0 ), sml::computeModulo( gsl_vector_get( solver->x, 4 ), 360.0 ), std::fabs( gsl_vector_get( solver->x, 5 ) ) ); // Free up memory. gsl_multiroot_fsolver_free( solver ); gsl_vector_free( initialGuessTleMeanElements ); return virtualTle; } //! Convert Cartesian state to TLE (Two Line Elements). template< typename Real, typename Vector6 > const Tle convertCartesianStateToTwoLineElements( const Vector6& cartesianState, const DateTime& epoch ) { std::string dummyString = ""; int dummyint = 0; return convertCartesianStateToTwoLineElements< Real >( cartesianState, epoch, dummyString, dummyint ); } //! Compute residuals for converting Cartesian state to TLE. template< typename Real, typename Vector6 > int computeCartesianToTwoLineElementResiduals( const gsl_vector* independentVariables, void* parameters, gsl_vector* residuals ) { const Vector6 targetState = static_cast< CartesianToTwoLineElementsParameters< Vector6 >* >( parameters )->targetState; const Tle templateTle = static_cast< CartesianToTwoLineElementsParameters< Vector6 >* >( parameters )->templateTle; // Create a TLE object with the mean TLE elements generated by the root-finder. Tle tle = templateTle; Real meanInclination = sml::computeModulo( std::fabs( gsl_vector_get( independentVariables, 0 ) ), 180.0 ); Real meanRightAscendingNode = sml::computeModulo( gsl_vector_get( independentVariables, 1 ), 360.0 ); Real meanEccentricity = gsl_vector_get( independentVariables, 2 ); if ( meanEccentricity < 0.0 ) { meanEccentricity = std::fabs( gsl_vector_get( independentVariables, 2 ) ); } if ( meanEccentricity > 0.999 ) { meanEccentricity = 0.99; } Real meanArgumentPerigee = sml::computeModulo( gsl_vector_get( independentVariables, 3 ), 360.0 ); Real meanMeanAnomaly = sml::computeModulo( gsl_vector_get( independentVariables, 4 ), 360.0 ); Real meanMeanMotion = std::fabs( gsl_vector_get( independentVariables, 5 ) ); tle.updateMeanElements( meanInclination, meanRightAscendingNode, meanEccentricity, meanArgumentPerigee, meanMeanAnomaly, meanMeanMotion ); // Propagate the TLE object to the specified epoch using the SGP4 propagator. SGP4 sgp4( tle ); Eci cartesianState = sgp4.FindPosition( 0.0 ); // Compute residuals by computing the difference between the Cartesian state generated by the // SGP4 propagator and the target Cartesian state. gsl_vector_set( residuals, astro::xPositionIndex, cartesianState.Position( ).x - targetState[ astro::xPositionIndex ] ); gsl_vector_set( residuals, astro::yPositionIndex, cartesianState.Position( ).y - targetState[ astro::yPositionIndex ] ); gsl_vector_set( residuals, astro::zPositionIndex, cartesianState.Position( ).z - targetState[ astro::zPositionIndex ] ); gsl_vector_set( residuals, astro::xVelocityIndex, cartesianState.Velocity( ).x - targetState[ astro::xVelocityIndex ] ); gsl_vector_set( residuals, astro::yVelocityIndex, cartesianState.Velocity( ).y - targetState[ astro::yVelocityIndex ] ); gsl_vector_set( residuals, astro::zVelocityIndex, cartesianState.Velocity( ).z - targetState[ astro::zVelocityIndex ] ); return GSL_SUCCESS; } //! Compute initial guess for TLE mean elements. template< typename Real, typename Vector6 > const Vector6 computeInitialGuessTleMeanElements( const Vector6& keplerianElements, const Real earthGravitationalParameter ) { Vector6 tleMeanElements = keplerianElements; // Compute mean inclination [deg]. tleMeanElements[ 0 ] = sml::computeModulo( sml::convertRadiansToDegrees( keplerianElements[ astro::inclinationIndex ] ), 180.0 ); // Compute mean right ascending node [deg]. tleMeanElements[ 1 ] = sml::computeModulo( sml::convertRadiansToDegrees( keplerianElements[ astro::longitudeOfAscendingNodeIndex ] ), 360.0 ); // Compute mean eccentricity [-]. tleMeanElements[ 2 ] = keplerianElements[ astro::eccentricityIndex ]; // Compute mean argument of perigee [deg]. tleMeanElements[ 3 ] = sml::computeModulo( sml::convertRadiansToDegrees( keplerianElements[ astro::argumentOfPeriapsisIndex ] ), 360.0 ); // Compute mean eccentric anomaly [rad]. const Real eccentricAnomaly = astro::convertTrueAnomalyToEccentricAnomaly( keplerianElements[ astro::trueAnomalyIndex ], keplerianElements[ astro::eccentricityIndex ] ); // Compute mean mean anomaly [deg]. tleMeanElements[ 4 ] = sml::computeModulo( sml::convertRadiansToDegrees( astro::convertEccentricAnomalyToMeanAnomaly( eccentricAnomaly, keplerianElements[ astro::eccentricityIndex ] ) ), 360.0 ); // Compute new mean motion [rev/day]. tleMeanElements[ 5 ] = astro::computeKeplerMeanMotion( keplerianElements[ astro::semiMajorAxisIndex ], earthGravitationalParameter ) / ( 2.0 * sml::SML_PI ) * astro::ASTRO_JULIAN_DAY_IN_SECONDS; return tleMeanElements; } //! Parameter struct used by Cartesian-to-TLE residual function. template< typename Vector6 > struct CartesianToTwoLineElementsParameters { public: //! Constructor taking parameter values. /*! * Default constructor, taking parameters for Cartesian-to-Two-Line-Elements conversion. * @sa convertCartesianStateToTwoLineElements, computeCartesianToTwoLineElementResiduals * * @tparam Vector6 Vector of length 6 * @param aTargetState Target Cartesian state [km; km/s] * @param aTemplateTle Template for Two-Line-Elements (TLE) with pre-filled * values */ CartesianToTwoLineElementsParameters( const Vector6& aTargetState, const Tle& aTemplateTle ) : targetState( aTargetState ), templateTle( aTemplateTle ) { } //! Target state in Cartesian elements [km; km/s]. const Vector6 targetState; //! Template TLE that contains pre-filled values (e.g., epoch, Bstar). const Tle templateTle; protected: private: }; } // namespace atom
namespace atom { //! Execute Atom solver. /*! * Executes Atom solver to find the transfer orbit connecting two positions. The epoch of the * departure position and the Time-of-flight need to be specified. * * The Atom solver is an analog of the Lambert solver (Lancaster and Blanchard, 1969; * Gooding, 1990; Izzo, 2014), that aims to find the conic section that bridges two positions, at * given epochs, by using impulsive manveuvers (Delta-V maneuvers) at departure and arrival. The * Atom solver aims to solver a similar orbital transfer, subject to perturbations. The * perturbations taken into account are those encoded in the SGP4/SDP4 propagators (Vallado, 2006). * * Since the Atom solver makes use fo the SGP4/SDP4 propagators, it can currently only solve for * perturbed transfers around the Earth. As a result, the Earth's gravitational parameter is fixed, * as specified by the SGP4/SDP4 propagators (Vallado, 2006). * * Details of the underlying non-linear system and algorithm are catalogued by * Kumar, et al. (2014). * * @sa convertCartesianStateToTwoLineElements * @tparam Real Type for reals * @tparam Vector3 Type for 3-vector of reals * @param departurePosition Cartesian position vector at departure [km] * @param departureEpoch Modified Julian Date (MJD) of departure * @param arrivalPosition Cartesian position vector at arrival [km] * @param timeOfFlight Time-of-Flight for orbital transfer [s] * @param departureVelocityGuess Initial guess for the departure velocity (serves as initial * guess for the internal root-finding procedure) [km/s] * @param solverStatusSummary Status of non-linear solver printed as a table * @param numberOfIterations Number of iterations completed by solver * @param referenceTle Reference Two Line Elements [default: 0-TLE] * @param earthGravitationalParameter Earth gravitational parameter [km^3 s^-2] [default: mu_SGP] * @param earthMeanRadius Earth mean radius [km] [default: R_SGP] * @param absoluteTolerance Absolute tolerance used to check if root-finder has * converged [default: 1.0e-10] (see Kumar, et al. (2014) for * details on how convergence is tested) * @param relativeTolerance Relative tolerance used to check if root-finder has * converged [default: 1.0e-5] (see Kumar, et al. (2014) for * details on how convergence is tested) * @param maximumIterations Maximum number of solver iterations permitted. Once the * solver reaches this limit, the loop will be broken and the * solver status will report that it has not converged * [default: 100]. * @return Departure and arrival velocities (stored in that order) */ template< typename Real, typename Vector3 > const std::pair< Vector3, Vector3 > executeAtomSolver( const Vector3& departurePosition, const DateTime& departureEpoch, const Vector3& arrivalPosition, const Real timeOfFlight, const Vector3& departureVelocityGuess, std::string& solverStatusSummary, int& numberOfIterations, const Tle& referenceTle = Tle( ), const Real earthGravitationalParameter = kMU, const Real earthMeanRadius = kXKMPER, const Real absoluteTolerance = 1.0e-10, const Real relativeTolerance = 1.0e-5, const int maximumIterations = 100 ); //! Execute Atom solver. /*! * Executes Atom solver to find the transfer orbit connecting two positions. The epoch of the * departure position and the Time-of-flight need to be specified. * * The Atom solver is an analog of the Lambert solver (Lancaster and Blanchard, 1969; * Gooding, 1990; Izzo, 2014), that aims to find the conic section that bridges two positions, at * given epochs, by using impulsive manveuvers (Delta-V maneuvers) at departure and arrival. The * Atom solver aims to solver a similar orbital transfer, subject to perturbations. The * perturbations taken into account are those encoded in the SGP4/SDP4 propagators (Vallado, 2006). * * Since the Atom solver makes use fo the SGP4/SDP4 propagators, it can currently only solve for * perturbed transfers around the Earth. As a result, the Earth's gravitational parameter is fixed, * as specified by the SGP4/SDP4 propagators (Vallado, 2006). * * Details of the underlying non-linear system and algorithm are catalogued by * Kumar, et al. (2014). * * This is a function overload to ensure that the user can opt to leave out solver summary status * string and number of iterations counter from the call (overload is necessary since non-const * references cannot be assigned default values in C++). * * @sa convertCartesianStateToTwoLineElements * @tparam Real Type for reals * @tparam Vector3 Type for 3-vector of reals * @param departurePosition Cartesian position vector at departure [km] * @param departureEpoch Modified Julian Date (MJD) of departure * @param arrivalPosition Cartesian position vector at arrival [km] * @param timeOfFlight Time-of-Flight for orbital transfer [s] * @param departureVelocityGuess Initial guess for the departure velocity (serves as initial * guess for the internal root-finding procedure) [km/s] * @return Departure and arrival velocities (stored in that order) */ template< typename Real, typename Vector3 > const std::pair< Vector3, Vector3 > executeAtomSolver( const Vector3& departurePosition, const DateTime& departureEpoch, const Vector3& arrivalPosition, const Real timeOfFlight, const Vector3& departureVelocityGuess ); //! Compute residuals to execute Atom solver. /*! * Evaluates system of non-linear equations and computes residuals to execute the Atom solver. The * residual function, \f$\bar{R}\f$ is computed as follows: * The system of non-linear equations used is: * \f[ * \bar{R} = 0 = \frac{\bar{r}_{new} - \bar{r}_{target}}{R_{Earth}} * \f] * where \f$\bar{r}_{new}\f$ is the Cartesian position computed by propagating the * initial, prescribed state under the action of an initial impulsive Delta V, by a prescribed * time-of-flight, \f$\bar{r}_{target}\f$ is the target Cartesian position and * \f$R_{Earth}\f$ is the mean radius of the Earth. Note that the residuals are non-dimensional. * They are used to drive a root-finding process that uses the GSL library. * * @sa executeAtomSolver * @tparam Real Type for reals * @tparam Vector3 Type for 3-vector of reals * @param independentVariables Vector of independent variables used by the root-finder * @param parameters Parameters required to compute the objective function * @param residuals Vector of computed residuals * @return GSL flag indicating success or failure */ template< typename Real, typename Vector3 > int computeAtomResiduals( const gsl_vector* independentVariables, void* parameters, gsl_vector* residuals ); //! Parameter struct used by Atom residual function. /*! * Data structure with parameters used to compute Atom residual function. * * @sa computeAtomResiduals * @tparam Real Type for reals * @tparam Vector3 Type for 3-vector of reals */ template< typename Real, typename Vector3 > struct AtomParameters; //! Execute Atom solver. template< typename Real, typename Vector3 > const std::pair< Vector3, Vector3 > executeAtomSolver( const Vector3& departurePosition, const DateTime& departureEpoch, const Vector3& arrivalPosition, const Real timeOfFlight, const Vector3& departureVelocityGuess, std::string& solverStatusSummary, int& numberOfIterations, const Tle& referenceTle, const Real earthGravitationalParameter, const Real earthMeanRadius, const Real absoluteTolerance, const Real relativeTolerance, const int maximumIterations ) { // Set up parameters for residual function. AtomParameters< Real, Vector3 > parameters( departurePosition, departureEpoch, arrivalPosition, timeOfFlight, earthGravitationalParameter, earthMeanRadius, referenceTle, absoluteTolerance, relativeTolerance, maximumIterations ); // Set up residual function. gsl_multiroot_function atomFunction = { &computeAtomResiduals< Real, Vector3 >, 3, ¶meters }; // Set initial guess. gsl_vector* initialGuess = gsl_vector_alloc( 3 ); for ( int i = 0; i < 3; i++ ) { gsl_vector_set( initialGuess, i, departureVelocityGuess[ i ] ); } // Set up solver type (derivative free). const gsl_multiroot_fsolver_type* solverType = gsl_multiroot_fsolver_hybrids; // Allocate memory for solver. gsl_multiroot_fsolver* solver = gsl_multiroot_fsolver_alloc( solverType, 3 ); // Set solver to use residual function with initial guess. gsl_multiroot_fsolver_set( solver, &atomFunction, initialGuess ); // Declare current solver status and iteration counter. int solverStatus = false; int counter = 0; // Set up buffer to store solver status summary table. std::ostringstream summary; // Print header for summary table to buffer. summary << printAtomSolverStateTableHeader( ); do { // Print current state of solver for summary table. summary << printAtomSolverState( counter, solver ); // Increment iteration counter. ++counter; // Execute solver iteration. solverStatus = gsl_multiroot_fsolver_iterate( solver ); // Check if solver is stuck; if it is stuck, break from loop. if ( solverStatus ) { std::cerr << "GSL solver status: " << solverStatus << std::endl; std::cerr << summary.str( ) << std::endl; std::cerr << std::endl; throw std::runtime_error( "ERROR: Non-linear solver is stuck!" ); } // Check if root has been found (within tolerance). solverStatus = gsl_multiroot_test_delta( solver->dx, solver->x, absoluteTolerance, relativeTolerance ); } while ( solverStatus == GSL_CONTINUE && counter < maximumIterations ); // Save number of iterations. numberOfIterations = counter - 1; // Print final status of solver to buffer. summary << std::endl; summary << "Status of non-linear solver: " << gsl_strerror( solverStatus ) << std::endl; summary << std::endl; // Write buffer contents to solver status summary string. solverStatusSummary = summary.str( ); // Store final departure velocity. Vector3 departureVelocity = departureVelocityGuess; for ( int i = 0; i < 3; i++ ) { departureVelocity[ i ] = gsl_vector_get( solver->x, i ); } // Set departure state [km/s]. std::vector< Real > departureState( 6 ); for ( int i = 0; i < 3; i++ ) { departureState[ i ] = departurePosition[ i ]; } for ( int i = 0; i < 3; i++ ) { departureState[ i + 3 ] = departureVelocity[ i ]; } // Convert departure state to TLE. std::string dummyString = ""; int dummyint = 0; const Tle departureTle = convertCartesianStateToTwoLineElements< Real >( departureState, departureEpoch, dummyString, dummyint, referenceTle, earthGravitationalParameter, earthMeanRadius, absoluteTolerance, relativeTolerance, maximumIterations ); // Propagate departure TLE by time-of-flight using SGP4 propagator. SGP4 sgp4( departureTle ); DateTime arrivalEpoch = departureEpoch.AddSeconds( timeOfFlight ); Eci arrivalState = sgp4.FindPosition( arrivalEpoch ); Vector3 arrivalVelocity = departureVelocity; arrivalVelocity[ 0 ] = arrivalState.Velocity( ).x; arrivalVelocity[ 1 ] = arrivalState.Velocity( ).y; arrivalVelocity[ 2 ] = arrivalState.Velocity( ).z; // Free up memory. gsl_multiroot_fsolver_free( solver ); gsl_vector_free( initialGuess ); // Return departure and arrival velocities. return std::make_pair< Vector3, Vector3 >( departureVelocity, arrivalVelocity ); } //! Execute Atom solver. template< typename Real, typename Vector3 > const std::pair< Vector3, Vector3 > executeAtomSolver( const Vector3& departurePosition, const DateTime& departureEpoch, const Vector3& arrivalPosition, const Real timeOfFlight, const Vector3& departureVelocityGuess ) { std::string dummyString = ""; int dummyint = 0; return executeAtomSolver( departurePosition, departureEpoch, arrivalPosition, timeOfFlight, departureVelocityGuess, dummyString, dummyint ); } //! Compute residuals to execute Atom solver. template< typename Real, typename Vector3 > int computeAtomResiduals( const gsl_vector* independentVariables, void* parameters, gsl_vector* residuals ) { // Store parameters locally. const Vector3 departurePosition = static_cast< AtomParameters< Real, Vector3 >* >( parameters )->departurePosition; const DateTime departureEpoch = static_cast< AtomParameters< Real, Vector3 >* >( parameters )->departureEpoch; const Vector3 targetPosition = static_cast< AtomParameters< Real, Vector3 >* >( parameters )->targetPosition; const Real timeOfFlight = static_cast< AtomParameters< Real, Vector3 >* >( parameters )->timeOfFlight; const Real earthGravitationalParameter = static_cast< AtomParameters< Real, Vector3 >* >( parameters )->earthGravitationalParameter; const Real earthMeanRadius = static_cast< AtomParameters< Real, Vector3 >* >( parameters )->earthMeanRadius; const Tle referenceTle = static_cast< AtomParameters< Real, Vector3 >* >( parameters )->referenceTle; const Real absoluteTolerance = static_cast< AtomParameters< Real, Vector3 >* >( parameters )->absoluteTolerance; const Real relativeTolerance = static_cast< AtomParameters< Real, Vector3 >* >( parameters )->relativeTolerance; const int maximumIterations = static_cast< AtomParameters< Real, Vector3 >* >( parameters )->maximumIterations; // Set Departure state [km; km/s]. std::vector< Real > departureVelocity( 3 ); for ( int i = 0; i < 3; i++ ) { departureVelocity[ i ] = gsl_vector_get( independentVariables, i ); } std::vector< Real > departureState( 6 ); for ( int i = 0; i < 3; i++ ) { departureState[ i ] = departurePosition[ i ]; } for ( int i = 0; i < 3; i++ ) { departureState[ i + 3 ] = departureVelocity[ i ]; } // Convert departure state to TLE. std::string dummyString = ""; int dummyint = 0; const Tle departureTle = convertCartesianStateToTwoLineElements( departureState, departureEpoch, dummyString, dummyint, referenceTle, earthGravitationalParameter, earthMeanRadius, absoluteTolerance, relativeTolerance, maximumIterations ); // Propagate departure TLE by time-of-flight using SGP4 propagator. SGP4 sgp4( departureTle ); DateTime arrivalEpoch = departureEpoch.AddSeconds( timeOfFlight ); Eci arrivalState = sgp4.FindPosition( arrivalEpoch ); // Evaluate system of non-linear equations and store residuals. gsl_vector_set( residuals, 0, ( arrivalState.Position( ).x - targetPosition[ 0 ] ) / earthMeanRadius ); gsl_vector_set( residuals, 1, ( arrivalState.Position( ).y - targetPosition[ 1 ] ) / earthMeanRadius ); gsl_vector_set( residuals, 2, ( arrivalState.Position( ).z - targetPosition[ 2 ] ) / earthMeanRadius ); return GSL_SUCCESS; } //! Parameter struct used by Atom residual function. template< typename Real, typename Vector3 > struct AtomParameters { public: //! Constructor taking parameter values. /*! * Default constructor, taking parameters to execute Atom solver. * @sa executeAtomSolver, computeCartesianToTwoLineElementResiduals * @param aDeparturePosition Cartesian departure position [km] * @param aDepartureEpoch Modified Julian Date (MJD) of departure * @param aTargetPosition Target Cartesian position [km] * @param aTimeOfFlight Time-of-Flight (TOF) [s] * @param anEarthGravitationalParameter Earth gravitational parameter [km^3 s^-2] * @param anEarthMeanRadius Earth mean radius [km] * @param aReferenceTle Reference Two-Line-Elements * @param anAbsoluteTolerance Absolute tolerance used to check for convergence * @param aRelativeTolerance Relative tolerance used to check for convergence * @param someMaximumIterations Maximum number of solver iterations permitted */ AtomParameters( const Vector3& aDeparturePosition, const DateTime& aDepartureEpoch, const Vector3& aTargetPosition, const Real aTimeOfFlight, const Real anEarthGravitationalParameter, const Real anEarthMeanRadius, const Tle& aReferenceTle, const Real anAbsoluteTolerance, const Real aRelativeTolerance, const int someMaximumIterations ) : departurePosition( aDeparturePosition ), departureEpoch( aDepartureEpoch ), targetPosition( aTargetPosition ), timeOfFlight( aTimeOfFlight ), earthGravitationalParameter( anEarthGravitationalParameter ), earthMeanRadius( anEarthMeanRadius ), referenceTle( aReferenceTle ), absoluteTolerance( anAbsoluteTolerance ), relativeTolerance( aRelativeTolerance ), maximumIterations( someMaximumIterations ) { } //! Departure position in Cartesian elements [km]. const Vector3 departurePosition; //! Departure epoch in Modified Julian Date (MJD). const DateTime departureEpoch; //! Target position in Cartesian elements [km]. const Vector3 targetPosition; //! Time-of-Flight (TOF) [s]. const Real timeOfFlight; //! Earth gravitational parameter [km^3 s^-2]. const Real earthGravitationalParameter; //! Earth mean radius [km]. const Real earthMeanRadius; //! Reference TLE. const Tle referenceTle; //! Absolute tolerance [-]. const Real absoluteTolerance; //! Relative tolerance [-]. const Real relativeTolerance; //! Maximum number of iterations. const int maximumIterations; protected: private: }; } // namespace atom