GNSSFieldAttitudeContext.java
- /* Copyright 2002-2018 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.gnss.attitude;
- import org.hipparchus.Field;
- import org.hipparchus.RealFieldElement;
- import org.hipparchus.analysis.differentiation.FDSFactory;
- import org.hipparchus.analysis.differentiation.FieldDerivativeStructure;
- import org.hipparchus.geometry.euclidean.threed.FieldRotation;
- import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
- import org.hipparchus.geometry.euclidean.threed.RotationConvention;
- import org.hipparchus.geometry.euclidean.threed.Vector3D;
- import org.hipparchus.util.FastMath;
- import org.orekit.errors.OrekitException;
- import org.orekit.frames.FieldTransform;
- import org.orekit.frames.LOFType;
- import org.orekit.time.FieldAbsoluteDate;
- import org.orekit.time.FieldTimeStamped;
- import org.orekit.utils.FieldPVCoordinates;
- import org.orekit.utils.PVCoordinates;
- import org.orekit.utils.TimeStampedFieldAngularCoordinates;
- import org.orekit.utils.TimeStampedFieldPVCoordinates;
- /**
- * Boilerplate computations for GNSS attitude.
- *
- * <p>
- * This class is intended to hold throw-away data pertaining to <em>one</em> call
- * to {@link GNSSAttitudeProvider#getAttitude(org.orekit.utils.FieldPVCoordinatesProvider,
- * org.orekit.time.FieldAbsoluteDate, org.orekit.frames.Frame)) getAttitude}. It allows
- * the various {@link GNSSAttitudeProvider} implementations to be immutable as they
- * do not store any state, and hence to be thread-safe, reentrant and naturally
- * serializable (so for example ephemeris built from them are also serializable).
- * </p>
- *
- * @author Luc Maisonobe
- * @since 9.2
- */
- class GNSSFieldAttitudeContext<T extends RealFieldElement<T>> implements FieldTimeStamped<T> {
- /** Constant Y axis. */
- private static final PVCoordinates PLUS_Y =
- new PVCoordinates(Vector3D.PLUS_J, Vector3D.ZERO, Vector3D.ZERO);
- /** Constant Z axis. */
- private static final PVCoordinates MINUS_Z =
- new PVCoordinates(Vector3D.MINUS_K, Vector3D.ZERO, Vector3D.ZERO);
- /** Limit value below which we shoud use replace beta by betaIni. */
- private static final double BETA_SIGN_CHANGE_PROTECTION = FastMath.toRadians(0.07);
- /** Derivation order. */
- private static final int ORDER = 2;
- /** Constant Y axis. */
- private final FieldPVCoordinates<T> plusY;
- /** Constant Z axis. */
- private final FieldPVCoordinates<T> minusZ;
- /** Spacecraft position-velocity in inertial frame. */
- private final TimeStampedFieldPVCoordinates<T> svPV;
- /** Spacecraft position-velocity in inertial frame. */
- private final FieldPVCoordinates<FieldDerivativeStructure<T>> svPVDS;
- /** Angle between Sun and orbital plane. */
- private final FieldDerivativeStructure<T> beta;
- /** Cosine of the angle between spacecraft and Sun direction. */
- private final FieldDerivativeStructure<T> svbCos;
- /** Nominal yaw. */
- private final TimeStampedFieldAngularCoordinates<T> nominalYaw;
- /** Nominal yaw. */
- private final FieldRotation<FieldDerivativeStructure<T>> nominalYawDS;
- /** Spacecraft angular velocity. */
- private T muRate;
- /** Limit cosine for the midnight turn. */
- private double cNight;
- /** Limit cosine for the noon turn. */
- private double cNoon;
- /** Relative orbit angle to turn center. */
- private FieldDerivativeStructure<T> delta;
- /** Half span of the turn region, as an angle in orbit plane. */
- private T halfSpan;
- /** Turn start date. */
- private FieldAbsoluteDate<T> turnStart;
- /** Turn end date. */
- private FieldAbsoluteDate<T> turnEnd;
- /** Simple constructor.
- * @param sunPV Sun position-velocity in inertial frame
- * @param svPV spacecraft position-velocity in inertial frame
- * @exception OrekitException if yaw cannot be corrected
- */
- GNSSFieldAttitudeContext(final TimeStampedFieldPVCoordinates<T> sunPV, final TimeStampedFieldPVCoordinates<T> svPV)
- throws OrekitException {
- final Field<T> field = sunPV.getDate().getField();
- plusY = new FieldPVCoordinates<>(field, PLUS_Y);
- minusZ = new FieldPVCoordinates<>(field, MINUS_Z);
- final FieldPVCoordinates<FieldDerivativeStructure<T>> sunPVDS = sunPV.toDerivativeStructurePV(ORDER);
- this.svPV = svPV;
- this.svPVDS = svPV.toDerivativeStructurePV(ORDER);
- this.svbCos = FieldVector3D.dotProduct(sunPVDS.getPosition(), svPVDS.getPosition()).
- divide(sunPVDS.getPosition().getNorm().
- multiply(svPVDS.getPosition().getNorm()));
- this.beta = FieldVector3D.angle(sunPVDS.getPosition(), svPVDS.getMomentum()).
- negate().
- add(0.5 * FastMath.PI);
- // nominal yaw steering
- this.nominalYaw =
- new TimeStampedFieldAngularCoordinates<>(svPV.getDate(),
- svPV.normalize(),
- sunPV.crossProduct(svPV).normalize(),
- minusZ,
- plusY,
- 1.0e-9);
- this.nominalYawDS = nominalYaw.toDerivativeStructureRotation(ORDER);
- this.muRate = svPV.getAngularVelocity().getNorm();
- }
- /** {@inheritDoc} */
- @Override
- public FieldAbsoluteDate<T> getDate() {
- return svPV.getDate();
- }
- /** Get the cosine of the angle between spacecraft and Sun direction.
- * @return cosine of the angle between spacecraft and Sun direction.
- */
- public T getSVBcos() {
- return svbCos.getValue();
- }
- /** Get the angle between Sun and orbital plane.
- * @return angle between Sun and orbital plane
- * @see #getBetaDS()
- * @see #getSecuredBeta(TurnTimeRange)
- */
- public T getBeta() {
- return beta.getValue();
- }
- /** Get the angle between Sun and orbital plane.
- * @return angle between Sun and orbital plane
- * @see #getBeta()
- * @see #getSecuredBeta(TurnTimeRange)
- */
- public FieldDerivativeStructure<T> getBetaDS() {
- return beta;
- }
- /** Get a Sun elevation angle that does not change sign within the turn.
- * <p>
- * This method either returns the current beta or replaces it with the
- * value at turn start, so the sign remains constant throughout the
- * turn. As of 9.2, it is only useful for GPS and Glonass.
- * </p>
- * @return secured Sun elevation angle
- * @see #getBeta()
- * @see #getBetaDS()
- */
- public T getSecuredBeta() {
- return FastMath.abs(beta.getReal()) < BETA_SIGN_CHANGE_PROTECTION ?
- beta.taylor(timeSinceTurnStart(getDate()).negate()) :
- getBeta();
- }
- /** Get the nominal yaw.
- * @return nominal yaw
- */
- public TimeStampedFieldAngularCoordinates<T> getNominalYaw() {
- return nominalYaw;
- }
- /** Compute nominal yaw angle.
- * @return nominal yaw angle
- */
- public T yawAngle() {
- final FieldVector3D<T> xSat = nominalYaw.getRotation().revert().applyTo(Vector3D.PLUS_I);
- return FastMath.copySign(FieldVector3D.angle(svPV.getVelocity(), xSat), -beta.getReal());
- }
- /** Compute nominal yaw angle.
- * @return nominal yaw angle
- */
- public FieldDerivativeStructure<T> yawAngleDS() {
- final FieldVector3D<FieldDerivativeStructure<T>> xSat = nominalYawDS.revert().applyTo(Vector3D.PLUS_I);
- final FDSFactory<T> factory = xSat.getX().getFactory();
- final FieldVector3D<T> v = svPV.getVelocity();
- final FieldVector3D<FieldDerivativeStructure<T>> vDS = new FieldVector3D<>(factory.constant(v.getX()),
- factory.constant(v.getY()),
- factory.constant(v.getZ()));
- return FieldVector3D.angle(vDS, xSat).copySign(beta.getValue().negate());
- }
- /** Set up the midnight/noon turn region.
- * @param cosNight limit cosine for the midnight turn
- * @param cosNoon limit cosine for the noon turn
- * @return true if spacecraft is in the midnight/noon turn region
- */
- public boolean setUpTurnRegion(final double cosNight, final double cosNoon) {
- this.cNight = cosNight;
- this.cNoon = cosNoon;
- if (svbCos.getValue().getReal() < cNight) {
- // in eclipse turn mode
- final FieldDerivativeStructure<T> absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos().negate().add(FastMath.PI));
- delta = absDelta.copySign(absDelta.getPartialDerivative(1).negate());
- return true;
- } else if (svbCos.getValue().getReal() > cNoon) {
- // in noon turn mode
- final FieldDerivativeStructure<T> absDelta = inOrbitPlaneAbsoluteAngle(svbCos.acos());
- delta = absDelta.copySign(absDelta.getPartialDerivative(1).negate());
- return true;
- } else {
- return false;
- }
- }
- /** Get the relative orbit angle to turn center.
- * @return relative orbit angle to turn center
- */
- public T getDelta() {
- return delta.getValue();
- }
- /** Get the relative orbit angle to turn center.
- * @return relative orbit angle to turn center
- */
- public FieldDerivativeStructure<T> getDeltaDS() {
- return delta;
- }
- /** Check if spacecraft is in the half orbit closest to Sun.
- * @return true if spacecraft is in the half orbit closest to Sun
- */
- public boolean inSunSide() {
- return svbCos.getValue().getReal() > 0;
- }
- /** Get yaw at turn start.
- * @param sunBeta Sun elevation above orbital plane
- * (it <em>may</em> be different from {@link #getBeta()} in
- * some special cases)
- * @return yaw at turn start
- */
- public T getYawStart(final T sunBeta) {
- return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos.getValue()));
- }
- /** Get yaw at turn end.
- * @param sunBeta Sun elevation above orbital plane
- * (it <em>may</em> be different from {@link #getBeta()} in
- * some special cases)
- * @return yaw at turn end
- */
- public T getYawEnd(final T sunBeta) {
- return computePhi(sunBeta, FastMath.copySign(halfSpan, svbCos.getValue().negate()));
- }
- /** Compute yaw rate.
- * @param sunBeta Sun elevation above orbital plane
- * (it <em>may</em> be different from {@link #getBeta()} in
- * some special cases)
- * @return yaw rate
- */
- public T yawRate(final T sunBeta) {
- return getYawEnd(sunBeta).subtract(getYawStart(sunBeta)).divide(getTurnDuration());
- }
- /** Get the spacecraft angular velocity.
- * @return spacecraft angular velocity
- */
- public T getMuRate() {
- return muRate;
- }
- /** Project a spacecraft/Sun angle into orbital plane.
- * <p>
- * This method is intended to find the limits of the noon and midnight
- * turns in orbital plane. The return angle is signed, depending on the
- * spacecraft being before or after turn middle point.
- * </p>
- * @param angle spacecraft/Sun angle (or spacecraft/opposite-of-Sun)
- * @return angle projected into orbital plane, always positive
- */
- private FieldDerivativeStructure<T> inOrbitPlaneAbsoluteAngle(final FieldDerivativeStructure<T> angle) {
- return FastMath.acos(FastMath.cos(angle).divide(FastMath.cos(beta)));
- }
- /** Project a spacecraft/Sun angle into orbital plane.
- * <p>
- * This method is intended to find the limits of the noon and midnight
- * turns in orbital plane. The return angle is always positive. The
- * correct sign to apply depends on the spacecraft being before or
- * after turn middle point.
- * </p>
- * @param angle spacecraft/Sun angle (or spacecraft/opposite-of-Sun)
- * @return angle projected into orbital plane, always positive
- */
- public T inOrbitPlaneAbsoluteAngle(final T angle) {
- return FastMath.acos(FastMath.cos(angle).divide(FastMath.cos(beta.getReal())));
- }
- /** Compute yaw.
- * @param sunBeta Sun elevation above orbital plane
- * (it <em>may</em> be different from {@link #getBeta()} in
- * some special cases)
- * @param inOrbitPlaneAngle in orbit angle between spacecraft
- * and Sun (or opposite of Sun) projection
- * @return yaw angle
- */
- public T computePhi(final T sunBeta, final T inOrbitPlaneAngle) {
- return FastMath.atan2(FastMath.tan(sunBeta).negate(), FastMath.sin(inOrbitPlaneAngle));
- }
- /** Set turn half span and compute corresponding turn time range.
- * @param halfSpan half span of the turn region, as an angle in orbit plane
- */
- public void setHalfSpan(final T halfSpan) {
- this.halfSpan = halfSpan;
- this.turnStart = svPV.getDate().shiftedBy(delta.getValue().subtract(halfSpan).divide(muRate));
- this.turnEnd = svPV.getDate().shiftedBy(delta.getValue().add(halfSpan).divide(muRate));
- }
- /** Check if a date is within range.
- * @param date date to check
- * @param endMargin margin in seconds after turn end
- * @return true if date is within range extended by end margin
- */
- public boolean inTurnTimeRange(final FieldAbsoluteDate<T> date, final double endMargin) {
- return date.durationFrom(turnStart).getReal() > 0 &&
- date.durationFrom(turnEnd).getReal() < endMargin;
- }
- /** Get turn duration.
- * @return turn duration
- */
- public T getTurnDuration() {
- return halfSpan.multiply(2).divide(muRate);
- }
- /** Get elapsed time since turn start.
- * @param date date to check
- * @return elapsed time from turn start to specified date
- */
- public T timeSinceTurnStart(final FieldAbsoluteDate<T> date) {
- return date.durationFrom(turnStart);
- }
- /** Generate an attitude with turn-corrected yaw.
- * @param yaw yaw value to apply
- * @param yawDot yaw first time derivative
- * @return attitude with specified yaw
- */
- public TimeStampedFieldAngularCoordinates<T> turnCorrectedAttitude(final T yaw, final T yawDot) {
- return turnCorrectedAttitude(beta.getFactory().build(yaw, yawDot, yaw.getField().getZero()));
- }
- /** Generate an attitude with turn-corrected yaw.
- * @param yaw yaw value to apply
- * @return attitude with specified yaw
- */
- public TimeStampedFieldAngularCoordinates<T> turnCorrectedAttitude(final FieldDerivativeStructure<T> yaw) {
- // compute a linear yaw correction model
- final FieldDerivativeStructure<T> nominalAngle = yawAngleDS();
- final TimeStampedFieldAngularCoordinates<T> correction =
- new TimeStampedFieldAngularCoordinates<>(nominalYaw.getDate(),
- new FieldRotation<>(FieldVector3D.getPlusK(nominalAngle.getField()),
- yaw.subtract(nominalAngle),
- RotationConvention.VECTOR_OPERATOR));
- // combine the two parts of the attitude
- return correction.addOffset(getNominalYaw());
- }
- /** Compute Orbit Normal (ON) yaw.
- * @return Orbit Normal yaw, using inertial frame as reference
- */
- public TimeStampedFieldAngularCoordinates<T> orbitNormalYaw() {
- final FieldTransform<T> t = LOFType.VVLH.transformFromInertial(svPV.getDate(), svPV);
- return new TimeStampedFieldAngularCoordinates<>(svPV.getDate(),
- t.getRotation(),
- t.getRotationRate(),
- t.getRotationAcceleration());
- }
- }