text:
#ifndef TUDAT_BODY_H
#define TUDAT_BODY_H

#include <map>
#include <vector>

#include <memory>

#include <Eigen/Core>

#include "Tudat/Astrodynamics/Aerodynamics/atmosphereModel.h"
#include "Tudat/Astrodynamics/Aerodynamics/aerodynamicCoefficientInterface.h"
#include "Tudat/Astrodynamics/Aerodynamics/flightConditions.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/timeConversions.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/accelerationModel.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/bodyShapeModel.h"
#include "Tudat/Astrodynamics/Ephemerides/ephemeris.h"
#include "Tudat/Astrodynamics/Ephemerides/rotationalEphemeris.h"
#include "Tudat/Astrodynamics/Gravitation/gravityFieldModel.h"
#include "Tudat/Astrodynamics/Gravitation/gravityFieldVariations.h"
#include "Tudat/Astrodynamics/Gravitation/timeDependentSphericalHarmonicsGravityField.h"
#include "Tudat/Astrodynamics/GroundStations/groundStation.h"
#include "Tudat/Astrodynamics/ElectroMagnetism/radiationPressureInterface.h"
#include "Tudat/Astrodynamics/ReferenceFrames/dependentOrientationCalculator.h"
#include "Tudat/Basics/basicTypedefs.h"
#include "Tudat/Astrodynamics/Ephemerides/rotationalEphemeris.h"
#include "Tudat/Astrodynamics/SystemModels/vehicleSystems.h"
#include "Tudat/Mathematics/BasicMathematics/numericalDerivative.h"

namespace tudat
{

namespace simulation_setup
{

//! Base class used for the determination of the inertial state of a Body's ephemeris origin
/*!
 *  Base class used for the determination of the inertial state of a Body's ephemeris origin. This base class is used
 *  to provide an untemplated interface class through which to call the base frame state. The state may be defined
 *  in a templated manner in the derived class.
 */
class BaseStateInterface
{
public:

    //! Constructor
    /*!
     * Constructor
     * \param baseFrameId Name of frame origin for which inertial state is computed by this class
     */
    BaseStateInterface(
            const std::string baseFrameId ):
        baseFrameId_( baseFrameId ){ }

    //! Destructor
    virtual ~BaseStateInterface( ){ }

    //! Function through which the state of baseFrameId_ in the inertial frame can be determined
    /*!
     *  Function through which the state of baseFrameId_ in the inertial frame can be determined
     *  \param time Time at which state is to be computed
     *  \return Inertial state of frame origin at requested time
     */
    template< typename OutputTimeType, typename OutputStateScalarType >
    Eigen::Matrix< OutputStateScalarType, 6, 1 > getBaseFrameState(
            const OutputTimeType time );

protected:

    //! Pure virtual function through which the state of baseFrameId_ in the inertial frame can be determined
    /*!
     *  Pure virtual function through which the state of baseFrameId_ in the inertial frame can be determined
     *  (double time and double state scalar).
     *  \param time Time at which state is to be computed
     *  \return Inertial state of frame origin at requested time
     */
    virtual Eigen::Matrix< double, 6, 1 > getBaseFrameDoubleState( const double time ) = 0;

    //! Pure virtual function through which the state of baseFrameId_ in the inertial frame can be determined
    /*!
     *  Pure virtual function through which the state of baseFrameId_ in the inertial frame can be determined
     *  (double time and double long state scalar).
     *  \param time Time at which state is to be computed
     *  \return Inertial state of frame origin at requested time
     */
    virtual Eigen::Matrix< long double, 6, 1 > getBaseFrameLongDoubleState( const double time ) = 0;

    //! Pure virtual function through which the state of baseFrameId_ in the inertial frame can be determined
    /*!
     *  Pure virtual function through which the state of baseFrameId_ in the inertial frame can be determined
     *  (Time object time and double state scalar).
     *  \param time Time at which state is to be computed
     *  \return Inertial state of frame origin at requested time
     */
    virtual Eigen::Matrix< double, 6, 1 > getBaseFrameDoubleState( const Time& time ) = 0;

    //! Pure virtual function through which the state of baseFrameId_ in the inertial frame can be determined
    /*!
     *  Pure virtual function through which the state of baseFrameId_ in the inertial frame can be determined
     *  (Time object time and long double state scalar).
     *  \param time Time at which state is to be computed
     *  \return Inertial state of frame origin at requested time
     */
    virtual Eigen::Matrix< long double, 6, 1 > getBaseFrameLongDoubleState( const Time& time ) = 0;

    //! Name of frame origin for which inertial state is computed by this class
    std::string baseFrameId_;
};

//! Class used for the determination of the inertial state of a Body's ephemeris origin
template< typename TimeType, typename StateScalarType >
class BaseStateInterfaceImplementation: public BaseStateInterface
{
public:

    //! Constructor
    /*!
     * Constructor
     * \param baseFrameId Name of frame origin for which inertial state is computed by this class
     * \param stateFunction Function returning frame's inertial state as a function of time.
     * \param subtractStateFunction Boolean denoting whether to subtract or add the state function (i.e. whether to multiply
     * result of stateFunction by -1).
     */
    BaseStateInterfaceImplementation(
            const std::string baseFrameId,
            const std::function< Eigen::Matrix< StateScalarType, 6, 1 >( const TimeType ) > stateFunction,
            const bool subtractStateFunction = 0 ):
        BaseStateInterface( baseFrameId ),
        stateFunction_( stateFunction ), stateMultiplier_( ( subtractStateFunction == 0 ) ? 1.0 : -1.0 )
    { }

    //! Destructor
    ~BaseStateInterfaceImplementation( ){ }

protected:

    //! Function through which the state of baseFrameId_ in the inertial frame can be determined
    /*!
     *  Function through which the state of baseFrameId_ in the inertial frame can be determined
     *  (double time and double state scalar).
     *  \param time Time at which state is to be computed
     *  \return Inertial state of frame origin at requested time
     */
    Eigen::Matrix< double, 6, 1 > getBaseFrameDoubleState( const double time )
    {
        return static_cast< double >( stateMultiplier_ ) * std::move( stateFunction_( time ) ).template cast< double >( );
    }

    //! Function through which the state of baseFrameId_ in the inertial frame can be determined
    /*!
     *  Function through which the state of baseFrameId_ in the inertial frame can be determined
     *  (double time and double long state scalar).
     *  \param time Time at which state is to be computed
     *  \return Inertial state of frame origin at requested time
     */
    Eigen::Matrix< long double, 6, 1 > getBaseFrameLongDoubleState( const double time )
    {
        return static_cast< long double >( stateMultiplier_ ) * std::move( stateFunction_( time ) ).template cast< long double >( );
    }

    //! Function through which the state of baseFrameId_ in the inertial frame can be determined
    /*!
     *  Function through which the state of baseFrameId_ in the inertial frame can be determined
     *  (Time object time and double state scalar).
     *  \param time Time at which state is to be computed
     *  \return Inertial state of frame origin at requested time
     */
    Eigen::Matrix< double, 6, 1 > getBaseFrameDoubleState( const Time& time )
    {
        return static_cast< double >( stateMultiplier_ ) * stateFunction_( time ).template cast< double >( );
    }

    //! Function through which the state of baseFrameId_ in the inertial frame can be determined
    /*!
     *  Function through which the state of baseFrameId_ in the inertial frame can be determined
     *  (Time object time and long double state scalar).
     *  \param time Time at which state is to be computed
     *  \return Inertial state of frame origin at requested time
     */
    Eigen::Matrix< long double, 6, 1 > getBaseFrameLongDoubleState( const Time& time )
    {
        return static_cast< long double >( stateMultiplier_ ) * std::move( stateFunction_( time ) ).template cast< long double >( );
    }

private:

    //! Function returning frame's inertial state as a function of time.
    std::function< Eigen::Matrix< StateScalarType, 6, 1 >( const TimeType ) > stateFunction_;

    //! Value (1 or -1) by which to multiply the state returned by stateFunction_.
    int stateMultiplier_;
};

//! Body class representing the properties of a celestial body (natural or artificial).
/*!
 *  Body class representing the properties of a celestial body (natural or artificial). By storing
 *  all properties of bodies (ephemeris, rotation, gravity, etc.) in a set of body objects,
 *  the simulation environment can be defined in a clear and modular way. To create body
 *  objects, the createBodies.h function provides a range of functionality. The
 *  createAccelerationModels.h file provides functions to use body objects to create acceleration
 *  objects.
 */
class Body
{
public:

    //! Constructor for a body
    /*!
     * Constructor for a body, sets current state (with zero default value).
     * \param state Current state of body at initialization (default = zeroes).
     */
    Body( const Eigen::Vector6d& state =
            Eigen::Vector6d::Zero( ) )
        : bodyIsGlobalFrameOrigin_( -1 ), currentState_( state ), timeOfCurrentState_( TUDAT_NAN ),
          ephemerisFrameToBaseFrame_( std::make_shared< BaseStateInterfaceImplementation< double, double > >(
                                          "", [ = ]( const double ){ return Eigen::Vector6d::Zero( ); } ) ),
          currentRotationToLocalFrame_( Eigen::Quaterniond( Eigen::Matrix3d::Identity( ) ) ),
          currentRotationToLocalFrameDerivative_( Eigen::Matrix3d::Zero( ) ),
          currentAngularVelocityVectorInGlobalFrame_( Eigen::Vector3d::Zero( ) ),
          currentAngularVelocityVectorInLocalFrame_( Eigen::Vector3d::Zero( ) ),
          bodyMassFunction_( nullptr ),
          bodyInertiaTensor_( Eigen::Matrix3d::Zero( ) ),
          scaledMeanMomentOfInertia_( TUDAT_NAN )
    {
        currentLongState_ = currentState_.cast< long double >( );
    }

