SmoothFieldOfView.java

  1. /* Copyright 2002-2024 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.geometry.fov;

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

  20. import org.hipparchus.geometry.euclidean.threed.Line;
  21. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  22. import org.hipparchus.util.FastMath;
  23. import org.hipparchus.util.MathUtils;
  24. import org.orekit.bodies.GeodeticPoint;
  25. import org.orekit.bodies.OneAxisEllipsoid;
  26. import org.orekit.errors.OrekitException;
  27. import org.orekit.errors.OrekitMessages;
  28. import org.orekit.frames.Frame;
  29. import org.orekit.frames.Transform;
  30. import org.orekit.propagation.events.VisibilityTrigger;

  31. /** Class representing a spacecraft sensor Field Of View with shape defined by a smooth single loop.
  32.  * @author Luc Maisonobe
  33.  * @since 10.1
  34.  */
  35. public abstract class SmoothFieldOfView extends AbstractFieldOfView {

  36.     /** Direction of the FOV center. */
  37.     private final Vector3D center;

  38.     /** X axis defining FoV boundary. */
  39.     private final Vector3D xAxis;

  40.     /** Y axis defining FoV boundary. */
  41.     private final Vector3D yAxis;

  42.     /** Z axis defining FoV boundary. */
  43.     private final Vector3D zAxis;

  44.     /** Build a new instance.
  45.      * @param center direction of the FOV center (Z<sub>smooth</sub>), in spacecraft frame
  46.      * @param primaryMeridian vector defining the (+X<sub>smooth</sub>, Z<sub>smooth</sub>)
  47.      * half-plane (it is allowed to have {@code primaryMeridian} not orthogonal to
  48.      * {@code center} as orthogonality will be fixed internally)
  49.      * @param margin angular margin to apply to the zone (if positive,
  50.      * the Field Of View will consider points slightly outside of the
  51.      * zone are still visible)
  52.      */
  53.     protected SmoothFieldOfView(final Vector3D center, final Vector3D primaryMeridian,
  54.                                 final double margin) {

  55.         super(margin);

  56.         this.center = center;
  57.         this.zAxis  = center.normalize();
  58.         this.yAxis  = Vector3D.crossProduct(center, primaryMeridian).normalize();
  59.         this.xAxis  = Vector3D.crossProduct(yAxis, center).normalize();

  60.     }

  61.     /** Get the direction of the FOV center, in spacecraft frame.
  62.      * @return direction of the FOV center, in spacecraft frame
  63.      */
  64.     public Vector3D getCenter() {
  65.         return center;
  66.     }

  67.     /** Get the X axis defining FoV boundary.
  68.      * @return X axis defining FoV boundary, in spacecraft frame
  69.      */
  70.     public Vector3D getX() {
  71.         return xAxis;
  72.     }

  73.     /** Get the Y axis defining FoV boundary.
  74.      * @return Y axis defining FoV boundary, in spacecraft frame
  75.      */
  76.     public Vector3D getY() {
  77.         return yAxis;
  78.     }

  79.     /** Get the Z axis defining FoV boundary.
  80.      * @return Z axis defining FoV boundary, in spacecraft frame
  81.      */
  82.     public Vector3D getZ() {
  83.         return zAxis;
  84.     }


  85.     /** {@inheritDoc} */
  86.     @Override
  87.     public List<List<GeodeticPoint>> getFootprint(final Transform fovToBody,
  88.                                                   final OneAxisEllipsoid body,
  89.                                                   final double angularStep) {

  90.         final Frame     bodyFrame = body.getBodyFrame();
  91.         final Vector3D  position  = fovToBody.transformPosition(Vector3D.ZERO);
  92.         final double    r         = position.getNorm();
  93.         if (body.isInside(position)) {
  94.             throw new OrekitException(OrekitMessages.POINT_INSIDE_ELLIPSOID);
  95.         }

  96.         // prepare loop around FoV
  97.         boolean                         intersectionsFound = false;
  98.         final int                       nbPoints           = (int) FastMath.ceil(MathUtils.TWO_PI / angularStep);
  99.         final List<GeodeticPoint>       loop               = new ArrayList<>(nbPoints);

  100.         // loop in inverse trigonometric order, so footprint is in trigonometric order
  101.         final double step = MathUtils.TWO_PI / nbPoints;
  102.         for (int i = 0; i < nbPoints; ++i) {
  103.             final Vector3D direction   = directionAt(-i * step);
  104.             final Vector3D awaySC      = new Vector3D(r, direction);
  105.             final Vector3D awayBody    = fovToBody.transformPosition(awaySC);
  106.             final Line     lineOfSight = new Line(position, awayBody, 1.0e-3);
  107.             GeodeticPoint  gp          = body.getIntersectionPoint(lineOfSight, position, bodyFrame, null);
  108.             if (gp != null &&
  109.                 Vector3D.dotProduct(awayBody.subtract(position), body.transform(gp).subtract(position)) < 0) {
  110.                 // the intersection is in fact on the half-line pointing
  111.                 // towards the back side, it is a spurious intersection
  112.                 gp = null;
  113.             }

  114.             if (gp != null) {
  115.                 // the line of sight does intersect the body
  116.                 intersectionsFound = true;
  117.             } else {
  118.                 // the line of sight does not intersect body
  119.                 // we use a point on the limb
  120.                 gp = body.transform(body.pointOnLimb(position, awayBody), bodyFrame, null);
  121.             }

  122.             // add the point
  123.             loop.add(gp);

  124.         }

  125.         final List<List<GeodeticPoint>> footprint = new ArrayList<>();
  126.         if (intersectionsFound) {
  127.             // at least some of the points did intersect the body, there is a footprint
  128.             footprint.add(loop);
  129.         } else {
  130.             // the Field Of View loop does not cross the body
  131.             // either the body is outside of Field Of View, or it is fully contained
  132.             // we check the center
  133.             final Vector3D bodyCenter = fovToBody.getStaticInverse().transformPosition(Vector3D.ZERO);
  134.             if (offsetFromBoundary(bodyCenter, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) < 0.0) {
  135.                 // the body is fully contained in the Field Of View
  136.                 // the previous loop did compute the full limb as the footprint
  137.                 footprint.add(loop);
  138.             }
  139.         }

  140.         return footprint;

  141.     }

  142.     /** Get boundary direction at angle.
  143.      * @param angle phase angle of the boundary direction
  144.      * @return boundary direction at phase angle in spacecraft frame
  145.      */
  146.     protected abstract Vector3D directionAt(double angle);

  147. }