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.ArrayList;
20 import java.util.List;
21
22 import org.hipparchus.geometry.euclidean.threed.Line;
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.GeodeticPoint;
27 import org.orekit.bodies.OneAxisEllipsoid;
28 import org.orekit.errors.OrekitException;
29 import org.orekit.errors.OrekitMessages;
30 import org.orekit.frames.Frame;
31 import org.orekit.frames.Transform;
32 import org.orekit.propagation.events.VisibilityTrigger;
33
34 /** Class representing a spacecraft sensor Field Of View with shape defined by a smooth single loop.
35 * @author Luc Maisonobe
36 * @since 10.1
37 */
38 public abstract class SmoothFieldOfView extends AbstractFieldOfView {
39
40 /** Direction of the FOV center. */
41 private final Vector3D center;
42
43 /** X axis defining FoV boundary. */
44 private final Vector3D xAxis;
45
46 /** Y axis defining FoV boundary. */
47 private final Vector3D yAxis;
48
49 /** Z axis defining FoV boundary. */
50 private final Vector3D zAxis;
51
52 /** Build a new instance.
53 * @param center direction of the FOV center (Z<sub>smooth</sub>), in spacecraft frame
54 * @param primaryMeridian vector defining the (+X<sub>smooth</sub>, Z<sub>smooth</sub>)
55 * half-plane (it is allowed to have {@code primaryMeridian} not orthogonal to
56 * {@code center} as orthogonality will be fixed internally)
57 * @param margin angular margin to apply to the zone (if positive,
58 * the Field Of View will consider points slightly outside of the
59 * zone are still visible)
60 */
61 protected SmoothFieldOfView(final Vector3D center, final Vector3D primaryMeridian,
62 final double margin) {
63
64 super(margin);
65
66 this.center = center;
67 this.zAxis = center.normalize();
68 this.yAxis = Vector3D.crossProduct(center, primaryMeridian).normalize();
69 this.xAxis = Vector3D.crossProduct(yAxis, center).normalize();
70
71 }
72
73 /** Get the direction of the FOV center, in spacecraft frame.
74 * @return direction of the FOV center, in spacecraft frame
75 */
76 public Vector3D getCenter() {
77 return center;
78 }
79
80 /** Get the X axis defining FoV boundary.
81 * @return X axis defining FoV boundary, in spacecraft frame
82 */
83 public Vector3D getX() {
84 return xAxis;
85 }
86
87 /** Get the Y axis defining FoV boundary.
88 * @return Y axis defining FoV boundary, in spacecraft frame
89 */
90 public Vector3D getY() {
91 return yAxis;
92 }
93
94 /** Get the Z axis defining FoV boundary.
95 * @return Z axis defining FoV boundary, in spacecraft frame
96 */
97 public Vector3D getZ() {
98 return zAxis;
99 }
100
101
102 /** {@inheritDoc} */
103 @Override
104 public List<List<GeodeticPoint>> getFootprint(final Transform fovToBody,
105 final OneAxisEllipsoid body,
106 final double angularStep) {
107
108 final Frame bodyFrame = body.getBodyFrame();
109 final Vector3D position = fovToBody.transformPosition(Vector3D.ZERO);
110 final double r = position.getNorm();
111 if (body.isInside(position)) {
112 throw new OrekitException(OrekitMessages.POINT_INSIDE_ELLIPSOID);
113 }
114
115 // prepare loop around FoV
116 boolean intersectionsFound = false;
117 final int nbPoints = (int) FastMath.ceil(MathUtils.TWO_PI / angularStep);
118 final List<GeodeticPoint> loop = new ArrayList<>(nbPoints);
119
120 // loop in inverse trigonometric order, so footprint is in trigonometric order
121 final double step = MathUtils.TWO_PI / nbPoints;
122 for (int i = 0; i < nbPoints; ++i) {
123 final Vector3D direction = directionAt(-i * step);
124 final Vector3D awaySC = new Vector3D(r, direction);
125 final Vector3D awayBody = fovToBody.transformPosition(awaySC);
126 final Line lineOfSight = new Line(position, awayBody, 1.0e-3);
127 GeodeticPoint gp = body.getIntersectionPoint(lineOfSight, position, bodyFrame, null);
128 if (gp != null &&
129 Vector3D.dotProduct(awayBody.subtract(position), body.transform(gp).subtract(position)) < 0) {
130 // the intersection is in fact on the half-line pointing
131 // towards the back side, it is a spurious intersection
132 gp = null;
133 }
134
135 if (gp != null) {
136 // the line of sight does intersect the body
137 intersectionsFound = true;
138 } else {
139 // the line of sight does not intersect body
140 // we use a point on the limb
141 gp = body.transform(body.pointOnLimb(position, awayBody), bodyFrame, null);
142 }
143
144 // add the point
145 loop.add(gp);
146
147 }
148
149 final List<List<GeodeticPoint>> footprint = new ArrayList<>();
150 if (intersectionsFound) {
151 // at least some of the points did intersect the body, there is a footprint
152 footprint.add(loop);
153 } else {
154 // the Field Of View loop does not cross the body
155 // either the body is outside of Field Of View, or it is fully contained
156 // we check the center
157 final Vector3D bodyCenter = fovToBody.getStaticInverse().transformPosition(Vector3D.ZERO);
158 if (offsetFromBoundary(bodyCenter, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) < 0.0) {
159 // the body is fully contained in the Field Of View
160 // the previous loop did compute the full limb as the footprint
161 footprint.add(loop);
162 }
163 }
164
165 return footprint;
166
167 }
168
169 /** Get boundary direction at angle.
170 * @param angle phase angle of the boundary direction
171 * @return boundary direction at phase angle in spacecraft frame
172 */
173 protected abstract Vector3D directionAt(double angle);
174
175 }