    //! Function to retrieve the class returning the state of this body's ephemeris origin w.r.t. the global origin
    /*!
     * Function to retrieve the class returning the state of this body's ephemeris origin w.r.t. the global origin
     * \return Class returning the state of this body's ephemeris origin w.r.t. the global origin
     */
    std::shared_ptr< BaseStateInterface > getEphemerisFrameToBaseFrame( )
    {
        return ephemerisFrameToBaseFrame_;
    }

    //! Function to set the class returning the state of this body's ephemeris origin w.r.t. the global origin
    /*!
     * Function to set the class returning the state of this body's ephemeris origin w.r.t. the global origin
     * \param ephemerisFrameToBaseFrame Class returning the state of this body's ephemeris origin w.r.t. the global origin
     */
    void setEphemerisFrameToBaseFrame( const std::shared_ptr< BaseStateInterface > ephemerisFrameToBaseFrame )
    {
        ephemerisFrameToBaseFrame_ = ephemerisFrameToBaseFrame;
    }

    //! Set current state of body manually
    /*!
     * Set current state of body manually, which must be in the global frame. Note that this
     * function does not set the currentLongState_, use the setLongState when needing the use of the
     * long precision current state.
     * \param state Current state of the body that is set.
     */
    void setState( const Eigen::Vector6d& state )
    {
        currentState_ = state;
    }

    //! Set current state of body manually in long double precision.
    /*!
     * Set current state of body manually in long double precision. State must be in the global
     * frame.  Note that this function sets both the currentState_ and currentLongState_ variables
     * (currentLongState_ directly and currentState_ by casting the input to double entries).
     * \param longState Current state of the body that is set, in long double precision.
     */
    void setLongState( const Eigen::Matrix< long double, 6, 1 >& longState )
    {
        currentLongState_ = longState;
        currentState_ = longState.cast< double >( );
    }

    //! Templated function to set the state manually.
    /*!
     * Templated function to set the state manually, calls either setState or setLongState function.
     * \param state Current state of the body that is set, with StateScalarType precision.
     */
    template< typename StateScalarType >
    void setTemplatedState( const Eigen::Matrix< StateScalarType, 6, 1 >& state );

    //! Templated function to set the current state of the body from its ephemeris and
    //! global-to-ephemeris-frame function.
    /*!
     * Templated function to set the current state of the body from its ephemeris and
     * global-to-ephemeris-frame function. It sets both the currentState_ and currentLongState_ variables. F
     * FUndamental coputation is done on state with StateScalarType precision as a function of TimeType time
     * \param time Time at which the global state is to be set.
     */
    template< typename StateScalarType = double, typename TimeType = double >
    void setStateFromEphemeris( const TimeType& time )
    {
        if( !( static_cast< Time >( time ) == timeOfCurrentState_ ) )
        {
            // If body is not global frame origin, set state.
            if( bodyIsGlobalFrameOrigin_  == 0 )
            {
                if( sizeof( StateScalarType ) == 8 )
                {
                    currentState_ =
                            ( bodyEphemeris_->getTemplatedStateFromEphemeris< StateScalarType, TimeType >( time ) +
                              ephemerisFrameToBaseFrame_->getBaseFrameState< TimeType, StateScalarType >( time ) ).
                            template cast< double >( );
                    currentLongState_ = currentState_.template cast< long double >( );
                }
                else
                {
                    currentLongState_ =
                            ( bodyEphemeris_->getTemplatedStateFromEphemeris< StateScalarType, TimeType >( time ) +
                              ephemerisFrameToBaseFrame_->getBaseFrameState< TimeType, StateScalarType >( time ) ).
                            template cast< long double >( );
                    currentState_ = currentLongState_.template cast< double >( );
                }
            }
            // If body is global frame origin, set state to zeroes, and barycentric state value.
            else if( bodyIsGlobalFrameOrigin_ == 1 )
            {
                currentState_.setZero( );
                currentLongState_.setZero( );

                if( sizeof( StateScalarType ) == 8 )
                {
                    currentBarycentricState_ =
                            ephemerisFrameToBaseFrame_->getBaseFrameState< TimeType, StateScalarType >( time ).
                            template cast< double >( );
                    currentBarycentricLongState_ = currentBarycentricState_.template cast< long double >( );
                }
                else
                {
                    currentBarycentricLongState_ =
                            ephemerisFrameToBaseFrame_->getBaseFrameState< TimeType, StateScalarType >( time ).
                            template cast< long double >( );
                    currentBarycentricState_ = currentBarycentricLongState_.template cast< double >( );
                }
            }
            else
            {
                throw std::runtime_error( "Error when setting body state, global origin not yet defined." );
            }

            timeOfCurrentState_ = static_cast< TimeType >( time );
        }
    }

//    extern template void setStateFromEphemeris< double, double >( const double& time );

    //! Templated function to get the current state of the body from its ephemeris and
    //! global-to-ephemeris-frame function.
    /*!
     * Templated function to get the current state of the body from its ephemeris and
     * global-to-ephemeris-frame function.  It calls the setStateFromEphemeris state, resetting the currentState_ /
     * currentLongState_ variables, and returning the state with the requested precision
     * \param time Time at which to evaluate states.
     * \return State at requested time
     */
    template< typename StateScalarType = double, typename TimeType = double >
    Eigen::Matrix< StateScalarType, 6, 1 > getStateInBaseFrameFromEphemeris( const TimeType time )
    {
        setStateFromEphemeris< StateScalarType, TimeType >( time );
        if( sizeof( StateScalarType ) == 8 )
        {
            return currentState_.template cast< StateScalarType >( );
        }
        else
        {
            return currentLongState_.template cast< StateScalarType >( );
        }
    }

    //! Templated function to get the current berycentric state of the body from its ephemeris andcglobal-to-ephemeris-frame
    //! function.
    /*!
     * Templated function to get the current berycentric state of the body from its ephemeris andcglobal-to-ephemeris-frame
     * function. It calls the setStateFromEphemeris state, resetting the currentBarycentricState_ /
     * currentBarycentricLongState_ variables, and returning the state with the requested precision. This function can ONLY be
     * called if this body is the global frame origin, otherwise an exception is thrown
     * \param time Time at which to evaluate states.
     * \return Barycentric State at requested time
     */
    template< typename StateScalarType = double, typename TimeType = double >
    Eigen::Matrix< StateScalarType, 6, 1 > getGlobalFrameOriginBarycentricStateFromEphemeris( const TimeType time )
    {
        if( bodyIsGlobalFrameOrigin_ != 1 )
        {
            throw std::runtime_error( "Error, calling global frame origin barycentric state on body that is not global frame origin" );
        }

        setStateFromEphemeris< StateScalarType, TimeType >( time );

        if( sizeof( StateScalarType ) == 8 )
        {
            return currentBarycentricState_.template cast< StateScalarType >( );
        }
        else
        {
            return currentBarycentricLongState_.template cast< StateScalarType >( );
        }
    }

    //! Get current state.
    /*!
     * Returns the internally stored current state vector.
     * \return Current state.
     */
    Eigen::Vector6d getState( ) { return currentState_; }

    //! Get current rotational state.
    /*!
     * Returns the internally stored current rotational state vector.
     * \return Current rotational state.
     */
    Eigen::Vector7d getRotationalStateVector( )
    {
        Eigen::Vector7d rotationalStateVector;

        rotationalStateVector.segment( 0, 4 ) =
                linear_algebra::convertQuaternionToVectorFormat( Eigen::Quaterniond( currentRotationToLocalFrame_.inverse( ) ) );
        rotationalStateVector.segment( 4, 3 ) = currentAngularVelocityVectorInLocalFrame_;
        return rotationalStateVector;
    }

    //! Get current position.
    /*!
     * Returns the internally stored current position vector.
     * \return Current position.
     */
    Eigen::Vector3d getPosition( ) { return currentState_.segment( 0, 3 ); }

    //! Get current velocity.
    /*!
     * Returns the internally stored current velocity vector.
     * \return Current velocity.
     */
    Eigen::Vector3d getVelocity( ) { return currentState_.segment( 3, 3 ); }

    //! Get current state, in long double precision
    /*!
     * Returns the internally stored current state vector, in long double precision
     * \return Current state, in long double precisio
     */
    Eigen::Matrix< long double, 6, 1 > getLongState( ) { return currentLongState_; }

    //! Get current position, in long double precision
    /*!
     * Returns the internally stored current position vector, in long double precision
     * \return Current position, in long double precision
     */
    Eigen::Matrix< long double, 3, 1 > getLongPosition( ) { return currentLongState_.segment( 0, 3 ); }

    //! Get current velocity, in long double precision.
    /*!
     * Returns the internally stored current velocity vector.
     * \return Current velocity, in long double precision
     */
    Eigen::Matrix< long double, 3, 1 > getLongVelocity( ) { return currentLongState_.segment( 3, 3 ); }

    //! Templated function to retrieve the state.
    /*!
     * Templated function to retrieve the state, calls either getState or getLongState function.
     * \return  Current state of the body, with StateScalarType precision.
     */
    template< typename ScalarStateType >
    Eigen::Matrix< ScalarStateType, 6, 1 > getTemplatedState( );

    //! Function to set the rotation from global to body-fixed frame at given time
    /*!
     * Function to set the rotation from global to body-fixed frame at given time, using the
     * rotationalEphemeris_ member object
     * \param time Time at which the rotation is to be retrieved.
     */
    void setCurrentRotationToLocalFrameFromEphemeris( const double time )
    {
        if( rotationalEphemeris_!= nullptr )
        {
            currentRotationToLocalFrame_ = rotationalEphemeris_->getRotationToTargetFrame( time );
        }
        else if( dependentOrientationCalculator_ != nullptr )
        {
            currentRotationToLocalFrame_ = dependentOrientationCalculator_->computeAndGetRotationToLocalFrame( time );
        }
        else
        {
            throw std::runtime_error(
                        "Error, no rotation model found in Body::setCurrentRotationToLocalFrameFromEphemeris" );
        }
    }

