OccultationEngine.java
/* Copyright 2002-2024 Luc Maisonobe
* Licensed to CS GROUP (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.utils;
import org.hipparchus.CalculusFieldElement;
import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.util.FastMath;
import org.orekit.bodies.OneAxisEllipsoid;
import org.orekit.propagation.FieldSpacecraftState;
import org.orekit.propagation.SpacecraftState;
/** Computation engine for occultation events.
* @author Luc Maisonobe
* @since 12.0
*/
public class OccultationEngine {
/** Occulting body. */
private final OneAxisEllipsoid occulting;
/** Occulted body. */
private final ExtendedPVCoordinatesProvider occulted;
/** Occulted body radius (m). */
private final double occultedRadius;
/** Build a new occultation engine.
* @param occulted the body to be occulted
* @param occultedRadius the radius of the body to be occulted (m)
* @param occulting the occulting body
*/
public OccultationEngine(final ExtendedPVCoordinatesProvider occulted, final double occultedRadius,
final OneAxisEllipsoid occulting) {
this.occulted = occulted;
this.occultedRadius = FastMath.abs(occultedRadius);
this.occulting = occulting;
}
/** Getter for the occulting body.
* @return the occulting body
*/
public OneAxisEllipsoid getOcculting() {
return occulting;
}
/** Getter for the occulted body.
* @return the occulted body
*/
public ExtendedPVCoordinatesProvider getOcculted() {
return occulted;
}
/** Getter for the occultedRadius.
* @return the occultedRadius
*/
public double getOccultedRadius() {
return occultedRadius;
}
/** Compute the occultation angles as seen from a spacecraft.
* @param state the current state information: date, kinematics, attitude
* @return occultation angles
*/
public OccultationAngles angles(final SpacecraftState state) {
final Vector3D psat = state.getPosition(occulting.getBodyFrame());
final Vector3D pted = occulted.getPosition(state.getDate(), occulting.getBodyFrame());
final Vector3D plimb = occulting.pointOnLimb(psat, pted);
final Vector3D ps = psat.subtract(pted);
final Vector3D pi = psat.subtract(plimb);
final double angle = Vector3D.angle(ps, psat);
final double rs = FastMath.asin(occultedRadius / ps.getNorm());
final double ro = Vector3D.angle(pi, psat);
if (Double.isNaN(rs)) {
// we are inside the occulted body…
// set up dummy values consistent with full lighting (assuming occulted is the Sun)
return new OccultationAngles(FastMath.PI, 0.0, 0.0);
} else {
// regular case, we can compute limit angles as seen from spacecraft
return new OccultationAngles(angle, ro, rs);
}
}
/** Compute the occultation angles as seen from a spacecraft.
* @param state the current state information: date, kinematics, attitude
* @param <T> the type of the field elements
* @return occultation angles
*/
public <T extends CalculusFieldElement<T>> FieldOccultationAngles<T> angles(final FieldSpacecraftState<T> state) {
final FieldVector3D<T> psat = state.getPosition(occulting.getBodyFrame());
final FieldVector3D<T> pted = occulted.getPosition(state.getDate(), occulting.getBodyFrame());
final FieldVector3D<T> plimb = occulting.pointOnLimb(psat, pted);
final FieldVector3D<T> ps = psat.subtract(pted);
final FieldVector3D<T> pi = psat.subtract(plimb);
final T angle = FieldVector3D.angle(ps, psat);
final T rs = FastMath.asin(ps.getNorm().reciprocal().multiply(occultedRadius));
final T ro = FieldVector3D.angle(pi, psat);
if (rs.isNaN()) {
// we are inside the occulted body…
// set up dummy values consistent with full lighting (assuming occulted is the Sun)
final T zero = rs.getField().getZero();
return new FieldOccultationAngles<>(zero.newInstance(FastMath.PI), zero, zero);
} else {
// regular case, we can compute limit angles as seen from spacecraft
return new FieldOccultationAngles<>(angle, ro, rs);
}
}
/** Container for occultation angles.
* @since 12.0
*/
public static class OccultationAngles {
/** Apparent separation between occulting and occulted directions. */
private final double separation;
/** Limb radius in occulting/occulted plane. */
private final double limbRadius;
/** Apparent radius of occulted body. */
private final double occultedApparentRadius;
/** Simple constructor.
* @param separation apparent separation between occulting and occulted directions (rad)
* @param limbRadius limb radius in occulting/occulted plane (rad)
* @param occultedApparentRadius apparent radius of occulted body (rad)
*/
OccultationAngles(final double separation, final double limbRadius, final double occultedApparentRadius) {
this.separation = separation;
this.limbRadius = limbRadius;
this.occultedApparentRadius = occultedApparentRadius;
}
/** Get apparent separation between occulting and occulted directions.
* @return apparent separation between occulting and occulted directions (rad)
*/
public double getSeparation() {
return separation;
}
/** Get limb radius in occulting/occulted plane.
* @return limb radius in occulting/occulted plane (rad)
*/
public double getLimbRadius() {
return limbRadius;
}
/** Get apparent radius of occulted body.
* @return apparent radius of occulted body (rad)
*/
public double getOccultedApparentRadius() {
return occultedApparentRadius;
}
}
/** Container for occultation angles.
* @param <T> the type of the field elements
* @since 12.0
*/
public static class FieldOccultationAngles<T extends CalculusFieldElement<T>> {
/** Apparent separation between occulting and occulted directions. */
private final T separation;
/** Limb radius in occulting/occulted plane. */
private final T limbRadius;
/** Apparent radius of occulted body. */
private final T occultedApparentRadius;
/** Simple constructor.
* @param separation apparent separation between occulting and occulted directions (rad)
* @param limbRadius limb radius in occulting/occulted plane (rad)
* @param occultedApparentRadius apparent radius of occulted body (rad)
*/
FieldOccultationAngles(final T separation, final T limbRadius, final T occultedApparentRadius) {
this.separation = separation;
this.limbRadius = limbRadius;
this.occultedApparentRadius = occultedApparentRadius;
}
/** Get apparent separation between occulting and occulted directions.
* @return apparent separation between occulting and occulted directions (rad)
*/
public T getSeparation() {
return separation;
}
/** Get limb radius in occulting/occulted plane.
* @return limb radius in occulting/occulted plane (rad)
*/
public T getLimbRadius() {
return limbRadius;
}
/** Get apparent radius of occulted body.
* @return apparent radius of occulted body (rad)
*/
public T getOccultedApparentRadius() {
return occultedApparentRadius;
}
}
}