TopocentricFrame.java

  1. /* Copyright 2002-2020 CS GROUP
  2.  * Licensed to CS GROUP (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.hipparchus.util.SinCos;
  30. import org.orekit.bodies.BodyShape;
  31. import org.orekit.bodies.FieldGeodeticPoint;
  32. import org.orekit.bodies.GeodeticPoint;
  33. import org.orekit.errors.OrekitException;
  34. import org.orekit.time.AbsoluteDate;
  35. import org.orekit.time.FieldAbsoluteDate;
  36. import org.orekit.utils.Constants;
  37. import org.orekit.utils.FieldPVCoordinates;
  38. import org.orekit.utils.PVCoordinates;
  39. import org.orekit.utils.PVCoordinatesProvider;
  40. import org.orekit.utils.TimeStampedPVCoordinates;


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  210.     }

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

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

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

  233.     }

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

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

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

  247.     }

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

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

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

  263.     }

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

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

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

  278.     }

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

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

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

  295.     }

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

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

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

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

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

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

  357. }