    //! Function to set the rotation matrix derivative from global to body-fixed frame at given time
    /*!
     * Function to set the rotation matrix derivative from global to body-fixed frame at given time,
     * using the rotationalEphemeris_ member object
     * \param time Time at which the rotation matrix derivative is to be retrieved.
     */
    void setCurrentRotationToLocalFrameDerivativeFromEphemeris( const double time )
    {
        if( rotationalEphemeris_!= nullptr )
        {
            currentRotationToLocalFrameDerivative_
                    = rotationalEphemeris_->getDerivativeOfRotationToTargetFrame( time );
        }
        else if( dependentOrientationCalculator_ != nullptr )
        {
            currentRotationToLocalFrameDerivative_.setZero( );
        }
        else
        {
            throw std::runtime_error(
                        "Error, no rotationalEphemeris_ found in Body::setCurrentRotationToLocalFrameDerivativeFromEphemeris" );
        }
    }

    //! Function to set the angular velocity vector in the global frame at given time
    /*!
     * Function to set the angular velocity vector in the global frame at given time, using the
     * rotationalEphemeris_ member object
     * \param time Time at which the angular velocity vector in the global frame is to be retrieved.
     */
    void setCurrentAngularVelocityVectorInGlobalFrame( const double time )
    {
        if( rotationalEphemeris_!= nullptr )
        {
            currentAngularVelocityVectorInGlobalFrame_
                    = rotationalEphemeris_->getRotationalVelocityVectorInBaseFrame( time );
            currentAngularVelocityVectorInLocalFrame_ = currentRotationToLocalFrame_ * currentAngularVelocityVectorInGlobalFrame_;

        }
        else if( dependentOrientationCalculator_ != nullptr )
        {
            currentAngularVelocityVectorInGlobalFrame_.setZero( );
            currentAngularVelocityVectorInLocalFrame_.setZero( );
        }
        else
        {
            throw std::runtime_error(
                        "Error, no rotationalEphemeris_ found in Body::setCurrentAngularVelocityVectorInGlobalFrame" );
        }
    }

    //! Function to set the full rotational state at given time
    /*!
     * Function to set the full rotational state at (rotation from global to body-fixed frame
     * rotation matrix derivative from global to body-fixed frame and angular velocity vector in the
     * global frame) at given time, using the rotationalEphemeris_ member object.
     * \param time Time at which the angular velocity vector in the global frame is to be retrieved.
     */
    template< typename TimeType >
    void setCurrentRotationalStateToLocalFrameFromEphemeris( const TimeType time )
    {
        if( rotationalEphemeris_ != nullptr )
        {
            rotationalEphemeris_->getFullRotationalQuantitiesToTargetFrameTemplated< TimeType >(
                        currentRotationToLocalFrame_, currentRotationToLocalFrameDerivative_,
                        currentAngularVelocityVectorInGlobalFrame_, time );
            currentAngularVelocityVectorInLocalFrame_ = currentRotationToLocalFrame_ * currentAngularVelocityVectorInGlobalFrame_;
        }
        else if( dependentOrientationCalculator_ != nullptr )
        {
            currentRotationToLocalFrame_ = dependentOrientationCalculator_->computeAndGetRotationToLocalFrame( time );
            currentRotationToLocalFrameDerivative_.setZero( );
            currentAngularVelocityVectorInGlobalFrame_.setZero( );
            currentAngularVelocityVectorInLocalFrame_.setZero( );
        }
        else
        {
            throw std::runtime_error(
                        "Error, no rotationalEphemeris_ found in Body::setCurrentRotationalStateToLocalFrameFromEphemeris" );
        }
    }

    //! Function to set the full rotational state directly
    /*!
     * Function to set the full rotational state  directly (rotation from global to body-fixed frame
     * rotation matrix derivative from global to body-fixed frame and angular velocity vector in the
     * global frame) directly, by providing the current rotational state as input.
     * \param currentRotationalStateFromLocalToGlobalFrame Quaternion from body-fixed to propagation frame
     * (in vector form) and the body's angular velocity vector in body-fixed frame.
     */
    void setCurrentRotationalStateToLocalFrame( const Eigen::Vector7d currentRotationalStateFromLocalToGlobalFrame )
    {
        Eigen::Quaterniond currentRotationToGlobalFrame =
                Eigen::Quaterniond( currentRotationalStateFromLocalToGlobalFrame( 0 ),
                                    currentRotationalStateFromLocalToGlobalFrame( 1 ),
                                    currentRotationalStateFromLocalToGlobalFrame( 2 ),
                                    currentRotationalStateFromLocalToGlobalFrame( 3 ) );
        currentRotationToGlobalFrame.normalize( );

        currentRotationToLocalFrame_ = currentRotationToGlobalFrame.inverse( );
        currentAngularVelocityVectorInGlobalFrame_ =
                currentRotationToGlobalFrame * currentRotationalStateFromLocalToGlobalFrame.block( 4, 0, 3, 1 );
        currentAngularVelocityVectorInLocalFrame_ = currentRotationalStateFromLocalToGlobalFrame.block( 4, 0, 3, 1 );

        Eigen::Matrix3d currentRotationMatrixToLocalFrame = ( currentRotationToLocalFrame_ ).toRotationMatrix( );
        currentRotationToLocalFrameDerivative_ = linear_algebra::getCrossProductMatrix(
                    currentRotationalStateFromLocalToGlobalFrame.block( 4, 0, 3, 1 ) ) * currentRotationMatrixToLocalFrame;
    }

    //! Get current rotation from body-fixed to inertial frame.
    /*!
     *  Get current rotation from body-fixed to inertial frame, as set from the rotationalEphemeris_
     *  by the setCurrentRotationalStateToLocalFrameFromEphemeris or
     *  setCurrentRotationToLocalFrameFromEphemeris function.  If body has no rotational ephemeris,
     *  an identity quaternion (no rotation) is returned.
     *  \return Current rotation from body-fixed to inertial frame.
     */
    Eigen::Quaterniond getCurrentRotationToGlobalFrame( )
    {
        return currentRotationToLocalFrame_.inverse( );
    }

    //! Get current rotation from inertial to body-fixed frame.
    /*!
     *  Get current rotation from inertial to body-fixed frame, as set from the rotationalEphemeris_
     *  by the setCurrentRotationalStateToLocalFrameFromEphemeris or
     *  setCurrentRotationToLocalFrameFromEphemeris function.  If body has no rotational ephemeris,
     *  an identity quaternion (no rotation) is returned.
     *  \return Current rotation from inertial to body-fixed frame.
     */
    Eigen::Quaterniond getCurrentRotationToLocalFrame( )
    {
        return currentRotationToLocalFrame_;
    }

    //! Get current rotational state.
    /*!
     *  Get current rotational state, expressed as a quaternion from global to body-fixed frame
     *  (in vector form) and the body's angular velocity vector in inertial frame.
     *  \return Current rotational state in quaternions and rotational velocity.
     */
    Eigen::Vector7d getCurrentRotationalState( )
    {
        return ( Eigen::VectorXd( 7 ) << linear_algebra::convertQuaternionToVectorFormat( getCurrentRotationToGlobalFrame( ) ),
                 getCurrentAngularVelocityVectorInGlobalFrame( ) ).finished( );
    }

    //! Get current rotation matrix derivative from body-fixed to global frame.
    /*!
     *  Get current rotation matrix derivative from body-fixed frame to global, as set from the
     *  rotationalEphemeris_ by the setCurrentRotationalStateToLocalFrameFromEphemeris or
     *  setCurrentRotationToLocalFrameDerivativeFromEphemeris function. If body has no rotational
     *  ephemeris, an zero matrix (no rotation) is returned.
     *  \return Current otation matrix derivative from global to body-fixed frame.
     */
    Eigen::Matrix3d getCurrentRotationMatrixDerivativeToGlobalFrame( )
    {
        return currentRotationToLocalFrameDerivative_.transpose( );
    }

    //! Get current rotation matrix derivative from global to body-fixed frame.
    /*!
     *  Get current rotation matrix derivative from global to body-fixed frame, as set from the
     *  rotationalEphemeris_ by the setCurrentRotationalStateToLocalFrameFromEphemeris or
     *  setCurrentRotationToLocalFrameDerivativeFromEphemeris function. If body has no rotational
     *  ephemeris, an zero matrix (no rotation) is returned.
     *  \return Current otation matrix derivative from global to body-fixed frame.
     */
    Eigen::Matrix3d getCurrentRotationMatrixDerivativeToLocalFrame( )
    {
        return currentRotationToLocalFrameDerivative_;
    }

    //! Get current angular velocity vector for body's rotation, expressed in the global frame.
    /*!
     *  Get current angular velocity vector for body's rotation, expressed in the global frame.
     *  \return Current angular velocity vector for body's rotation, expressed in the global frame.
     */
    Eigen::Vector3d getCurrentAngularVelocityVectorInGlobalFrame( )
    {
        return currentAngularVelocityVectorInGlobalFrame_;
    }

    //! Get current angular velocity vector for body's rotation, expressed in the local frame.
    /*!
     *  Get current angular velocity vector for body's rotation, expressed in the local frame.
     *  Transformation from the global to the local frame is done by rotating the vector with the
     *  current quaternion to local frame.
     *  \return Current angular velocity vector for body's rotation, expressed in the local frame.
     */
    Eigen::Vector3d getCurrentAngularVelocityVectorInLocalFrame( )
    {
        return currentAngularVelocityVectorInLocalFrame_;
    }

    //! Function to set the ephemeris of the body.
    /*!
     *  Function to set the ephemeris of the body, which is used to represent the (a priori)
     *  state history of the body.
     *  \param bodyEphemeris New ephemeris of the body.
     */
    void setEphemeris( const std::shared_ptr< ephemerides::Ephemeris > bodyEphemeris )
    {
        bodyEphemeris_ = bodyEphemeris;
    }

