1   /* Copyright 2002-2025 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  
19  import java.util.List;
20  
21  import org.hipparchus.geometry.euclidean.threed.Vector3D;
22  import org.orekit.bodies.GeodeticPoint;
23  import org.orekit.bodies.OneAxisEllipsoid;
24  import org.orekit.frames.Transform;
25  import org.orekit.propagation.events.VisibilityTrigger;
26  
27  /** Interface representing a spacecraft sensor Field Of View.
28   * <p>Fields Of View are zones defined on the unit sphere centered on the
29   * spacecraft. Different implementations may use specific modeling
30   * depending on the shape.</p>
31   * @author Luc Maisonobe
32   * @since 10.1
33   */
34  public interface FieldOfView {
35  
36      /** Get the angular margin to apply (radians).
37       * If angular margin is positive, points outside of the raw FoV but close
38       * enough to the boundary are considered visible. If angular margin is negative,
39       * points inside of the raw FoV but close enough to the boundary are considered
40       * not visible
41       * @return angular margin
42       * @see #offsetFromBoundary(Vector3D, double, VisibilityTrigger)
43       */
44      double getMargin();
45  
46      /** Get the offset of target body with respect to the Field Of View Boundary.
47       * <p>
48       * The offset is the signed angular distance between target body and closest boundary
49       * point, taking into account {@link VisibilityTrigger} and {@link #getMargin() margin}.
50       * </p>
51       * <p>
52       * As Field Of View can have complex shapes that may require long computation,
53       * when the target point can be proven to be outside of the Field Of View, a
54       * faster but approximate computation can be used. This approximation is only
55       * performed about 0.01 radians outside of the Field Of View augmented by the
56       * deadband defined by target body radius and Field Of View margin and should be
57       * designed to still return a positive value if the full accurate computation
58       * would return a positive value. When target point is close to the zone (and
59       * furthermore when it is inside the zone), the full accurate computation is
60       * performed. This design allows this offset to be used as a reliable way to
61       * detect Field Of View boundary crossings (taking {@link VisibilityTrigger}
62       * and {@link #getMargin() margin} into account), which correspond to sign
63       * changes of the offset.
64       * </p>
65       * @param lineOfSight line of sight from the center of the Field Of View support
66       * unit sphere to the target in spacecraft frame
67       * @param angularRadius target body angular radius
68       * @param trigger visibility trigger for spherical bodies
69       * @return an offset negative if the target is visible within the Field Of
70       * View and positive if it is outside of the Field Of View
71       * (note that this cannot take into account interposing bodies)
72       * @see #offsetFromBoundary(Vector3D, double, VisibilityTrigger)
73       */
74      double offsetFromBoundary(Vector3D lineOfSight, double angularRadius, VisibilityTrigger trigger);
75  
76      /** Find the direction on Field Of View Boundary closest to a line of sight.
77       * @param lineOfSight line of sight from the center of the Field Of View support
78       * unit sphere to the target in spacecraft frame
79       * @return direction on Field Of View Boundary closest to a line of sight
80       */
81      Vector3D projectToBoundary(Vector3D lineOfSight);
82  
83      /** Get the footprint of the Field Of View on ground.
84       * <p>
85       * This method assumes the Field Of View is centered on some carrier,
86       * which will typically be a spacecraft or a ground station antenna.
87       * The points in the footprint boundary loops are all at altitude zero
88       * with respect to the ellipsoid, they correspond either to projection
89       * on ground of the edges of the Field Of View, or to points on the body
90       * limb if the Field Of View goes past horizon. The points on the limb
91       * see the carrier origin at zero elevation. If the Field Of View is so
92       * large it contains entirely the body, all points will correspond to
93       * points at limb. If the Field Of View looks away from body, the
94       * boundary loops will be an empty list. The points within footprint
95       * loops are sorted in trigonometric order as seen from the carrier.
96       * This implies that someone traveling on ground from one point to the
97       * next one will have the points visible from the carrier on his left
98       * hand side, and the points not visible from the carrier on his right
99       * hand side.
100      * </p>
101      * <p>
102      * The truncation of Field Of View at limb can induce strange results
103      * for complex Fields Of View. If for example a Field Of View is a
104      * ring with a hole and part of the ring goes past horizon, then instead
105      * of having a single loop with a C-shaped boundary, the method will
106      * still return two loops truncated at the limb, one clockwise and one
107      * counterclockwise, hence "closing" the C-shape twice. This behavior
108      * is considered acceptable.
109      * </p>
110      * <p>
111      * If the carrier is a spacecraft, then the {@code fovToBody} transform
112      * can be computed from a {@link org.orekit.propagation.SpacecraftState}
113      * as follows:
114      * </p>
115      * <pre>
116      * Transform inertToBody = state.getFrame().getTransformTo(body.getBodyFrame(), state.getDate());
117      * Transform fovToBody   = new Transform(state.getDate(),
118      *                                       state.toTransform().getInverse(),
119      *                                       inertToBody);
120      * </pre>
121      * <p>
122      * If the carrier is a ground station, located using a topocentric frame
123      * and managing its pointing direction using a transform between the
124      * dish frame and the topocentric frame, then the {@code fovToBody} transform
125      * can be computed as follows:
126      * </p>
127      * <pre>
128      * Transform topoToBody = topocentricFrame.getTransformTo(body.getBodyFrame(), date);
129      * Transform topoToDish = ...
130      * Transform fovToBody  = new Transform(date,
131      *                                      topoToDish.getInverse(),
132      *                                      topoToBody);
133      * </pre>
134      * <p>
135      * Only the raw zone is used, the angular margin is ignored here.
136      * </p>
137      * @param fovToBody transform between the frame in which the Field Of View
138      * is defined and body frame.
139      * @param body body surface the Field Of View will be projected on
140      * @param angularStep step used for boundary loops sampling (radians),
141      * beware this is generally <em>not</em> an angle on the unit sphere, but rather a
142      * phase angle used by the underlying Field Of View boundary model
143      * @return list footprint boundary loops (there may be several independent
144      * loops if the Field Of View shape is complex)
145      */
146     List<List<GeodeticPoint>> getFootprint(Transform fovToBody,
147                                            OneAxisEllipsoid body,
148                                            double angularStep);
149 
150 }