TopocentricFrame.java

  1. /* Copyright 2002-2018 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.analysis.UnivariateFunction;
  19. import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
  20. import org.hipparchus.analysis.solvers.UnivariateSolver;
  21. import org.hipparchus.exception.MathRuntimeException;
  22. import org.hipparchus.geometry.euclidean.threed.Rotation;
  23. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  24. import org.hipparchus.util.FastMath;
  25. import org.hipparchus.util.MathUtils;
  26. import org.orekit.bodies.BodyShape;
  27. import org.orekit.bodies.GeodeticPoint;
  28. import org.orekit.errors.OrekitException;
  29. import org.orekit.errors.OrekitExceptionWrapper;
  30. import org.orekit.time.AbsoluteDate;
  31. import org.orekit.utils.Constants;
  32. import org.orekit.utils.PVCoordinates;
  33. import org.orekit.utils.PVCoordinatesProvider;
  34. import org.orekit.utils.TimeStampedPVCoordinates;


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

  51.     /** Serializable UID. */
  52.     private static final long serialVersionUID = -5997915708080966466L;

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

  55.     /** Point where the topocentric frame is defined. */
  56.     private final GeodeticPoint point;

  57.     /** Simple constructor.
  58.      * @param parentShape body shape on which the local point is defined
  59.      * @param point local surface point where topocentric frame is defined
  60.      * @param name the string representation
  61.      */
  62.     public TopocentricFrame(final BodyShape parentShape, final GeodeticPoint point,
  63.                             final String name) {

  64.         super(parentShape.getBodyFrame(),
  65.               new Transform(AbsoluteDate.J2000_EPOCH,
  66.                             new Transform(AbsoluteDate.J2000_EPOCH,
  67.                                           parentShape.transform(point).negate()),
  68.                             new Transform(AbsoluteDate.J2000_EPOCH,
  69.                                           new Rotation(point.getEast(), point.getZenith(),
  70.                                                        Vector3D.PLUS_I, Vector3D.PLUS_K),
  71.                                           Vector3D.ZERO)),
  72.               name, false);
  73.         this.parentShape = parentShape;
  74.         this.point = point;
  75.     }

  76.     /** Get the body shape on which the local point is defined.
  77.      * @return body shape on which the local point is defined
  78.      */
  79.     public BodyShape getParentShape() {
  80.         return parentShape;
  81.     }

  82.     /** Get the surface point defining the origin of the frame.
  83.      * @return surface point defining the origin of the frame
  84.      */
  85.     public GeodeticPoint getPoint() {
  86.         return point;
  87.     }

  88.     /** Get the zenith direction of topocentric frame, expressed in parent shape frame.
  89.      * <p>The zenith direction is defined as the normal to local horizontal plane.</p>
  90.      * @return unit vector in the zenith direction
  91.      * @see #getNadir()
  92.      */
  93.     public Vector3D getZenith() {
  94.         return point.getZenith();
  95.     }

  96.     /** Get the nadir direction of topocentric frame, expressed in parent shape frame.
  97.      * <p>The nadir direction is the opposite of zenith direction.</p>
  98.      * @return unit vector in the nadir direction
  99.      * @see #getZenith()
  100.      */
  101.     public Vector3D getNadir() {
  102.         return point.getNadir();
  103.     }

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

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

  121.     /** Get the east direction of topocentric frame, expressed in parent shape frame.
  122.      * <p>The east direction is defined in the horizontal plane
  123.      * in order to complete direct triangle (east, north, zenith).</p>
  124.      * @return unit vector in the east direction
  125.      * @see #getWest()
  126.      */
  127.     public Vector3D getEast() {
  128.         return point.getEast();
  129.     }

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

  138.     /** Get the elevation of a point with regards to the local point.
  139.      * <p>The elevation is the angle between the local horizontal and
  140.      * the direction from local point to given point.</p>
  141.      * @param extPoint point for which elevation shall be computed
  142.      * @param frame frame in which the point is defined
  143.      * @param date computation date
  144.      * @return elevation of the point
  145.      * @exception OrekitException if frames transformations cannot be computed
  146.      */
  147.     public double getElevation(final Vector3D extPoint, final Frame frame,
  148.                                final AbsoluteDate date)
  149.         throws OrekitException {

  150.         // Transform given point from given frame to topocentric frame
  151.         final Transform t = frame.getTransformTo(this, date);
  152.         final Vector3D extPointTopo = t.transformPosition(extPoint);

  153.         // Elevation angle is PI/2 - angle between zenith and given point direction
  154.         return extPointTopo.getDelta();
  155.     }

  156.     /** Get the azimuth of a point with regards to the topocentric frame center point.
  157.      * <p>The azimuth is the angle between the North direction at local point and
  158.      * the projection in local horizontal plane of the direction from local point
  159.      * to given point. Azimuth angles are counted clockwise, i.e positive towards the East.</p>
  160.      * @param extPoint point for which elevation shall be computed
  161.      * @param frame frame in which the point is defined
  162.      * @param date computation date
  163.      * @return azimuth of the point
  164.      * @exception OrekitException if frames transformations cannot be computed
  165.      */
  166.     public double getAzimuth(final Vector3D extPoint, final Frame frame,
  167.                              final AbsoluteDate date)
  168.         throws OrekitException {

  169.         // Transform given point from given frame to topocentric frame
  170.         final Transform t = getTransformTo(frame, date).getInverse();
  171.         final Vector3D extPointTopo = t.transformPosition(extPoint);

  172.         // Compute azimuth
  173.         double azimuth = FastMath.atan2(extPointTopo.getX(), extPointTopo.getY());
  174.         if (azimuth < 0.) {
  175.             azimuth += MathUtils.TWO_PI;
  176.         }
  177.         return azimuth;

  178.     }

  179.     /** Get the range of a point with regards to the topocentric frame center point.
  180.      * @param extPoint point for which range shall be computed
  181.      * @param frame frame in which the point is defined
  182.      * @param date computation date
  183.      * @return range (distance) of the point
  184.      * @exception OrekitException if frames transformations cannot be computed
  185.      */
  186.     public double getRange(final Vector3D extPoint, final Frame frame,
  187.                            final AbsoluteDate date)
  188.         throws OrekitException {

  189.         // Transform given point from given frame to topocentric frame
  190.         final Transform t = frame.getTransformTo(this, date);
  191.         final Vector3D extPointTopo = t.transformPosition(extPoint);

  192.         // Compute range
  193.         return extPointTopo.getNorm();

  194.     }

  195.     /** Get the range rate of a point with regards to the topocentric frame center point.
  196.      * @param extPV point/velocity for which range rate shall be computed
  197.      * @param frame frame in which the point is defined
  198.      * @param date computation date
  199.      * @return range rate of the point (positive if point departs from frame)
  200.      * @exception OrekitException if frames transformations cannot be computed
  201.      */
  202.     public double getRangeRate(final PVCoordinates extPV, final Frame frame,
  203.                                final AbsoluteDate date)
  204.         throws OrekitException {

  205.         // Transform given point from given frame to topocentric frame
  206.         final Transform t = frame.getTransformTo(this, date);
  207.         final PVCoordinates extPVTopo = t.transformPVCoordinates(extPV);

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

  211.     }

  212.     /**
  213.      * Compute the limit visibility point for a satellite in a given direction.
  214.      * <p>
  215.      * This method can be used to compute visibility circles around ground stations
  216.      * for example, using a simple loop on azimuth, with either a fixed elevation
  217.      * or an elevation that depends on azimuth to take ground masks into account.
  218.      * </p>
  219.      * @param radius satellite distance to Earth center
  220.      * @param azimuth pointing azimuth from station
  221.      * @param elevation pointing elevation from station
  222.      * @return limit visibility point for the satellite
  223.      * @throws OrekitException if point cannot be found
  224.      */
  225.     public GeodeticPoint computeLimitVisibilityPoint(final double radius,
  226.                                                      final double azimuth, final double elevation)
  227.         throws OrekitException {
  228.         try {
  229.             // convergence threshold on point position: 1mm
  230.             final double deltaP = 0.001;
  231.             final UnivariateSolver solver =
  232.                     new BracketingNthOrderBrentSolver(deltaP / Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
  233.                                                       deltaP, deltaP, 5);

  234.             // find the distance such that a point in the specified direction and at the solved-for
  235.             // distance is exactly at the specified radius
  236.             final double distance = solver.solve(1000, new UnivariateFunction() {
  237.                 /** {@inheritDoc} */
  238.                 public double value(final double x) {
  239.                     try {
  240.                         final GeodeticPoint gp = pointAtDistance(azimuth, elevation, x);
  241.                         return parentShape.transform(gp).getNorm() - radius;
  242.                     } catch (OrekitException oe) {
  243.                         throw new OrekitExceptionWrapper(oe);
  244.                     }
  245.                 }
  246.             }, 0, 2 * radius);

  247.             // return the limit point
  248.             return pointAtDistance(azimuth, elevation, distance);

  249.         } catch (MathRuntimeException mrte) {
  250.             throw new OrekitException(mrte);
  251.         } catch (OrekitExceptionWrapper lwe) {
  252.             throw lwe.getException();
  253.         }
  254.     }

  255.     /** Compute the point observed from the station at some specified distance.
  256.      * @param azimuth pointing azimuth from station
  257.      * @param elevation pointing elevation from station
  258.      * @param distance distance to station
  259.      * @return observed point
  260.      * @exception OrekitException if point cannot be computed
  261.      */
  262.     public GeodeticPoint pointAtDistance(final double azimuth, final double elevation,
  263.                                          final double distance)
  264.         throws OrekitException {
  265.         final double cosAz = FastMath.cos(azimuth);
  266.         final double sinAz = FastMath.sin(azimuth);
  267.         final double cosEl = FastMath.cos(elevation);
  268.         final double sinEl = FastMath.sin(elevation);
  269.         final Vector3D  observed = new Vector3D(distance * cosEl * sinAz,
  270.                                                 distance * cosEl * cosAz,
  271.                                                 distance * sinEl);
  272.         return parentShape.transform(observed, this, AbsoluteDate.J2000_EPOCH);
  273.     }

  274.     /** Get the {@link PVCoordinates} of the topocentric frame origin in the selected frame.
  275.      * @param date current date
  276.      * @param frame the frame where to define the position
  277.      * @return position/velocity of the topocentric frame origin (m and m/s)
  278.      * @exception OrekitException if position cannot be computed in given frame
  279.      */
  280.     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame)
  281.         throws OrekitException {
  282.         return getTransformTo(frame, date).transformPVCoordinates(new TimeStampedPVCoordinates(date,
  283.                                                                                                Vector3D.ZERO,
  284.                                                                                                Vector3D.ZERO,
  285.                                                                                                Vector3D.ZERO));
  286.     }

  287. }