    //! Function to set the gravity field of the body.
    /*!
     *  Function to set the gravity field of the body; input is also used to (re)set the mass of the
     *  body.
     *  \param gravityFieldModel New gravity field of the body.
     */
    void setGravityFieldModel(
            const std::shared_ptr< gravitation::GravityFieldModel > gravityFieldModel )
    {
        gravityFieldModel_ = gravityFieldModel;

        // Update current mass of body, provide warning
        if( bodyMassFunction_ != nullptr )
        {
            std::cerr << "Warning when settings gravity field model for body, mass function already found: resetting" << std::endl;
        }

        currentMass_ = gravityFieldModel_->getGravitationalParameter( )
                       / physical_constants::GRAVITATIONAL_CONSTANT;
        bodyMassFunction_ = [ = ]( const double ){ return currentMass_; };
    }

    //! Function to set the atmosphere model of the body.
    /*!
     *  Function to set the atmosphere model of the body.
     *  \param atmosphereModel Atmosphere model of the body.
     */
    void setAtmosphereModel(
            const std::shared_ptr< aerodynamics::AtmosphereModel > atmosphereModel )
    {
        atmosphereModel_ = atmosphereModel;
    }

    //! Function to set the rotation model of the body.
    /*!
     *  Function to set the rotation model of the body.
     *  \param rotationalEphemeris Rotation model of the body.
     */
    void setRotationalEphemeris(
            const std::shared_ptr< ephemerides::RotationalEphemeris > rotationalEphemeris )
    {
        if( dependentOrientationCalculator_ != nullptr )
        {
            std::cerr << "Warning when setting rotational ephemeris, dependentOrientationCalculator_ already found, NOT setting closure" << std::endl;
        }
        rotationalEphemeris_ = rotationalEphemeris;
    }

    //! Function to set a rotation model that is only valid during numerical propagation
    /*!
     *  Function to set a rotation model that is only valid during numerical propagation, as it depends on the full state
     *  of the environment
     *  \param dependentOrientationCalculator Object from which the orientation is computed.
     */
    void setDependentOrientationCalculator(
            const std::shared_ptr< reference_frames::DependentOrientationCalculator > dependentOrientationCalculator )
    {
        // Check if object already exists
        if( dependentOrientationCalculator_ != nullptr )
        {
            // Try to create closure between new and existing objects (i.e ensure that they end up computing the same rotation
            // in differen manenrs.
            if( ( std::dynamic_pointer_cast< reference_frames::AerodynamicAngleCalculator >(
                      dependentOrientationCalculator ) != nullptr ) &&
                    ( std::dynamic_pointer_cast< reference_frames::AerodynamicAngleCalculator >(
                          dependentOrientationCalculator_ ) == nullptr ) )
            {
                reference_frames::setAerodynamicDependentOrientationCalculatorClosure(
                            dependentOrientationCalculator_,
                            std::dynamic_pointer_cast< reference_frames::AerodynamicAngleCalculator >(
                                dependentOrientationCalculator ) );
            }
            else if( ( std::dynamic_pointer_cast< reference_frames::AerodynamicAngleCalculator >(
                           dependentOrientationCalculator_ ) != nullptr ) &&
                     ( std::dynamic_pointer_cast< reference_frames::AerodynamicAngleCalculator >(
                           dependentOrientationCalculator ) == nullptr ) )
            {
                reference_frames::setAerodynamicDependentOrientationCalculatorClosure(
                            dependentOrientationCalculator,
                            std::dynamic_pointer_cast< reference_frames::AerodynamicAngleCalculator >(
                                dependentOrientationCalculator_ ) );
            }
            else
            {
                std::cerr << "Warning, cannot reset dependentOrientationCalculator, incompatible object already exists" << std::endl;
            }
        }
        else
        {
            dependentOrientationCalculator_ = dependentOrientationCalculator;
        }
    }

    //! Function to set the shape model of the body.
    /*!
     *  Function to set the shape model of the body.
     *  \param shapeModel Shape model of the body.
     */
    void setShapeModel( const std::shared_ptr< basic_astrodynamics::BodyShapeModel > shapeModel )
    {
        shapeModel_ = shapeModel;
    }

    //! Function to set the aerodynamic coefficient interface of the body.
    /*!
     *  Function to set the aerodynamic coefficient interface of the body.
     *  \param aerodynamicCoefficientInterface Aerodynamic coefficient interface of the body.
     */
    void setAerodynamicCoefficientInterface(
            const std::shared_ptr< aerodynamics::AerodynamicCoefficientInterface >
            aerodynamicCoefficientInterface)
    {
        aerodynamicCoefficientInterface_ = aerodynamicCoefficientInterface;
    }

    //! Function to set the body flight conditions
    /*!
     * Function to set the body flight conditions, which calculates current aerodynamic angles,
     * altitude, etc.
     * \param aerodynamicFlightConditions Body flight conditions
     */
    void setFlightConditions(
            const std::shared_ptr< aerodynamics::FlightConditions > aerodynamicFlightConditions )
    {
        aerodynamicFlightConditions_ = aerodynamicFlightConditions;

        // If dependentOrientationCalculator_ object already exists, provide a warning and create closure between the two
        if( dependentOrientationCalculator_ != nullptr )
        {
            reference_frames::setAerodynamicDependentOrientationCalculatorClosure(
                        dependentOrientationCalculator_, aerodynamicFlightConditions_->getAerodynamicAngleCalculator( ) );
        }
        else
        {
            dependentOrientationCalculator_ = aerodynamicFlightConditions->getAerodynamicAngleCalculator( );
        }

        // Create closure between rotational ephemeris and aerodynamic angle calculator.
        if( rotationalEphemeris_ != nullptr )
        {
            reference_frames::setAerodynamicDependentOrientationCalculatorClosure(
                        rotationalEphemeris_, aerodynamicFlightConditions_->getAerodynamicAngleCalculator( )  );
        }
    }

    //! Function to set the radiation pressure interface of the body, for a single radiation source.
    /*!
     *  Function to set the radiation pressure interface of the body, for a single radiation source
     *  \param radiatingBody Name of body that is the source of the radiation.
     *  \param radiationPressureInterface Radiation pressure interface of the body.
     */
    void setRadiationPressureInterface(
            const std::string& radiatingBody,
            const std::shared_ptr< electro_magnetism::RadiationPressureInterface >
            radiationPressureInterface )
    {
        radiationPressureInterfaces_[ radiatingBody ] = radiationPressureInterface;
    }

    //! Function to set object containing all variations in the gravity field of this body.
    /*!
     * Function to set object containing all variations in the gravity field of this body.
     * \param gravityFieldVariationSet Object containing all variations in the gravity field of this body.
     */
    void setGravityFieldVariationSet(
            const std::shared_ptr< gravitation::GravityFieldVariationsSet >
            gravityFieldVariationSet )
    {
        gravityFieldVariationSet_ = gravityFieldVariationSet;
    }

    //! Function to get the gravity field model of the body.
    /*!
     *  Function to get the gravity field model of the body.
     *  \return Gravity field model of the body.
     */
    std::shared_ptr< gravitation::GravityFieldModel > getGravityFieldModel( )
    {
        return gravityFieldModel_;
    }

    //! Function to get the ephemeris of the body.
    /*!
     *  Function to get the ephemeris of the body.
     *  \return Ephemeris of the body.
     */
    std::shared_ptr< ephemerides::Ephemeris > getEphemeris( )
    {
        return bodyEphemeris_;
    }

    //! Function to get the atmosphere model of the body.
    /*!
     *  Function to get the atmosphere model of the body.
     *  \return Atmosphere model of the body.
     */
    std::shared_ptr< aerodynamics::AtmosphereModel > getAtmosphereModel( )
    {
        return atmosphereModel_;
    }

    //! Function to get the rotation model of the body.
    /*!
     *  Function to get the rotation model of the body.
     *  \return Rotation model of the body.
     */
    std::shared_ptr< ephemerides::RotationalEphemeris > getRotationalEphemeris( )
    {
        return rotationalEphemeris_;
    }

    //! Function to retrieve the model to compute the rotation of the body based on the current state of the environment.
    /*!
     * Function to retrieve the model to compute the rotation of the body based on the current state of the environment
     * (model is only valid during propagation).
     * \return Model to compute the rotation of the body based on the current state of the environment
     */
    std::shared_ptr< reference_frames::DependentOrientationCalculator > getDependentOrientationCalculator( )
    {
        return dependentOrientationCalculator_;
    }

    //! Function to retrieve the shape model of body.
    /*!
     * Function to retrieve the shape model of body.
     * \return Shape model of body.
     */
    std::shared_ptr< basic_astrodynamics::BodyShapeModel > getShapeModel( )
    {
        return shapeModel_;
    }

    //! Function to retrieve the aerodynamic coefficient model of body.
    /*!
     * Function to retrieve the body aerodynamic coefficient model of body.
     * \return Aerodynamic coefficient model of body.
     */
    std::shared_ptr< aerodynamics::AerodynamicCoefficientInterface >
    getAerodynamicCoefficientInterface( )
    {
        return aerodynamicCoefficientInterface_;
    }

    //! Function to retrieve the body flight conditions
    /*!
     * Function to retrieve the body flight conditions, which calculates current aerodynamic angles,
     * altitude, etc.
     * \return Body flight conditions
     */
    std::shared_ptr< aerodynamics::FlightConditions > getFlightConditions( )
    {
        return aerodynamicFlightConditions_;
    }

    //! Function to retrieve the shape model of the body.
    /*!
     *  Function to retrieve the shape model of the body.
     *  \return Shape model of the body.
     */
    std::map< std::string, std::shared_ptr< electro_magnetism::RadiationPressureInterface > >
    getRadiationPressureInterfaces( )
    {
        return radiationPressureInterfaces_;
    }

