TopocentricFrame.java

  1. /* Copyright 2002-2019 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.frames;

  18. import org.hipparchus.Field;
  19. import org.hipparchus.RealFieldElement;
  20. import org.hipparchus.analysis.UnivariateFunction;
  21. import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
  22. import org.hipparchus.analysis.solvers.UnivariateSolver;
  23. import org.hipparchus.exception.MathRuntimeException;
  24. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  25. import org.hipparchus.geometry.euclidean.threed.Rotation;
  26. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  27. import org.hipparchus.util.FastMath;
  28. import org.hipparchus.util.MathUtils;
  29. import org.orekit.bodies.BodyShape;
  30. import org.orekit.bodies.FieldGeodeticPoint;
  31. import org.orekit.bodies.GeodeticPoint;
  32. import org.orekit.errors.OrekitException;
  33. import org.orekit.time.AbsoluteDate;
  34. import org.orekit.time.FieldAbsoluteDate;
  35. import org.orekit.utils.Constants;
  36. import org.orekit.utils.FieldPVCoordinates;
  37. import org.orekit.utils.PVCoordinates;
  38. import org.orekit.utils.PVCoordinatesProvider;
  39. import org.orekit.utils.TimeStampedPVCoordinates;


  40. /** Topocentric frame.
  41.  * <p>Frame associated to a position near the surface of a body shape.</p>
  42.  * <p>
  43.  * The origin of the frame is at the defining {@link GeodeticPoint geodetic point}
  44.  * location, and the right-handed canonical trihedra is:
  45.  * </p>
  46.  * <ul>
  47.  *   <li>X axis in the local horizontal plane (normal to zenith direction) and
  48.  *   following the local parallel towards East</li>
  49.  *   <li>Y axis in the horizontal plane (normal to zenith direction) and
  50.  *   following the local meridian towards North</li>
  51.  *   <li>Z axis towards Zenith direction</li>
  52.  * </ul>
  53.  * @author V&eacute;ronique Pommier-Maurussane
  54.  */
  55. public class TopocentricFrame extends Frame implements PVCoordinatesProvider {

  56.     /** Serializable UID. */
  57.     private static final long serialVersionUID = -5997915708080966466L;

  58.     /** Body shape on which the local point is defined. */
  59.     private final BodyShape parentShape;

  60.     /** Point where the topocentric frame is defined. */
  61.     private final GeodeticPoint point;

  62.     /** Simple constructor.
  63.      * @param parentShape body shape on which the local point is defined
  64.      * @param point local surface point where topocentric frame is defined
  65.      * @param name the string representation
  66.      */
  67.     public TopocentricFrame(final BodyShape parentShape, final GeodeticPoint point,
  68.                             final String name) {

  69.         super(parentShape.getBodyFrame(),
  70.               new Transform(AbsoluteDate.J2000_EPOCH,
  71.                             new Transform(AbsoluteDate.J2000_EPOCH,
  72.                                           parentShape.transform(point).negate()),
  73.                             new Transform(AbsoluteDate.J2000_EPOCH,
  74.                                           new Rotation(point.getEast(), point.getZenith(),
  75.                                                        Vector3D.PLUS_I, Vector3D.PLUS_K),
  76.                                           Vector3D.ZERO)),
  77.               name, false);
  78.         this.parentShape = parentShape;
  79.         this.point = point;
  80.     }

  81.     /** Get the body shape on which the local point is defined.
  82.      * @return body shape on which the local point is defined
  83.      */
  84.     public BodyShape getParentShape() {
  85.         return parentShape;
  86.     }

  87.     /** Get the surface point defining the origin of the frame.
  88.      * @return surface point defining the origin of the frame
  89.      */
  90.     public GeodeticPoint getPoint() {
  91.         return point;
  92.     }

  93.     /** Get the surface point defining the origin of the frame.
  94.      * @param <T> tyoe of the elements
  95.      * @param field of the elements
  96.      * @return surface point defining the origin of the frame
  97.      * @since 9.3
  98.      */
  99.     public <T extends RealFieldElement<T>> FieldGeodeticPoint<T> getPoint(final Field<T> field) {
  100.         final T zero = field.getZero();
  101.         return new FieldGeodeticPoint<>(zero.add(point.getLatitude()),
  102.                                         zero.add(point.getLongitude()),
  103.                                         zero.add(point.getAltitude()));
  104.     }

  105.     /** Get the zenith direction of topocentric frame, expressed in parent shape frame.
  106.      * <p>The zenith direction is defined as the normal to local horizontal plane.</p>
  107.      * @return unit vector in the zenith direction
  108.      * @see #getNadir()
  109.      */
  110.     public Vector3D getZenith() {
  111.         return point.getZenith();
  112.     }

  113.     /** Get the nadir direction of topocentric frame, expressed in parent shape frame.
  114.      * <p>The nadir direction is the opposite of zenith direction.</p>
  115.      * @return unit vector in the nadir direction
  116.      * @see #getZenith()
  117.      */
  118.     public Vector3D getNadir() {
  119.         return point.getNadir();
  120.     }

  121.    /** Get the north direction of topocentric frame, expressed in parent shape frame.
  122.      * <p>The north direction is defined in the horizontal plane
  123.      * (normal to zenith direction) and following the local meridian.</p>
  124.      * @return unit vector in the north direction
  125.      * @see #getSouth()
  126.      */
  127.     public Vector3D getNorth() {
  128.         return point.getNorth();
  129.     }

  130.     /** Get the south direction of topocentric frame, expressed in parent shape frame.
  131.      * <p>The south direction is the opposite of north direction.</p>
  132.      * @return unit vector in the south direction
  133.      * @see #getNorth()
  134.      */
  135.     public Vector3D getSouth() {
  136.         return point.getSouth();
  137.     }

  138.     /** Get the east direction of topocentric frame, expressed in parent shape frame.
  139.      * <p>The east direction is defined in the horizontal plane
  140.      * in order to complete direct triangle (east, north, zenith).</p>
  141.      * @return unit vector in the east direction
  142.      * @see #getWest()
  143.      */
  144.     public Vector3D getEast() {
  145.         return point.getEast();
  146.     }

  147.     /** Get the west direction of topocentric frame, expressed in parent shape frame.
  148.      * <p>The west direction is the opposite of east direction.</p>
  149.      * @return unit vector in the west direction
  150.      * @see #getEast()
  151.      */
  152.     public Vector3D getWest() {
  153.         return point.getWest();
  154.     }

  155.     /** Get the elevation of a point with regards to the local point.
  156.      * <p>The elevation is the angle between the local horizontal and
  157.      * the direction from local point to given point.</p>
  158.      * @param extPoint point for which elevation shall be computed
  159.      * @param frame frame in which the point is defined
  160.      * @param date computation date
  161.      * @return elevation of the point
  162.      */
  163.     public double getElevation(final Vector3D extPoint, final Frame frame,
  164.                                final AbsoluteDate date) {

  165.         // Transform given point from given frame to topocentric frame
  166.         final Transform t = frame.getTransformTo(this, date);
  167.         final Vector3D extPointTopo = t.transformPosition(extPoint);

  168.         // Elevation angle is PI/2 - angle between zenith and given point direction
  169.         return extPointTopo.getDelta();
  170.     }

  171.     /** Get the elevation of a point with regards to the local point.
  172.      * <p>The elevation is the angle between the local horizontal and
  173.      * the direction from local point to given point.</p>
  174.      * @param <T> type of the elements
  175.      * @param extPoint point for which elevation shall be computed
  176.      * @param frame frame in which the point is defined
  177.      * @param date computation date
  178.      * @return elevation of the point
  179.      * @since 9.3
  180.      */
  181.     public <T extends RealFieldElement<T>> T getElevation(final FieldVector3D<T> extPoint, final Frame frame,
  182.                                                           final FieldAbsoluteDate<T> date) {

  183.         // Transform given point from given frame to topocentric frame
  184.         final FieldTransform<T> t = frame.getTransformTo(this, date);
  185.         final FieldVector3D<T> extPointTopo = t.transformPosition(extPoint);

  186.         // Elevation angle is PI/2 - angle between zenith and given point direction
  187.         return extPointTopo.getDelta();
  188.     }

  189.     /** Get the azimuth of a point with regards to the topocentric frame center point.
  190.      * <p>The azimuth is the angle between the North direction at local point and
  191.      * the projection in local horizontal plane of the direction from local point
  192.      * to given point. Azimuth angles are counted clockwise, i.e positive towards the East.</p>
  193.      * @param extPoint point for which elevation shall be computed
  194.      * @param frame frame in which the point is defined
  195.      * @param date computation date
  196.      * @return azimuth of the point
  197.      */
  198.     public double getAzimuth(final Vector3D extPoint, final Frame frame,
  199.                              final AbsoluteDate date) {

  200.         // Transform given point from given frame to topocentric frame
  201.         final Transform t = getTransformTo(frame, date).getInverse();
  202.         final Vector3D extPointTopo = t.transformPosition(extPoint);

  203.         // Compute azimuth
  204.         double azimuth = FastMath.atan2(extPointTopo.getX(), extPointTopo.getY());
  205.         if (azimuth < 0.) {
  206.             azimuth += MathUtils.TWO_PI;
  207.         }
  208.         return azimuth;

  209.     }

  210.     /** Get the azimuth of a point with regards to the topocentric frame center point.
  211.      * <p>The azimuth is the angle between the North direction at local point and
  212.      * the projection in local horizontal plane of the direction from local point
  213.      * to given point. Azimuth angles are counted clockwise, i.e positive towards the East.</p>
  214.      * @param <T> type of the elements
  215.      * @param extPoint point for which elevation shall be computed
  216.      * @param frame frame in which the point is defined
  217.      * @param date computation date
  218.      * @return azimuth of the point
  219.      * @since 9.3
  220.      */
  221.     public <T extends RealFieldElement<T>> T getAzimuth(final FieldVector3D<T> extPoint, final Frame frame,
  222.                                                         final FieldAbsoluteDate<T> date) {

  223.         // Transform given point from given frame to topocentric frame
  224.         final FieldTransform<T> t = getTransformTo(frame, date).getInverse();
  225.         final FieldVector3D<T> extPointTopo = t.transformPosition(extPoint);

  226.         // Compute azimuth
  227.         T azimuth = FastMath.atan2(extPointTopo.getX(), extPointTopo.getY());
  228.         if (azimuth.getReal() < 0.) {
  229.             azimuth = azimuth.add(MathUtils.TWO_PI);
  230.         }
  231.         return azimuth;

  232.     }

  233.     /** Get the range of a point with regards to the topocentric frame center point.
  234.      * @param extPoint point for which range shall be computed
  235.      * @param frame frame in which the point is defined
  236.      * @param date computation date
  237.      * @return range (distance) of the point
  238.      */
  239.     public double getRange(final Vector3D extPoint, final Frame frame,
  240.                            final AbsoluteDate date) {

  241.         // Transform given point from given frame to topocentric frame
  242.         final Transform t = frame.getTransformTo(this, date);
  243.         final Vector3D extPointTopo = t.transformPosition(extPoint);

  244.         // Compute range
  245.         return extPointTopo.getNorm();

  246.     }

  247.     /** Get the range of a point with regards to the topocentric frame center point.
  248.      * @param <T> type of the elements
  249.      * @param extPoint point for which range shall be computed
  250.      * @param frame frame in which the point is defined
  251.      * @param date computation date
  252.      * @return range (distance) of the point
  253.      * @since 9.3
  254.      */
  255.     public <T extends RealFieldElement<T>> T getRange(final FieldVector3D<T> extPoint, final Frame frame,
  256.                                                       final FieldAbsoluteDate<T> date) {

  257.         // Transform given point from given frame to topocentric frame
  258.         final FieldTransform<T> t = frame.getTransformTo(this, date);
  259.         final FieldVector3D<T> extPointTopo = t.transformPosition(extPoint);

  260.         // Compute range
  261.         return extPointTopo.getNorm();

  262.     }

  263.     /** Get the range rate of a point with regards to the topocentric frame center point.
  264.      * @param extPV point/velocity for which range rate shall be computed
  265.      * @param frame frame in which the point is defined
  266.      * @param date computation date
  267.      * @return range rate of the point (positive if point departs from frame)
  268.      */
  269.     public double getRangeRate(final PVCoordinates extPV, final Frame frame,
  270.                                final AbsoluteDate date) {

  271.         // Transform given point from given frame to topocentric frame
  272.         final Transform t = frame.getTransformTo(this, date);
  273.         final PVCoordinates extPVTopo = t.transformPVCoordinates(extPV);

  274.         // Compute range rate (doppler) : relative rate along the line of sight
  275.         return Vector3D.dotProduct(extPVTopo.getPosition(), extPVTopo.getVelocity()) /
  276.                extPVTopo.getPosition().getNorm();

  277.     }

  278.     /** Get the range rate of a point with regards to the topocentric frame center point.
  279.      * @param <T> type of the elements
  280.      * @param extPV point/velocity for which range rate shall be computed
  281.      * @param frame frame in which the point is defined
  282.      * @param date computation date
  283.      * @return range rate of the point (positive if point departs from frame)
  284.      * @since 9.3
  285.      */
  286.     public <T extends RealFieldElement<T>> T getRangeRate(final FieldPVCoordinates<T> extPV, final Frame frame,
  287.                                                           final FieldAbsoluteDate<T> date) {

  288.         // Transform given point from given frame to topocentric frame
  289.         final FieldTransform<T> t = frame.getTransformTo(this, date);
  290.         final FieldPVCoordinates<T> extPVTopo = t.transformPVCoordinates(extPV);

  291.         // Compute range rate (doppler) : relative rate along the line of sight
  292.         return FieldVector3D.dotProduct(extPVTopo.getPosition(), extPVTopo.getVelocity()).divide(
  293.                extPVTopo.getPosition().getNorm());

  294.     }

  295.     /**
  296.      * Compute the limit visibility point for a satellite in a given direction.
  297.      * <p>
  298.      * This method can be used to compute visibility circles around ground stations
  299.      * for example, using a simple loop on azimuth, with either a fixed elevation
  300.      * or an elevation that depends on azimuth to take ground masks into account.
  301.      * </p>
  302.      * @param radius satellite distance to Earth center
  303.      * @param azimuth pointing azimuth from station
  304.      * @param elevation pointing elevation from station
  305.      * @return limit visibility point for the satellite
  306.      */
  307.     public GeodeticPoint computeLimitVisibilityPoint(final double radius,
  308.                                                      final double azimuth, final double elevation) {
  309.         try {
  310.             // convergence threshold on point position: 1mm
  311.             final double deltaP = 0.001;
  312.             final UnivariateSolver solver =
  313.                     new BracketingNthOrderBrentSolver(deltaP / Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
  314.                                                       deltaP, deltaP, 5);

  315.             // find the distance such that a point in the specified direction and at the solved-for
  316.             // distance is exactly at the specified radius
  317.             final double distance = solver.solve(1000, new UnivariateFunction() {
  318.                 /** {@inheritDoc} */
  319.                 public double value(final double x) {
  320.                     final GeodeticPoint gp = pointAtDistance(azimuth, elevation, x);
  321.                     return parentShape.transform(gp).getNorm() - radius;
  322.                 }
  323.             }, 0, 2 * radius);

  324.             // return the limit point
  325.             return pointAtDistance(azimuth, elevation, distance);

  326.         } catch (MathRuntimeException mrte) {
  327.             throw new OrekitException(mrte);
  328.         }
  329.     }

  330.     /** Compute the point observed from the station at some specified distance.
  331.      * @param azimuth pointing azimuth from station
  332.      * @param elevation pointing elevation from station
  333.      * @param distance distance to station
  334.      * @return observed point
  335.      */
  336.     public GeodeticPoint pointAtDistance(final double azimuth, final double elevation,
  337.                                          final double distance) {
  338.         final double cosAz = FastMath.cos(azimuth);
  339.         final double sinAz = FastMath.sin(azimuth);
  340.         final double cosEl = FastMath.cos(elevation);
  341.         final double sinEl = FastMath.sin(elevation);
  342.         final Vector3D  observed = new Vector3D(distance * cosEl * sinAz,
  343.                                                 distance * cosEl * cosAz,
  344.                                                 distance * sinEl);
  345.         return parentShape.transform(observed, this, AbsoluteDate.J2000_EPOCH);
  346.     }

  347.     /** Get the {@link PVCoordinates} of the topocentric frame origin in the selected frame.
  348.      * @param date current date
  349.      * @param frame the frame where to define the position
  350.      * @return position/velocity of the topocentric frame origin (m and m/s)
  351.      */
  352.     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
  353.         return getTransformTo(frame, date).transformPVCoordinates(new TimeStampedPVCoordinates(date,
  354.                                                                                                Vector3D.ZERO,
  355.                                                                                                Vector3D.ZERO,
  356.                                                                                                Vector3D.ZERO));
  357.     }

  358. }