J2DifferentialEffect.java
/* Copyright 2002-2017 CS Systèmes d'Information
* Licensed to CS Systèmes d'Information (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.propagation.analytical;
import org.hipparchus.util.FastMath;
import org.orekit.errors.OrekitException;
import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
import org.orekit.orbits.EquinoctialOrbit;
import org.orekit.orbits.Orbit;
import org.orekit.orbits.OrbitType;
import org.orekit.orbits.PositionAngle;
import org.orekit.propagation.SpacecraftState;
import org.orekit.time.AbsoluteDate;
/** Analytical model for J2 effect.
* <p>
* This class computes the differential effect of J2 due to an initial orbit
* offset. A typical case is when an inclination maneuver changes an orbit
* inclination at time t₀. As ascending node drift rate depends on
* inclination, the change induces a time-dependent change in ascending node
* for later dates.
* </p>
* @see org.orekit.forces.maneuvers.SmallManeuverAnalyticalModel
* @author Luc Maisonobe
*/
public class J2DifferentialEffect
implements AdapterPropagator.DifferentialEffect {
/** Reference date. */
private final AbsoluteDate referenceDate;
/** Differential drift on perigee argument. */
private final double dPaDot;
/** Differential drift on ascending node. */
private final double dRaanDot;
/** Indicator for applying effect before reference date. */
private final boolean applyBefore;
/** Simple constructor.
* <p>
* The {@code applyBefore} parameter is mainly used when the differential
* effect is associated with a maneuver. In this case, the parameter must be
* set to {@code false}.
* </p>
* @param original original state at reference date
* @param directEffect direct effect changing the orbit
* @param applyBefore if true, effect is applied both before and after
* reference date, if false it is only applied after reference date
* @param gravityField gravity field to use
* @exception OrekitException if gravity field does not contain J2 coefficient
*/
public J2DifferentialEffect(final SpacecraftState original,
final AdapterPropagator.DifferentialEffect directEffect,
final boolean applyBefore,
final UnnormalizedSphericalHarmonicsProvider gravityField)
throws OrekitException {
this(original, directEffect, applyBefore,
gravityField.getAe(), gravityField.getMu(),
-gravityField.onDate(original.getDate()).getUnnormalizedCnm(2, 0));
}
/** Simple constructor.
* <p>
* The {@code applyBefore} parameter is mainly used when the differential
* effect is associated with a maneuver. In this case, the parameter must be
* set to {@code false}.
* </p>
* @param orbit0 original orbit at reference date
* @param orbit1 shifted orbit at reference date
* @param applyBefore if true, effect is applied both before and after
* reference date, if false it is only applied after reference date
* @param gravityField gravity field to use
* @exception OrekitException if gravity field does not contain J2 coefficient
*/
public J2DifferentialEffect(final Orbit orbit0, final Orbit orbit1, final boolean applyBefore,
final UnnormalizedSphericalHarmonicsProvider gravityField)
throws OrekitException {
this(orbit0, orbit1, applyBefore,
gravityField.getAe(), gravityField.getMu(),
-gravityField.onDate(orbit0.getDate()).getUnnormalizedCnm(2, 0));
}
/** Simple constructor.
* <p>
* The {@code applyBefore} parameter is mainly used when the differential
* effect is associated with a maneuver. In this case, the parameter must be
* set to {@code false}.
* </p>
* @param original original state at reference date
* @param directEffect direct effect changing the orbit
* @param applyBefore if true, effect is applied both before and after
* reference date, if false it is only applied after reference date
* @param referenceRadius reference radius of the Earth for the potential model (m)
* @param mu central attraction coefficient (m³/s²)
* @param j2 un-normalized zonal coefficient (about +1.08e-3 for Earth)
* @exception OrekitException if direct effect cannot be applied
*/
public J2DifferentialEffect(final SpacecraftState original,
final AdapterPropagator.DifferentialEffect directEffect,
final boolean applyBefore,
final double referenceRadius, final double mu, final double j2)
throws OrekitException {
this(original.getOrbit(),
directEffect.apply(original.shiftedBy(0.001)).getOrbit().shiftedBy(-0.001),
applyBefore, referenceRadius, mu, j2);
}
/** Simple constructor.
* <p>
* The {@code applyBefore} parameter is mainly used when the differential
* effect is associated with a maneuver. In this case, the parameter must be
* set to {@code false}.
* </p>
* @param orbit0 original orbit at reference date
* @param orbit1 shifted orbit at reference date
* @param applyBefore if true, effect is applied both before and after
* reference date, if false it is only applied after reference date
* @param referenceRadius reference radius of the Earth for the potential model (m)
* @param mu central attraction coefficient (m³/s²)
* @param j2 un-normalized zonal coefficient (about +1.08e-3 for Earth)
*/
public J2DifferentialEffect(final Orbit orbit0, final Orbit orbit1, final boolean applyBefore,
final double referenceRadius, final double mu, final double j2) {
this.referenceDate = orbit0.getDate();
this.applyBefore = applyBefore;
// extract useful parameters
final double a0 = orbit0.getA();
final double e0 = orbit0.getE();
final double i0 = orbit0.getI();
final double a1 = orbit1.getA();
final double e1 = orbit1.getE();
final double i1 = orbit1.getI();
// compute reference drifts
final double oMe2 = 1 - e0 * e0;
final double ratio = referenceRadius / (a0 * oMe2);
final double cosI = FastMath.cos(i0);
final double sinI = FastMath.sin(i0);
final double n = FastMath.sqrt(mu / a0) / a0;
final double c = ratio * ratio * n * j2;
final double refPaDot = 0.75 * c * (4 - 5 * sinI * sinI);
final double refRaanDot = -1.5 * c * cosI;
// differential model on perigee argument drift
final double dPaDotDa = -3.5 * refPaDot / a0;
final double dPaDotDe = 4 * refPaDot * e0 / oMe2;
final double dPaDotDi = -7.5 * c * sinI * cosI;
dPaDot = dPaDotDa * (a1 - a0) + dPaDotDe * (e1 - e0) + dPaDotDi * (i1 - i0);
// differential model on ascending node drift
final double dRaanDotDa = -3.5 * refRaanDot / a0;
final double dRaanDotDe = 4 * refRaanDot * e0 / oMe2;
final double dRaanDotDi = -refRaanDot * FastMath.tan(i0);
dRaanDot = dRaanDotDa * (a1 - a0) + dRaanDotDe * (e1 - e0) + dRaanDotDi * (i1 - i0);
}
/** Compute the effect of the maneuver on an orbit.
* @param orbit1 original orbit at t₁, without maneuver
* @return orbit at t₁, taking the maneuver
* into account if t₁ > t₀
* @see #apply(SpacecraftState)
*/
public Orbit apply(final Orbit orbit1) {
if (orbit1.getDate().compareTo(referenceDate) <= 0 && !applyBefore) {
// the orbit change has not occurred yet, don't change anything
return orbit1;
}
return updateOrbit(orbit1);
}
/** {@inheritDoc} */
public SpacecraftState apply(final SpacecraftState state1) {
if (state1.getDate().compareTo(referenceDate) <= 0 && !applyBefore) {
// the orbit change has not occurred yet, don't change anything
return state1;
}
return new SpacecraftState(updateOrbit(state1.getOrbit()),
state1.getAttitude(), state1.getMass());
}
/** Compute the differential effect of J2 on an orbit.
* @param orbit1 original orbit at t₁, without differential J2
* @return orbit at t₁, always taking the effect into account
*/
private Orbit updateOrbit(final Orbit orbit1) {
// convert current orbital state to equinoctial elements
final EquinoctialOrbit original =
(EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(orbit1);
// compute differential effect
final AbsoluteDate date = original.getDate();
final double dt = date.durationFrom(referenceDate);
final double dPaRaan = (dPaDot + dRaanDot) * dt;
final double cPaRaan = FastMath.cos(dPaRaan);
final double sPaRaan = FastMath.sin(dPaRaan);
final double dRaan = dRaanDot * dt;
final double cRaan = FastMath.cos(dRaan);
final double sRaan = FastMath.sin(dRaan);
final double ex = original.getEquinoctialEx() * cPaRaan -
original.getEquinoctialEy() * sPaRaan;
final double ey = original.getEquinoctialEx() * sPaRaan +
original.getEquinoctialEy() * cPaRaan;
final double hx = original.getHx() * cRaan - original.getHy() * sRaan;
final double hy = original.getHx() * sRaan + original.getHy() * cRaan;
final double lambda = original.getLv() + dPaRaan;
// build updated orbit
final EquinoctialOrbit updated =
new EquinoctialOrbit(original.getA(), ex, ey, hx, hy, lambda, PositionAngle.TRUE,
original.getFrame(), date, original.getMu());
// convert to required type
return orbit1.getType().convertType(updated);
}
}