    //! Function to retrieve a single object describing variation in the gravity field of this body.
    /*!
     *  Function to retrieve a single object describing variation in the gravity field of this body.
     *  \param deformationType Type of gravity field variation.
     *  \param identifier Identifier of gravity field variation that is to be retrieved (empty by default; only required
     *  if multiple variations of same type are present)
     *  \return Object describing requested variation in the gravity field of this body.
     */
    std::pair< bool, std::shared_ptr< gravitation::GravityFieldVariations > >
    getGravityFieldVariation(
            const gravitation::BodyDeformationTypes& deformationType,
            const std::string identifier = "" )
    {
        return gravityFieldVariationSet_->getGravityFieldVariation( deformationType, identifier );
    }

    //! Function to retrieve object containing all variations in the gravity field of this body.
    /*!
     * Function to retrieve object containing all variations in the gravity field of this body.
     * \return Object containing all variations in the gravity field of this body.
     */
    std::shared_ptr< gravitation::GravityFieldVariationsSet > getGravityFieldVariationSet( )
    {
        return gravityFieldVariationSet_;
    }

    //! Function to retrieve container object with hardware systems present on/in body
    /*!
     * Function to retrieve container object with hardware systems present on/in body.
     * \return Container object with hardware systems present on/in body.
     */
    std::shared_ptr< system_models::VehicleSystems > getVehicleSystems( )
    {
        return vehicleSystems_;
    }

    //! Function to set container object with hardware systems present on/in body
    /*!
     * Function to set container object with hardware systems present on/in body (typically only non-nullptr for a vehicle).
     * \param vehicleSystems Container object with hardware systems present on/in body.
     */
    void setVehicleSystems( const std::shared_ptr< system_models::VehicleSystems > vehicleSystems )
    {
        vehicleSystems_ = vehicleSystems;
    }

    //! Function to set the function returning body mass as a function of time
    /*!
     * Function to set the function returning body mass as a function of time
     * \param bodyMassFunction Function returning body mass as a function of time
     */
    void setBodyMassFunction( const std::function< double( const double ) > bodyMassFunction )
    {
        bodyMassFunction_ = bodyMassFunction;
    }

    //! Function to set the body mass as being constant (i.e. time-independent)
    /*!
     * Function to set the body mass as being constant (i.e. time-independent)
     * \param bodyMass New constant body mass
     */
    void setConstantBodyMass( const double bodyMass )
    {
        bodyMassFunction_ = [ = ]( const double ){ return bodyMass; };
        currentMass_ = bodyMass;
    }

    //! Function to get the function returning body mass as a function of time
    /*!
     * Function to get the function returning body mass as a function of time
     * \return Function returning body mass as a function of time
     */
    std::function< double( const double ) > getBodyMassFunction( )
    {
        return bodyMassFunction_;
    }

    //! Function to update the body mass to the current time
    /*!
     * Function to update the body mass to the current time, using the bodyMassFunction_ function
     * \param time Current time
     */
    void updateMass( const double time )
    {
        if( bodyMassFunction_ != nullptr )
        {
            currentMass_ = bodyMassFunction_( time );
        }
        else
        {
            throw std::runtime_error( "Error when updating body mass, no mass function is set" );
        }
    }

    //! Function to retrieve the current body mass
    /*!
     * Function to retrieve the current body mass
     * \return Current body mass.
     */
    double getBodyMass( )
    {
        return currentMass_;
    }

    //! Function to retrieve the body moment-of-inertia tensor.
    /*!
     * Function to retrieve the body moment-of-inertia tensor.
     * \return  Body moment-of-inertia tensor.
     */
    Eigen::Matrix3d getBodyInertiaTensor( )
    {
        return bodyInertiaTensor_;
    }

    //! Function to retrieve body scaled mean moment of inertia
    /*!
     *  Function to retrieve body scaled mean moment of inertia
     * \return Body scaled mean moment of inertia
     */
    double getScaledMeanMomentOfInertia( )
    {
        return scaledMeanMomentOfInertia_;
    }

    //! Function to reset body scaled mean moment of inertia
    /*!
     *  Function to reset body scaled mean moment of inertia, and update associated inertia tensor
     *  \param scaledMeanMomentOfInertia New body scaled mean moment of inertia
     */
    void setScaledMeanMomentOfInertia( const double scaledMeanMomentOfInertia )
    {
        double oldScaledMeanMomentOfInertia = scaledMeanMomentOfInertia_;
        double oldMeanMomentOfInertia =
                ( bodyInertiaTensor_( 0, 0 ) + bodyInertiaTensor_( 1, 1 ) + bodyInertiaTensor_( 2, 2 ) ) / 3.0;
        scaledMeanMomentOfInertia_ = scaledMeanMomentOfInertia;
        double meanMomentOfInertia = scaledMeanMomentOfInertia_/ oldScaledMeanMomentOfInertia * oldMeanMomentOfInertia;
        bodyInertiaTensor_( 0, 0 ) += meanMomentOfInertia - oldMeanMomentOfInertia;
        bodyInertiaTensor_( 1, 1 ) += meanMomentOfInertia - oldMeanMomentOfInertia;
        bodyInertiaTensor_( 2, 2 ) += meanMomentOfInertia - oldMeanMomentOfInertia;

    }

    //! Function to (re)set the body moment-of-inertia tensor.
    /*!
     * Function to (re)set the body moment-of-inertia tensor.
     * \param bodyInertiaTensor Body moment-of-inertia tensor.
     */
    void setBodyInertiaTensor( const Eigen::Matrix3d& bodyInertiaTensor )
    {
        bodyInertiaTensor_ = bodyInertiaTensor;
    }
    //! Function to (re)set the body moment-of-inertia tensor and scaled mean-moment of inertia.
    /*!
     * Function to (re)set the body moment-of-inertia tensor and scaled mean-moment of inertia.
     * \param bodyInertiaTensor Body moment-of-inertia tensor.
     * \param scaledMeanMomentOfInertia Body scaled mean-moment of inertia
     */
    void setBodyInertiaTensor( const Eigen::Matrix3d& bodyInertiaTensor, const double scaledMeanMomentOfInertia )
    {
        bodyInertiaTensor_ = bodyInertiaTensor;
        scaledMeanMomentOfInertia_ = scaledMeanMomentOfInertia;
    }

    //! Function to (re)set the body moment-of-inertia tensor from the gravity field.
    /*!
     * Function to (re)set the body moment-of-inertia tensor from the gravity field, requires only a mean moment of inertia
     * (scaled by mass times reference radius squared). Other data are taken from this body's spherical harmonic gravity field
     * \param scaledMeanMomentOfInertia  Mean moment of inertial, divided by (M*R^2), with M the mass of the body and R the
     * reference radius of the gravity field.
     */
    void setBodyInertiaTensorFromGravityField( const double scaledMeanMomentOfInertia )
    {
        if( std::dynamic_pointer_cast< gravitation::SphericalHarmonicsGravityField >( gravityFieldModel_ ) == nullptr )
        {
            throw std::runtime_error( "Error when setting inertia tensor from mean moments of inertia, gravity field model is not spherical harmonic" );
        }
        else
        {
            scaledMeanMomentOfInertia_ = scaledMeanMomentOfInertia;
            bodyInertiaTensor_ = gravitation::getInertiaTensor(
                        std::dynamic_pointer_cast< gravitation::SphericalHarmonicsGravityField >( gravityFieldModel_ ),
                        scaledMeanMomentOfInertia );
        }
    }

    //! Function to (re)set the body moment-of-inertia tensor from existing gravity field and mean moment of inertia.
    /*!
     * Function to (re)set the body moment-of-inertia tensor from existing gravity field and mean moment of inertia.
     * \param printWarningIfNotSet  Boolean to denote whether a warning is to be printed if scaled mean moment is not defined.
     */
    void setBodyInertiaTensorFromGravityFieldAndExistingMeanMoment(
            const bool printWarningIfNotSet = true )
    {
        if( !( scaledMeanMomentOfInertia_ == scaledMeanMomentOfInertia_ ) )
        {
            std::shared_ptr< gravitation::SphericalHarmonicsGravityField > sphericalHarmonicGravityField =
                    std::dynamic_pointer_cast< gravitation::SphericalHarmonicsGravityField >( gravityFieldModel_ );
            if( sphericalHarmonicGravityField != nullptr )
            {
                double normalizationFactor =
                        sphericalHarmonicGravityField->getGravitationalParameter( ) *
                        sphericalHarmonicGravityField->getReferenceRadius( ) *
                        sphericalHarmonicGravityField->getReferenceRadius( ) / physical_constants::GRAVITATIONAL_CONSTANT;
                scaledMeanMomentOfInertia_ =
                        ( bodyInertiaTensor_( 0, 0 ) + bodyInertiaTensor_( 1, 1 ) + bodyInertiaTensor_( 2, 2 ) ) /
                        ( 3.0 * normalizationFactor );
            }
            else if( printWarningIfNotSet )
            {
                std::cerr<<"Warning when setting body inertia tensor, mean moment of inertia set to zero. "<<std::endl;
            }
        }

        if( scaledMeanMomentOfInertia_ == scaledMeanMomentOfInertia_ )
        {
            setBodyInertiaTensorFromGravityField( scaledMeanMomentOfInertia_ );
        }
    }

    //! Function to add a ground station to the body
    /*!
     * Function to add a ground station to the body
     * \param stationName Name of ground station
     * \param station Ground station object that is to be set
     */
    void addGroundStation( const std::string& stationName,
                           const std::shared_ptr< ground_stations::GroundStation >& station )
    {
        groundStationMap[ stationName ] = station;
    }

