RoughVisibilityEstimator.java

  1. /* Copyright 2013-2022 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.rugged.utils;

  18. import java.util.ArrayList;
  19. import java.util.List;

  20. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  21. import org.hipparchus.util.FastMath;
  22. import org.orekit.bodies.GeodeticPoint;
  23. import org.orekit.bodies.OneAxisEllipsoid;
  24. import org.orekit.frames.Frame;
  25. import org.orekit.frames.Transform;
  26. import org.orekit.time.AbsoluteDate;
  27. import org.orekit.utils.TimeStampedPVCoordinates;

  28. /** Class estimating very roughly when a point may be visible from spacecraft.
  29.  * <p>
  30.  * The class only uses spacecraft position to compute a very rough sub-satellite
  31.  * point. It assumes the position-velocities are regular enough and without holes.
  32.  * It is intended only only has a quick estimation in order to set up search
  33.  * boundaries in inverse location.
  34.  * </p>
  35.  * @see org.orekit.rugged.api.Rugged#dateLocation(String, org.orekit.bodies.GeodeticPoint, int, int)
  36.  * @see org.orekit.rugged.api.Rugged#dateLocation(String, double, double, int, int)
  37.  * @see org.orekit.rugged.api.Rugged#inverseLocation(String, org.orekit.bodies.GeodeticPoint, int, int)
  38.  * @see org.orekit.rugged.api.Rugged#inverseLocation(String, double, double, int, int)
  39.  * @author Luc Maisonobe
  40.  */
  41. public class RoughVisibilityEstimator {

  42.     /** Ground ellipsoid. */
  43.     private final OneAxisEllipsoid ellipsoid;

  44.     /** Sub-satellite point. */
  45.     private final List<TimeStampedPVCoordinates> pvGround;

  46.     /** Mean angular rate with respect to position-velocity indices. */
  47.     private final double rateVSIndices;

  48.     /** Mean angular rate with respect to time. */
  49.     private final double rateVSTime;

  50.     /** Last found index. */
  51.     private int last;

  52.     /**
  53.      * Simple constructor.
  54.      * @param ellipsoid ground ellipsoid
  55.      * @param frame frame in which position and velocity are defined (may be inertial or body frame)
  56.      * @param positionsVelocities satellite position and velocity (m and m/s in specified frame)
  57.      */
  58.     public RoughVisibilityEstimator(final OneAxisEllipsoid ellipsoid, final Frame frame,
  59.                                     final List<TimeStampedPVCoordinates> positionsVelocities) {

  60.         this.ellipsoid = ellipsoid;

  61.         // project spacecraft position-velocity to ground
  62.         final Frame bodyFrame = ellipsoid.getBodyFrame();
  63.         final int n = positionsVelocities.size();
  64.         this.pvGround = new ArrayList<>(n);
  65.         for (final TimeStampedPVCoordinates pv : positionsVelocities) {
  66.             final Transform t = frame.getTransformTo(bodyFrame, pv.getDate());
  67.             pvGround.add(ellipsoid.projectToGround(t.transformPVCoordinates(pv), bodyFrame));
  68.         }

  69.         // initialize first search at mid point
  70.         this.last = n / 2;

  71.         // estimate mean angular rate with respect to indices
  72.         double alpha = 0;
  73.         for (int i = 0; i < n - 1; ++i) {
  74.             // angular motion between points i and i+1
  75.             alpha += Vector3D.angle(pvGround.get(i).getPosition(),
  76.                                     pvGround.get(i + 1).getPosition());
  77.         }
  78.         this.rateVSIndices = alpha / n;

  79.         // estimate mean angular rate with respect to time
  80.         final AbsoluteDate firstDate = pvGround.get(0).getDate();
  81.         final AbsoluteDate lastDate  = pvGround.get(pvGround.size() - 1).getDate();
  82.         this.rateVSTime              = alpha / lastDate.durationFrom(firstDate);

  83.     }

  84.     /** Estimate <em>very roughly</em> when spacecraft comes close to a ground point.
  85.      * @param groundPoint ground point to check
  86.      * @return rough date at which spacecraft comes close to ground point (never null,
  87.      * but may be really far from reality if ground point is away from trajectory)
  88.      */
  89.     public AbsoluteDate estimateVisibility(final GeodeticPoint groundPoint) {

  90.         final Vector3D point = ellipsoid.transform(groundPoint);
  91.         int closeIndex = findClose(last, point);

  92.         // check if there are closer points in previous periods
  93.         final int repeat = (int) FastMath.rint(2.0 * FastMath.PI / rateVSIndices);
  94.         for (int index = closeIndex - repeat; index > 0; index -= repeat) {
  95.             final int otherIndex = findClose(index, point);
  96.             if (otherIndex != closeIndex &&
  97.                 Vector3D.distance(pvGround.get(otherIndex).getPosition(), point) <
  98.                 Vector3D.distance(pvGround.get(closeIndex).getPosition(), point)) {
  99.                 closeIndex = otherIndex;
  100.             }
  101.         }

  102.         // check if there are closer points in next periods
  103.         for (int index = closeIndex + repeat; index < pvGround.size(); index += repeat) {
  104.             final int otherIndex = findClose(index, point);
  105.             if (otherIndex != closeIndex &&
  106.                 Vector3D.distance(pvGround.get(otherIndex).getPosition(), point) <
  107.                 Vector3D.distance(pvGround.get(closeIndex).getPosition(), point)) {
  108.                 closeIndex = otherIndex;
  109.             }
  110.         }

  111.         // we have found the closest sub-satellite point index
  112.         last = closeIndex;

  113.         // final adjustment
  114.         final TimeStampedPVCoordinates closest = pvGround.get(closeIndex);
  115.         final double alpha = neededMotion(closest, point);
  116.         return closest.getDate().shiftedBy(alpha / rateVSTime);

  117.     }

  118.     /** Find the index of a close sub-satellite point.
  119.      * @param start start index for the search
  120.      * @param point test point
  121.      * @return index of a sub-satellite point close to the test point
  122.      */
  123.     private int findClose(final int start, final Vector3D point) {
  124.         int current  = start;
  125.         int previous = Integer.MIN_VALUE;
  126.         int maxLoop  = 1000;
  127.         while (maxLoop-- > 0 && FastMath.abs(current - previous) > 1) {
  128.             previous = current;
  129.             final double alpha = neededMotion(pvGround.get(current), point);
  130.             final int    shift = (int) FastMath.rint(alpha / rateVSIndices);
  131.             current = FastMath.max(0, FastMath.min(pvGround.size() - 1, current + shift));
  132.         }
  133.         return current;
  134.     }

  135.     /** Estimate angular motion needed to go past test point.
  136.      * <p>
  137.      * This estimation is quite crude. The sub-satellite point is properly on the
  138.      * ellipsoid surface, but we compute the angle assuming a spherical shape.
  139.      * </p>
  140.      * @param subSatellite current sub-satellite position-velocity
  141.      * @param point test point
  142.      * @return angular motion to go past test point (positive is
  143.      * test point is ahead, negative if it is behind)
  144.      */
  145.     private double neededMotion(final TimeStampedPVCoordinates subSatellite,
  146.                                 final Vector3D point) {

  147.         final Vector3D ssP      = subSatellite.getPosition();
  148.         final Vector3D momentum = subSatellite.getMomentum();
  149.         final double   y        = Vector3D.dotProduct(point, Vector3D.crossProduct(momentum, ssP).normalize());
  150.         final double   x        = Vector3D.dotProduct(point, ssP.normalize());

  151.         return FastMath.atan2(y, x);

  152.     }

  153. }