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 }