    //! Function to retrieve a ground station
    /*!
     * Function to retrieve a ground station
     * \param stationName Name of ground station
     * \return Ground station object that is retrieved
     */
    std::shared_ptr< ground_stations::GroundStation > getGroundStation( const std::string& stationName ) const
    {
        if( groundStationMap.count( stationName ) == 0 )
        {
            throw std::runtime_error( "Error, station " + stationName + " does not exist" );
        }

        return groundStationMap.at( stationName );
    }

    //! Function to retrieve full list of ground stations
    /*!
     * Function to retrieve full list of ground stations
     * \return Full list of ground stations
     */
    std::map< std::string, std::shared_ptr< ground_stations::GroundStation > > getGroundStationMap( ) const
    {
        return groundStationMap;
    }

    //! Function to recompute the internal variables of member variables that depend on the ephemerides bodies.
    /*!
     * Function to recompute the internal variables of member variables that depend on the ephemerides of this and other
     * bodies. This function is typically called after equations of motion have been computed and set in environment to
     * ensure full model consistency.
     */
    void updateConstantEphemerisDependentMemberQuantities( )
    {
        if( std::dynamic_pointer_cast< gravitation::TimeDependentSphericalHarmonicsGravityField >(
                    gravityFieldModel_ ) != nullptr )
        {
            std::dynamic_pointer_cast< gravitation::TimeDependentSphericalHarmonicsGravityField >(
                        gravityFieldModel_ )->updateCorrectionFunctions( );
        }
    }

    //! Function to indicate that the state needs to be recomputed on next call to setStateFromEphemeris.
    /*!
     * Function to reset the time to which the state was last updated using setStateFromEphemeris function to nan, thereby
     * singalling that it needs to be recomputed upon next call.
     */
    void recomputeStateOnNextCall( )
    {
        timeOfCurrentState_ = Time( TUDAT_NAN );
    }

    //! Function to retrieve variable denoting whether this body is the global frame origin
    /*!
     * Function to retrieve variable denoting whether this body is the global frame origin
     * \return Variable denoting whether this body is the global frame origin
     */
    int getIsBodyGlobalFrameOrigin( )
    {
        return bodyIsGlobalFrameOrigin_;
    }

    //! Function to set variable denoting whether this body is the global frame origin
    /*!
     * Function to set variable denoting whether this body is the global frame origin
     * \param bodyIsGlobalFrameOrigin Variable denoting whether this body is the global frame origin
     */
    void setIsBodyGlobalFrameOrigin( const int bodyIsGlobalFrameOrigin )
    {
        bodyIsGlobalFrameOrigin_ = bodyIsGlobalFrameOrigin;
    }

protected:

private:

    //! Variable denoting whether this body is the global frame origin (1 if true, 0 if false, -1 if not yet set)
    int bodyIsGlobalFrameOrigin_;

    //! Current state.
    Eigen::Vector6d currentState_;

    //! Current state with long double precision.
    Eigen::Matrix< long double, 6, 1 > currentLongState_;

    //! Current state.
    Eigen::Vector6d currentBarycentricState_;

    //! Current state with long double precision.
    Eigen::Matrix< long double, 6, 1 > currentBarycentricLongState_;

    //! Time at which state was last set from ephemeris
    Time timeOfCurrentState_;

    //! Class returning the state of this body's ephemeris origin w.r.t. the global origin (as typically created by
    //! setGlobalFrameBodyEphemerides function).
    std::shared_ptr< BaseStateInterface > ephemerisFrameToBaseFrame_;

    //! Current rotation from the global to the body-fixed frame.
    Eigen::Quaterniond currentRotationToLocalFrame_;

    //! Current first derivative w.r.t. time of the rotation matrix from the global to the
    //! body-fixed frame.
    Eigen::Matrix3d currentRotationToLocalFrameDerivative_;

    //! Current angular velocity vector for body's rotation, expressed in the global frame.
    Eigen::Vector3d currentAngularVelocityVectorInGlobalFrame_;

    //! Current angular velocity vector for body's rotation, expressed in the body-fixed frame.
    Eigen::Vector3d currentAngularVelocityVectorInLocalFrame_;

    //! Mass of body (default set to zero, calculated from GravityFieldModel when it is set).
    double currentMass_;

    //! Function returning body mass as a function of time.
    std::function< double( const double ) > bodyMassFunction_;

    //! Body moment-of-inertia tensor.
    Eigen::Matrix3d bodyInertiaTensor_;

    //! Body scaled mean moment of inertia
    double scaledMeanMomentOfInertia_;

    //! Ephemeris of body.
    std::shared_ptr< ephemerides::Ephemeris > bodyEphemeris_;

    //! Gravity field model of body.
    std::shared_ptr< gravitation::GravityFieldModel > gravityFieldModel_;

    //! Object containing all variations in the gravity field of this body.
    std::shared_ptr< gravitation::GravityFieldVariationsSet > gravityFieldVariationSet_;

    //! Atmosphere model of body.
    std::shared_ptr< aerodynamics::AtmosphereModel > atmosphereModel_;

    //! Shape model of body.
    std::shared_ptr< basic_astrodynamics::BodyShapeModel > shapeModel_;

    //! Aerodynamic coefficient model of body.
    std::shared_ptr< aerodynamics::AerodynamicCoefficientInterface > aerodynamicCoefficientInterface_;

    //! Object used for calculating current aerodynamic angles, altitude, etc.
    std::shared_ptr< aerodynamics::FlightConditions > aerodynamicFlightConditions_;

    //! Rotation model of body.
    std::shared_ptr< ephemerides::RotationalEphemeris > rotationalEphemeris_;

    //! Model to compute the rotation of the body based on the current state of the environment, only valid during propagation.
    std::shared_ptr< reference_frames::DependentOrientationCalculator > dependentOrientationCalculator_;

    //! List of radiation pressure models for the body, with the sources bodies as key
    std::map< std::string, std::shared_ptr< electro_magnetism::RadiationPressureInterface > >
    radiationPressureInterfaces_;

    //! Predefined iterator for efficiency purposes.
    std::map< std::string,
    std::shared_ptr< electro_magnetism::RadiationPressureInterface > >::iterator
    radiationPressureIterator_;

    //! List of ground station objects on Body
    std::map< std::string, std::shared_ptr< ground_stations::GroundStation > > groundStationMap;

    //! Container object with hardware systems present on/in body (typically only non-nullptr for a vehicle).
    std::shared_ptr< system_models::VehicleSystems > vehicleSystems_;

};

//! Typdef for a list of body objects (as unordered_map for efficiency reasons)
typedef std::unordered_map< std::string, std::shared_ptr< Body > > NamedBodyMap;

//! Function ot retrieve the common global translational state origin of the environment
/*!
 * Function ot retrieve the common global translational state origin of the environment. This function throws an exception
 * if multiple bodies are found as the frame origin
 * \param bodyMap List of body objects.
 * \return Global translational state origin of the environment
 */
std::string getGlobalFrameOrigin( const NamedBodyMap& bodyMap );

//! Function to compute the acceleration of a body, using its ephemeris and finite differences
/*!
 *  Function to compute the acceleration of a body, using its ephemeris and 8th order finite difference and 100 s time step
 *  \param bodyWithAcceleration Body for which acceleration is to be computed
 *  \param nominalEvalutationTime Time at which acceleration is to be evaluated.
 */
template< typename StateScalarType = double, typename TimeType = double >
Eigen::Matrix< StateScalarType, 3, 1 > getBodyAccelerationInBaseFramefromNumericalDifferentiation(
        const std::shared_ptr< Body > bodyWithAcceleration,
        const TimeType nominalEvalutationTime )
{
    std::function< Eigen::Matrix< StateScalarType, 6, 1  >( const TimeType ) > bodyStateFunction =
            std::bind( &Body::getStateInBaseFrameFromEphemeris< StateScalarType, TimeType >, bodyWithAcceleration, std::placeholders::_1 );
    return numerical_derivatives::computeCentralDifferenceFromFunction(
                bodyStateFunction, nominalEvalutationTime, 100.0, numerical_derivatives::order8 ).segment( 3, 3 );
}

} // namespace simulation_setup

} // namespace tudat

#endif // TUDAT_BODY_H

redpajama_set_name: RedPajamaGithub


Code V 10.4 Free Download - Get Into Pc !
Code V 10.4 Free Download Latest Version for Windows. It is full offline installer standalone setup of Code V 10.4.
Code V 10.4 is a very handy optical design software application which can be used for modeling, analyzing, optimizing and providing the fabrication support for development of the optical systems for diverse applications. Code V 10.4 provides powerful and a very easy to use toolkit of optical techniques and calculations which will enable you to create superior designs.
Code V 10.4 has got a very vast array of technical, graphical as well as ease of use features that mkes the lens design software application of choice for developing superior imaging optics. This application also supports fast and accurate tolerancing for superior fabrication support and it can also perform accurate and the groundbreaking beam propagation analysis. Code V 10.4 has got Global Synthesis that can quickly generate various different design solutions. All in all Code V 10.4 is a very useful optical design application which can be used for modeling, analyzing, optimizing and providing the fabrication support for developing the optical systems.
Below are some noticeable features which you'll experience after Code V 10.4 free download.
Handy optical design application which can be used for modeling, analyzing, optimizing and providing the fabrication support for development of the optical systems for diverse applications.
Provides powerful and a very easy to use toolkit of optical techniques and calculations which will enable you to create superior designs.
Got a very vast array of technical, graphical as well as ease of use features that makes the lens design software application of choice for developing superior imaging optics.
Supports fast and accurate tolerancing for superior fabrication support.
Can also perform accurate and the groundbreaking beam propagation analysis.
Got Global Synthesis that can quickly generate various different design solutions.
Before you start Code V 10.4 free download, make sure your PC meets minimum system requirements.
By clicking on the button below you can start download code v 10.4 full and complete setup. Also install file extraction tool like Winrar in your computer because mostly files are consist of Zip, RAR or ISO format.


