| /******************************************************************************* |
| * Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Pierre Allard, |
| * Regent L'Archeveque - initial API and implementation |
| * |
| * SPDX-License-Identifier: EPL-1.0 |
| * |
| *******************************************************************************/ |
| package org.eclipse.apogy.core.environment.orbit.earth.impl; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; |
| import org.eclipse.apogy.core.environment.earth.ApogyEarthFacade; |
| import org.eclipse.apogy.core.environment.earth.GeographicCoordinates; |
| import org.eclipse.apogy.core.environment.orbit.earth.ApogyCoreEnvironmentOrbitEarthFacade; |
| import org.eclipse.apogy.core.environment.orbit.earth.ApogyCoreEnvironmentOrbitEarthFactory; |
| import org.eclipse.apogy.core.environment.orbit.earth.VisibilityPass; |
| import org.eclipse.apogy.core.environment.orbit.earth.VisibilityPassSpacecraftPosition; |
| import org.orekit.bodies.BodyShape; |
| import org.orekit.bodies.GeodeticPoint; |
| import org.orekit.bodies.OneAxisEllipsoid; |
| import org.orekit.frames.Frame; |
| import org.orekit.frames.FramesFactory; |
| import org.orekit.frames.LOFType; |
| import org.orekit.frames.LocalOrbitalFrame; |
| import org.orekit.frames.TopocentricFrame; |
| import org.orekit.propagation.Propagator; |
| import org.orekit.time.AbsoluteDate; |
| import org.orekit.time.TimeScale; |
| import org.orekit.time.TimeScalesFactory; |
| import org.orekit.utils.Constants; |
| import org.orekit.utils.IERSConventions; |
| import org.orekit.utils.PVCoordinates; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public class VisibilityPassSpacecraftPositionHistoryCustomImpl extends VisibilityPassSpacecraftPositionHistoryImpl { |
| |
| private static final Logger Logger = LoggerFactory.getLogger(VisibilityPassSpacecraftPositionHistoryImpl.class); |
| |
| @Override |
| public VisibilityPassSpacecraftPosition getClosestRangePosition() { |
| VisibilityPassSpacecraftPosition smallestRangePosition = null; |
| |
| if (!getPositions().isEmpty()) { |
| // Finds the position with the highest elevation. |
| double closestRange = Double.POSITIVE_INFINITY; |
| Iterator<VisibilityPassSpacecraftPosition> it = getPositions().iterator(); |
| while (it.hasNext()) { |
| VisibilityPassSpacecraftPosition p = it.next(); |
| if (p.getRange() < closestRange) { |
| closestRange = p.getRange(); |
| smallestRangePosition = p; |
| } |
| } |
| } |
| |
| return smallestRangePosition; |
| } |
| |
| @Override |
| public VisibilityPassSpacecraftPosition getHighestElevationPosition() { |
| VisibilityPassSpacecraftPosition higestElevationPosition = null; |
| |
| if (!getPositions().isEmpty()) { |
| // Finds the position with the highest elevation. |
| double highestElevation = Double.NEGATIVE_INFINITY; |
| Iterator<VisibilityPassSpacecraftPosition> it = getPositions().iterator(); |
| while (it.hasNext()) { |
| VisibilityPassSpacecraftPosition p = it.next(); |
| if (p.getElevation() > highestElevation) { |
| highestElevation = p.getElevation(); |
| higestElevationPosition = p; |
| } |
| } |
| } |
| |
| return higestElevationPosition; |
| } |
| |
| @Override |
| public VisibilityPassSpacecraftPosition getSmallestSpacecraftCrossTrackAnglePosition() { |
| VisibilityPassSpacecraftPosition smallestSpacecraftCrossTrackAnglePosition = null; |
| |
| if (!getPositions().isEmpty()) { |
| // Finds the position with the smallest cross-track angle. |
| double smallestCrossTrackAngle = Double.MAX_VALUE; |
| Iterator<VisibilityPassSpacecraftPosition> it = getPositions().iterator(); |
| while (it.hasNext()) { |
| VisibilityPassSpacecraftPosition p = it.next(); |
| double crossTrackAngle = Math.abs(p.getSpacecraftCrossTrackAngle()); |
| if (crossTrackAngle < smallestCrossTrackAngle) { |
| smallestCrossTrackAngle = crossTrackAngle; |
| smallestSpacecraftCrossTrackAnglePosition = p; |
| } |
| } |
| } |
| |
| return smallestSpacecraftCrossTrackAnglePosition; |
| } |
| |
| @Override |
| public void updateHistory() { |
| getPositions().clear(); |
| |
| // Computes the pass history |
| List<VisibilityPassSpacecraftPosition> pos = getVisibilityPassPositionHistory(getVisibilityPass(), |
| getTimeInterval()); |
| |
| getPositions().addAll(pos); |
| } |
| |
| protected List<VisibilityPassSpacecraftPosition> getVisibilityPassPositionHistory(VisibilityPass pass, |
| double timeInterval) { |
| List<VisibilityPassSpacecraftPosition> positions = new ArrayList<VisibilityPassSpacecraftPosition>(); |
| |
| try { |
| Propagator propagator = pass.getOrbitModel().getOreKitPropagator(); |
| Frame inertialFrame = FramesFactory.getEME2000(); |
| LocalOrbitalFrame lof = new LocalOrbitalFrame(inertialFrame, LOFType.QSW, propagator, "QSW"); |
| GeographicCoordinates coord = pass.getSurfaceLocation(); |
| GeodeticPoint location = new GeodeticPoint(coord.getLatitude(), coord.getLongitude(), coord.getElevation()); |
| Frame earthFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, true); |
| BodyShape earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, |
| Constants.WGS84_EARTH_FLATTENING, earthFrame); |
| TopocentricFrame loc = new TopocentricFrame(earth, location, "location"); |
| |
| TimeScale utc = TimeScalesFactory.getUTC(); |
| AbsoluteDate startAbsoluteDate = ApogyCoreEnvironmentOrbitEarthFacade.INSTANCE |
| .createAbsoluteDate(pass.getStartTime()); |
| AbsoluteDate endAbsoluteDate = new AbsoluteDate(startAbsoluteDate, pass.getDuration()); |
| AbsoluteDate extrapDate = startAbsoluteDate; |
| |
| while (extrapDate.compareTo(endAbsoluteDate) <= 0) { |
| // We can simply get the position and velocity of spacecraft in |
| // LOC frame at any |
| // time |
| PVCoordinates pv = lof.getTransformTo(loc, extrapDate).transformPVCoordinates(PVCoordinates.ZERO); |
| |
| // Calculate the range. |
| double range = pv.getPosition().getNorm(); |
| |
| // Calculate the range rate. |
| double rangeRate = Vector3D.dotProduct(pv.getPosition(), pv.getVelocity()) / pv.getPosition().getNorm(); |
| |
| // Calculate the azimuth. |
| double azimuth = Math.atan2(pv.getPosition().getX(), pv.getPosition().getY()); |
| |
| // Calculate the elevation. |
| double l = Math.sqrt((pv.getPosition().getX() * pv.getPosition().getX() |
| + pv.getPosition().getY() * pv.getPosition().getY())); |
| double elevation = Math.atan2(pv.getPosition().getZ(), l); |
| |
| PVCoordinates sc = lof.getTransformTo(earth.getBodyFrame(), extrapDate) |
| .transformPVCoordinates(PVCoordinates.ZERO); |
| PVCoordinates tgt = loc.getTransformTo(earth.getBodyFrame(), extrapDate) |
| .transformPVCoordinates(PVCoordinates.ZERO); |
| |
| // Spacecraft position vector |
| Vector3D p = sc.getPosition(); |
| |
| // Spacecraft velocity vector |
| Vector3D v = sc.getVelocity(); |
| |
| // Spacecraft cross track vector. |
| Vector3D crossTrack = p.crossProduct(v); |
| |
| // Target position vector. |
| Vector3D target = tgt.getPosition(); |
| |
| // Spacecraft to target vector. |
| Vector3D scToTarget = p.subtract(target); |
| |
| // Projection of scToTarget onto the orbital plane. |
| Vector3D scToTargetOntoOrbitalPlane = projectOntoPlane(scToTarget, crossTrack.normalize()); |
| |
| // Projection of scToTarget onto the crosstrack plane. |
| Vector3D scToTargetOntoCrossTrackPlane = projectOntoPlane(scToTarget, v.normalize()); |
| |
| // double scAlongTrackAngle = getAngle(p, |
| // scToTargetOntoOrbitalPlane); |
| double scAlongTrackAngle = getAlongTrackAngle(p, scToTargetOntoOrbitalPlane, crossTrack); |
| |
| // double scCrossTrackAngle = getAngle(p, |
| // scToTargetOntoCrossTrackPlane); |
| double scCrossTrackAngle = getCrossTrackAngle(p, scToTargetOntoCrossTrackPlane, v); |
| |
| VisibilityPassSpacecraftPosition position = ApogyCoreEnvironmentOrbitEarthFactory.eINSTANCE |
| .createVisibilityPassSpacecraftPosition(); |
| position.setTime(ApogyCoreEnvironmentOrbitEarthFacade.INSTANCE.createDate(extrapDate)); |
| position.setRange(range); |
| position.setRangeRate(rangeRate); |
| position.setAzimuth(azimuth); |
| position.setElevation(elevation); |
| position.setSpacecraftCrossTrackAngle(scCrossTrackAngle); |
| position.setSpacecraftAlongTrackAngle(scAlongTrackAngle); |
| |
| // Computes the position of the spacecraft. |
| GeodeticPoint scCoords = earth.transform(sc.getPosition(), earth.getBodyFrame(), extrapDate); |
| |
| GeographicCoordinates geo = ApogyEarthFacade.INSTANCE.createGeographicCoordinates( |
| scCoords.getLongitude(), scCoords.getLatitude(), scCoords.getAltitude()); |
| position.setSpacecraftCoordinates(geo); |
| positions.add(position); |
| |
| extrapDate = new AbsoluteDate(extrapDate, timeInterval, utc); |
| } |
| |
| // Start orbit propagation. |
| propagator.propagate(new AbsoluteDate(startAbsoluteDate, pass.getDuration())); |
| } catch (Throwable t) { |
| Logger.error(t.getMessage(), t); |
| } |
| |
| return positions; |
| } |
| |
| /** |
| * Computes the along track angle. |
| * |
| * @param p |
| * The position of the S/C. |
| * @param scToTargetOntoOrbitalPlane |
| * he position of the target projected onto the orbital plane. |
| * @param crossTrackVector |
| * The cross track vector. |
| * @return The along track angle. |
| */ |
| private double getAlongTrackAngle(Vector3D p, Vector3D scToTargetOntoOrbitalPlane, Vector3D crossTrackVector) { |
| // Finds the angle between the spacecraft position and the target |
| // position on |
| // the orbital plane. |
| double angle = getAngle(p, scToTargetOntoOrbitalPlane); |
| |
| // Figures out the sign of the angle. |
| Vector3D rotation = p.crossProduct(scToTargetOntoOrbitalPlane).normalize(); |
| double dotProduct = rotation.dotProduct(crossTrackVector.normalize()); |
| if (dotProduct < 0) { |
| angle = -angle; |
| } |
| |
| // return angle; |
| |
| // DEBUG |
| return Math.abs(angle); |
| |
| } |
| |
| /** |
| * Computes the cross track angle. |
| * |
| * @param p |
| * The position of the S/C. |
| * @param scToTargetOntoCrossTrackPlane |
| * The position of the target projected onto the plane |
| * perpendicular to the orbital plane. |
| * @param alongTrackVector |
| * The along track vector. |
| * @return The cross track angle. |
| */ |
| private double getCrossTrackAngle(Vector3D p, Vector3D scToTargetOntoCrossTrackPlane, Vector3D alongTrackVector) { |
| // Finds the angle between the spacecraft position and the target |
| // position on |
| // the plane perpendicular to the orbital plane. |
| double angle = getAngle(p, scToTargetOntoCrossTrackPlane); |
| |
| // Figures out the sign of the angle. |
| Vector3D rotation = p.crossProduct(scToTargetOntoCrossTrackPlane).normalize(); |
| double dotProduct = rotation.dotProduct(alongTrackVector.normalize()); |
| if (dotProduct > 0) { |
| angle = -angle; |
| } |
| |
| return angle; |
| } |
| |
| private double getAngle(Vector3D v1, Vector3D v2) { |
| double dotProduct = v1.dotProduct(v2); |
| return Math.acos(dotProduct / (v1.getNorm() * v2.getNorm())); |
| } |
| |
| private Vector3D projectOntoPlane(Vector3D u, Vector3D planeNormal) { |
| double projUOnNormalLenght = u.dotProduct(planeNormal) / (planeNormal.getNormSq()); |
| Vector3D projUOnNormal = planeNormal.scalarMultiply(projUOnNormalLenght); |
| return u.subtract(projUOnNormal); |
| } |
| |
| } // VisibilityPassSpacecraftPositionHistoryImpl |