text:
Rożyńsk Wielki (Duits: Groß Rosinsko; 1938-1945: Großfreiendorf) is een plaats in het Poolse woiwodschap Ermland-Mazurië, in het district Gołdapski. De plaats maakt deel uit van de stad- en landgemeente Gołdap.

Plaats in Ermland-Mazurië

redpajama_set_name: RedPajamaWikipedia


text:
Carnegie Joint Global Coordinator in largest Finnish IPO in recent history
Publicerad 11 oktober 2017
Carnegie Joint Global Coordinator in the EUR 876 million IPO of Terveystalo, the largest ever non-privatisation IPO in Finland in recent history.
The Board of Directors of Terveystalo has announced the completion of the Offering today. The subscription price for the Offer Shares was EUR 9.76 per share in the Institutional and the Public Offering, and EUR 8.79 per share in the Personnel Offering, corresponding to a company market capitalisation of approximately EUR 1,250 million, immediately following the Offering.
Terveystalo is a leading private healthcare service provider in Finland offering primary and outpatient secondary healthcare services to corporates, private and public sector customers. Terveystalo is the market leader in occupational healthcare in Finland and provides a nationwide reach through its approximately 180 clinics and digital platform.
In 2016, the Company had approximately 1.0 million individual customers as well as approximately 2.7 million doctor visits, accounting for 12 percent of the total doctor visits in Finland. As at June 30 2017, Terveystalo had approximately 4,445 employees (including part-time employees).
The Company will receive gross proceeds of approximately EUR 100 million from the Offering. Demand from Finnish and international investors was strong and the Offering was multiple times oversubscribed. Trading of the Company's shares is expected to commence on the pre-list of the Helsinki Stock Exchange on October 11, 2017 and on the official list on October 13, 2017.
Carnegie acted as Joint Global Coordinator and Joint Bookrunner in connection with the Offering.

redpajama_set_name: RedPajamaCommonCrawl


text:
Alameda County Bar Association Lawyer Referral Service
Don't get lost in Google results. Contact the ACBA LRS to be referred to a qualified, insured, pre-screened attorney. (510) 302-2222. Do you need a lawyer and not know where to start?
Call us at (510) 302-ACBA (2222), opt 4 or contact us online at: https://www.acbanet.org/need-a-lawyer/ to be matched up with a pre-screened attorney. The ACBA LRS has been the trusted source for lawyer referrals in Alameda County for 50 years and is regulated and certified by the State Bar of California. For a $35 fee, we match you with an attorney qualified to help you with your legal issue for up to a 30 minute consultation.
Please note: The ACBA LRS phone lines will closed starting Thursday December 17 and will reopen on Tues. Jan. 5th.
We will be accepting online requests for legal assistance until Wednesday, December 23rd. From December 24th – January 4th, the online request system will be closed and will not be accepting online requests. The online request system will reopen on Tues. Jan. 5th.
It's not too late - take the 2020 Census today. Takes 2-3 minutes and you're done!
Learn about the 2020 Census - how to respond, and why it matters. Shape your future. START HERE.
Bill Text - AB-3088 Tenancy: rental payment default: mortgage forbearance: state of emergency: COVID-19.
AB 3088 - Moratorium on evictions for non-payment of rent due to COVID-19 financial hardship, subject to numerous conditions, until January 31, 2021 signed by the Governor August 31st.
Our phones are closed from August 3rd to August 17th, but we're taking online referrals at 510.law!
IRS adds phone operators to answer Economic Impact Payment questions | Internal Revenue Service
The IRS is set to add 3,500 telephone representatives to answer common questions about Economic Impact Payments
IR-2020-97, May 18, 2020 — Today, the Internal Revenue Service is starting to add 3,500 telephone representatives to answer some of the most common questions about Economic Impact Payments.
We're closed on Monday for the Memorial Day Holiday. Our referral specialists will be back to take your calls and online requests Tuesday am.
How to Avoid Eviction in Alameda County
Good tips on how to avoid eviction in Alameda County
On Tuesday March 31, 2020 the Alameda County Board of Supervisors passed an eviction moratorium, suspending evictions for renters and homeowners impacted by COVID-19 who live in unincorporated portions of the County and in Alameda County cities where local protections have not been adopted.
Beware: COVID-19 Scams » Alameda County Bar Association
Watch out for these COVID-19 scams.
Covid-19 Scams: Beware of the explosion of Scams tied to the Coronavirus. "Barracuda, a site specializing in online security, revealed that it spotted 9,116 coronavirus-themed spear-phishing emails between March 1 and March 23 a huge increase from the 167 in January." A couple of the most c...
We're open (working remotely) during the shelter in place order. Need a lawyer? Send us a request today! https://www.acbanet.org/request-a-lawyer/
How to Apply for Unemployment Benefits
Have you lost work because of the COVID-19 outbreak?
Wednesday: If you've lost work as a result of the coronavirus outbreak in California, here's what to know.
A Roundup of California Worker Rights in the Time of COVID-19
BERKELEY, CALIFORNIA – As COVID-19 spreads, many workers are considering how to protect themselves and their families – while worrying about their work and budgets. At the time this blog post…
Small Business owners in Oakland: The City will work with small business owners that have been affected by the COVID-19 pandemic. To mitigate the economic impacts caused by the COVID-19 pandemic , the City's Revenue Bureau, Finance Department will waive late payment penalties for small businesses resulting from failure to file taxes due March 2, 2020 on time due to COVID-19. The waiver applies to only 2020 taxes and for businesses generating less than $250,000 in annual gross receipts.
To apply for a payment plan or to seek waiver of penalties due to COVID-19, please call the customer service line at (510) 238-3704 or email [email protected]
Taxpayers can file an extension on their returns until June 15th
https://www.law360.com/california/articles/1253845/calif-taxpayers-hindered-by-covid-19-get-extension?nl_pk=7e88123b-363a-47f7-9a88-2644ce802270&utm_source=newsletter&utm_medium=email&utm_campaign=california
Press Release: ACBA Moves Legal Services Online » Alameda County Bar Association
We're excited to partner with Qase Inc. to bring virtual capabilities to our Lawyer Referral Service!
"We are committed to reducing the risk of coronavirus exposure for our lawyers and the rest of our community. Remote delivery of legal services, including pro bono services, is a great place to start," said Tiela Chalmers, ACBA CEO. "And in the long run, it will also help us serve people in more remote areas."
Coronavirus speeds adoption of virtual legal services in Alameda County Oakland, California & Vancouver, British Columbia, March 10, 2020 – Alameda County Bar Association (ACBA) and Qase today announce initiatives to help slow the spread of coronavirus. The ACBA connects more than 10,000 resid...
COVID-19 - County of Alameda - Superior Court of California
If you are currently experiencing symptoms associated with COVID-19 (or any flu-like symptoms) and have business with the court, please consider the following options that are available to you to minimize the need to come to a courthouse:
The official language used for the content of the Alameda County Superior Court public website is English. Google™ Translate is a free online language translation service that can translate text and web pages into different languages. Computerized translations are only an approximation of the webs...
The ACBA Lawyer Referral Service will be closed Monday January 21st for the Martin Luther King Jr. Holiday. Please submit a referral request online on our website and we'll get back to you ASAP.
California Rings In The New Year With A New Data Privacy Law
New digital privacy laws in 2020.
Under California's new digital privacy law, consumers can opt out of the sale of their personal information. The toughest data privacy law in the U.S., it's expected to set the standard nationwide.
Please note that the ACBA Lawyer Referral Service will be closed at noon on 12/20/19 - 1/2/2020.
Pregnancy + My Job: A Guide to What, When, How
Great resource for pregnant employees.
This guide describes WHAT your workplace rights are in California during and after pregnancy. It also teaches you WHEN and HOW to use your rights.
Regardless of your immigration status, you have constitutional rights.
Regardless of your immigration status, you have guaranteed rights under the Constitution. Learn more here about your rights as an immigrant, and how to express them.
Amid fears of mass deportation of immigrants, State Bar warns of potential legal fraud
Citizenship is not required to file a State Bar complaint for unauthorized practice of law, and the State Bar will not ask about immigration status when a complaint is filed
The State Bar issued an alert Wednesday warning immigrants of potential legal fraud amid fears of mass family deportation. Citizenship is not required to file a complaint for unauthorized practice of law.
We will be closed on Thursday July 4th, but back in action on Friday the 5th! You can send us an online request anytime at https://www.acbanet.org/request-a-lawyer/.
East Bay court to offer new payment options for traffic fees
New options to pay traffic fees coming July 1st
Starting July 1, those with outstanding traffic fees have new options for payment.
How to Avoid Ageism
Good tips for avoiding ageism in the workplace.
There are several misconceptions about older workers and the attributes they bring to ther workplace. Here are tips to avoid them.
Please note that the ACBA and the ACBA LAwyer Referral Service will be closed on Monday April 1, 2019 for Cesar Chavez Day.
Worried about negligent referrals? We got you.
Tune into KALW Your Legal Rights, at 7 pm March 6th, to hear ACBA CEO Tiela Chalmers talking about what local bars can do for the community.
For more than 30 years, Your Legal Rights has provided KALW listeners with free legal information and guidance. We carry on the work of the program's
Scammers impersonate the Social Security Administration
SCAMMER ALERT: If you get a call from the "Social Security Administration" please review these tips from the FTC before you give ANY personal information. Protect your identity!
Your Social Security number is an important key for an identity thief.
Please note that the ACBA LRS will be closed for the holidays from noon today through January 1, 2019. If you need a lawyer while our referral specialists are out of the office, please send us a message online and we'll get back to you as soon as possible! https://www.acbanet.org/public/
Happy Holidays from the ACBA! We'll be closed from noon today through January 1st. We'll be back in the office on January 2, 2019.
Great news for Oakland residents.
Alameda Superior Court issues press release about re-opening Self-Help unit In Oakland early 2019
media2.mofo.com
Morrison & Foerster LLP has released an updated handbook as a resource for survivors of the Fall 2018 California fires.
Wildfire Assistance
Good guide for those affected by wildfires.
Click here for wildfire assistance resources and information.
Our LRS has had quite the year so far! Learn more about joining our referral service on our webiste at https://www.acbanet.org/lawyer-referral-service/.
Gov. Brown signs bill eliminating money bail in California
BREAKING - California will replace bail with "risk assessments" of individuals and nonmonetary conditions of release
Under Senate Bill 10, California will replace bail with "risk assessments" of individuals and non-monetary conditions of release.
Certified by the American Bar Association and the State Bar of California, the ACBA LRS refers thousands of clients to qualified attorneys every year. Our referral counselors are trained to evaluate your situation and find the right lawyer for your needs. LRS attorneys are screened for their experience, knowledge and ethics. Contact us now for a consultation. Be sure to mention Facebook. The ACBA LRS page is not confidential. Please do not share specific details of legal matters on this page. For help finding a lawyer, please call (510) 302- 2222 ext 4, or visit https://www.acbanet.org/For%20the%20Public/NeedaLawyer.aspx.
(510) 302-2222 Ext 4
https://www.acbanet.org/need-a-lawyer/
Be the first to know and let us send you an email when Alameda County Bar Association Lawyer Referral Service posts news and promotions. Your email address will not be used for any other purpose, and you can unsubscribe at any time.
Do you need a lawyer and not know where to start? Call us at (510) 302-ACBA (2222), opt 4 or contact us online at: https://www.acbanet.org/need-a-lawyer/ to be matched up with a pre-screened attorney. The ACBA LRS has been the trusted source for lawyer referrals in Alameda County for 50 years and is regulated and certified by the State Bar of California. For a $40 fee (or $15 fee for our modest means panels), we match you with an attorney qualified to help you with your legal issue for up to a 30 minute consultation.
Town of Phelps, NY
79 Main Street, Phelps
III Corps & Fort Hood NCO Academy
761st Tank Battalion Avenue, Fort Hood
Attorneys: If you were sent a digital record instead of stenographer, contact us at the Protect Your Record Project FB page. Stenographers Protect Your Record. There's no comparison.
Your lawyer request/referral page (at www.acbanet.org) is missing a Submit button.
Make $300 for placing adverts on your car, bike, bicycle, or any other available space you've got. Note: Our agents will come to your location for inspection, and see if truly you placed the advert, during the period of placement. NOTE: ONLY SERIOUS AND FOCUSED MINDED PEOPLE SHOULD INBOX ME. SCAM SHOULDN'T INBOX ME.
Download the FREE mobile app, Expedite, available in the AppleStore and GooglePlay. Expedite is Uberizing litigation support services; i.e., court reporters, videographers, interpreters, mobile videoconference techs, notaries public and couriers. This on-demand platform allows attorneys and paralegals to hail assistance expeditiously by capitalizing on providers in the immediate vicinity. It's that easy! Download it today! Get back to managing your schedule more effectively with Expedite. #paralegals #attorneys www.expedite.legal
I think I have been over charged for no fee collection and for equal equity partners


text:
Indian companies top global list on reporting CSR
Steps taken by the Securities and Exchange Board of India has helped the country score high on this parameter.
Ruchika Chitravanshi
India has emerged as the top country for reporting corporate responsibility information in the annual financial report of companies, but the quality of disclosures has to improve, according to a KPMG survey.
In February, Sebi asked the top 500 listed companies to adopt integrated reporting on a voluntary basis, although only five companies published such information. But 99% of the companies reported on their sustainability performance. The 10th KPMG Survey of Corporate Responsibility Reporting studied annual financial reports and corporate responsibility reports of the top 100 companies by revenue in each of 49 countries it covered.
In India, 95 companies acknowledged human rights as an issue to their business, ranking the country well above the global average. The business risks of climate change, however, were acknowledged by only 34 in India in their non-financial disclosures, according to the survey.
"The quality of disclosures has to improve. Companies in India are not going beyond their organisation to study violation while globally companies even examine their supply chains … private sector has started taking the initiatives and corporate India is discussing these issues," said Santhosh Jayaram, partner, sustainability services, KPMG in India.
"Pressure on firms to up their game on disclosure is growing by the day. Some investors are already taking a hard line approach to demanding disclosure; some countries are considering regulation to mandate it," said KPMG's global head of sustainability services, José Luis Blasco.
The survey explored trends in corporate responsibility reporting, including reporting on the UN's Sustainable Development Goals (SDGs), on human rights and carbon reduction targets. The reporting or linkage of SDGs in India was below the global average of 39%, with only 18 companies linking their business activities with the goals in their business reporting.
Almost three quarters of large and mid-cap companies worldwide do not acknowledge the financial risks of climate change in their annual financial reports, as per the survey findings.
Of the minority that acknowledge that, fewer than one in 20 provides investors with analysis of the potential business value at risk.
CSR spend
securities and exchange board of india
Most Read in Corporate
Key technology trends that will reshape the broking sector in 2023: Siddharth Patnaik, Upstox
There is an increasing focus on patient-centricity, with a growing expectation among patients for more customised care: Manish Mittal, Axtria
Yes Bank collaborates with Microsoft to transform mobile banking experience
Vodafone Idea faces DoT's operations & finance inspection


text:
Q: Last login details of a user This is the query I am using
ResultSet rsta3=st.executeQuery("Select * FROM login_details WHERE name ='"+name1+"' AND intime = (select max(intime) from login_details where name = '"+name1+"')");

With this I get the current login details.How to get the previous login details?What query should I use?

The query given in the below answers gives only 11.44 time

A: You find by this
Select * FROM login_details WHERE name ='"+name1+"' order by intime desc LIMIT 1,1

A: How about
Select * FROM login_details WHERE name ='"+name1+"' order by intime desc LIMIT 1,1

this should retrieve the second oldest record
Also as mentioned due to subsequent findings, as your DB is incorrectly storing your Date data as Varchar, your can use the below query until your fix your DB.
Select * FROM login_details WHERE name ='"+name1+"' order by id desc LIMIT 1,1

A: ResultSet rsta3=st.executeQuery("Select * FROM login_details WHERE name ='"+name1+"' AND intime = (select max(intime) from login_details where name = '"+name1+"' order by intime desc limit 1,1)");

Try this

A: Your Query is right, but you are fetching the data after inserting the current login session.
So, once you validate your for user name and password, get the last login details using your query and then update your table with new login time.
Even the fetching with ORDER BY LIMIT should work.

A: Try something like this :
Select * from login_details where name = '"+name+"' order by outtime desc limit 1,1.
The sole idea is firstly sort the list in descending order by time which in your case is : (outtime) will display the result in the order you want. Then using limit fetch only the first record .

redpajama_set_name: RedPajamaStackExchange


I'm hoping for a 3 day side trip to either Harar or the Great Rift Valley next, but things are difficult to coordinate - I neglected to take "Ethipian time" into account when I thought I could plan this side trip. I'm beginning to feel very comfortable walking the streets in Addis and communicating, though I still hate brushing off the beggars and the kids selling packs of gum. I thnk farenji fatigue is setting in a little - my group gets extra stares because we're 3 tall pale women and 2 kids with dredlocks. Dredlocks are strongly associated with rastafarians here, so we're constantly pointed at with shouts of "Rasta! Rasta!".


It seems like everyone in my life has everything they want or need, so coming up with unique gift ideas can be a challenge.
I'm sure you've had similar experiences, too.
But this year, my wife is going to get the PERFECT gift - a photo of our son.
It's not just any photo, though. It's an acrylic photo block that looks absolutely amazing.
In fact, these things look so incredible that I wanted to share my experience with you so that you can get your loved one a perfect gift as well.
The image above is of the acrylic photo block I have on my desk that I ordered a couple of months ago from BumbleJax.
I wanted something unique and something that would look good on my desk (which is usually not as clean as it appears in the photo!).
In other words, I didn't want just another photo on the wall - I wanted something that would stand out and make a statement.
I think you'll agree - I got what I wanted!
This 6x8 photo block is one inch thick, so it's got a nice heft and visual weight to it.
That's not just because of how beautiful the polished acrylic is, either.
I went with the metallic paper option, so the colors and contrast are truly eye-popping.
To say that I was blown away with the quality of the product is an understatement.
In fact, the quality is so good that when my wife saw it, she tried to steal it and take it to her office to put on her desk!
Thus the perfect gift idea!
I'll be surprising my wife with another photo of our son, and you guessed it, it will be in the form of a BumbleJax acrylic photo block.
I even ordered another one of these things for myself, this time in the form of an acrylic photo block of one of my favorite landscape photos.
I think you'll agree that the acrylic block gives my landscape shot more depth and dimension, while also making it something that is incredibly unique.
I love the size I ordered - 15x10 - because it's small enough to put just about anywhere (mine's on the table in the entryway of my home, but you can also hang them on the wall). But even though it's a smaller block, it's got so much visual appeal that it made an immediate impact on the foyer.
The diamond polished acrylic is insane, too. You can see the photo in crystal-clear definition, and as I mentioned earlier, it really makes the colors and contrast in the images take on new life.
Like I said, I chose the metallic paper option for these, though you can choose lustre if that's more your style.
Either way, BumbleJax face mounts the image onto the acrylic, which is a superior method of printing to maximize the vibrancy of the colors in the shot.
I've seen some pretty bad acrylic blocks in the past, and I didn't want to make that mistake again. After reading tons of positive reviews for BumbleJax, I gave it a shot, and boy, was it worth it!
If you're totally stumped about what gift to give your loved ones for birthdays and other special occasions, believe me, you can't go wrong with an acrylic photo block from BumbleJax.