1 /* Copyright 2013-2019 CS Systèmes d'Information
2 * Licensed to CS Systèmes d'Information (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
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import org.hipparchus.geometry.euclidean.threed.Vector3D;
23 import org.hipparchus.util.FastMath;
24 import org.orekit.bodies.GeodeticPoint;
25 import org.orekit.bodies.OneAxisEllipsoid;
26 import org.orekit.frames.Frame;
27 import org.orekit.frames.Transform;
28 import org.orekit.time.AbsoluteDate;
29 import org.orekit.utils.TimeStampedPVCoordinates;
30
31 /** Class estimating very roughly when a point may be visible from spacecraft.
32 * <p>
33 * The class only uses spacecraft position to compute a very rough sub-satellite
34 * point. It assumes the position-velocities are regular enough and without holes.
35 * It is intended only only has a quick estimation in order to set up search
36 * boundaries in inverse location.
37 * </p>
38 * @see org.orekit.rugged.api.Rugged#dateLocation(String, org.orekit.bodies.GeodeticPoint, int, int)
39 * @see org.orekit.rugged.api.Rugged#dateLocation(String, double, double, int, int)
40 * @see org.orekit.rugged.api.Rugged#inverseLocation(String, org.orekit.bodies.GeodeticPoint, int, int)
41 * @see org.orekit.rugged.api.Rugged#inverseLocation(String, double, double, int, int)
42 * @author Luc Maisonobe
43 */
44 public class RoughVisibilityEstimator {
45
46 /** Ground ellipsoid. */
47 private final OneAxisEllipsoid ellipsoid;
48
49 /** Sub-satellite point. */
50 private final List<TimeStampedPVCoordinates> pvGround;
51
52 /** Mean angular rate with respect to position-velocity indices. */
53 private final double rateVSIndices;
54
55 /** Mean angular rate with respect to time. */
56 private final double rateVSTime;
57
58 /** Last found index. */
59 private int last;
60
61 /**
62 * Simple constructor.
63 * @param ellipsoid ground ellipsoid
64 * @param frame frame in which position and velocity are defined (may be inertial or body frame)
65 * @param positionsVelocities satellite position and velocity (m and m/s in specified frame)
66 */
67 public RoughVisibilityEstimator(final OneAxisEllipsoid ellipsoid, final Frame frame,
68 final List<TimeStampedPVCoordinates> positionsVelocities) {
69
70 this.ellipsoid = ellipsoid;
71
72 // project spacecraft position-velocity to ground
73 final Frame bodyFrame = ellipsoid.getBodyFrame();
74 final int n = positionsVelocities.size();
75 this.pvGround = new ArrayList<TimeStampedPVCoordinates>(n);
76 for (final TimeStampedPVCoordinates pv : positionsVelocities) {
77 final Transform t = frame.getTransformTo(bodyFrame, pv.getDate());
78 pvGround.add(ellipsoid.projectToGround(t.transformPVCoordinates(pv), bodyFrame));
79 }
80
81 // initialize first search at mid point
82 this.last = n / 2;
83
84 // estimate mean angular rate with respect to indices
85 double alpha = 0;
86 for (int i = 0; i < n - 1; ++i) {
87 // angular motion between points i and i+1
88 alpha += Vector3D.angle(pvGround.get(i).getPosition(),
89 pvGround.get(i + 1).getPosition());
90 }
91 this.rateVSIndices = alpha / n;
92
93 // estimate mean angular rate with respect to time
94 final AbsoluteDate firstDate = pvGround.get(0).getDate();
95 final AbsoluteDate lastDate = pvGround.get(pvGround.size() - 1).getDate();
96 this.rateVSTime = alpha / lastDate.durationFrom(firstDate);
97
98 }
99
100 /** Estimate <em>very roughly</em> when spacecraft comes close to a ground point.
101 * @param groundPoint ground point to check
102 * @return rough date at which spacecraft comes close to ground point (never null,
103 * but may be really far from reality if ground point is away from trajectory)
104 */
105 public AbsoluteDate estimateVisibility(final GeodeticPoint groundPoint) {
106
107 final Vector3D point = ellipsoid.transform(groundPoint);
108 int closeIndex = findClose(last, point);
109
110 // check if there are closer points in previous periods
111 final int repeat = (int) FastMath.rint(2.0 * FastMath.PI / rateVSIndices);
112 for (int index = closeIndex - repeat; index > 0; index -= repeat) {
113 final int otherIndex = findClose(index, point);
114 if (otherIndex != closeIndex &&
115 Vector3D.distance(pvGround.get(otherIndex).getPosition(), point) <
116 Vector3D.distance(pvGround.get(closeIndex).getPosition(), point)) {
117 closeIndex = otherIndex;
118 }
119 }
120
121 // check if there are closer points in next periods
122 for (int index = closeIndex + repeat; index < pvGround.size(); index += repeat) {
123 final int otherIndex = findClose(index, point);
124 if (otherIndex != closeIndex &&
125 Vector3D.distance(pvGround.get(otherIndex).getPosition(), point) <
126 Vector3D.distance(pvGround.get(closeIndex).getPosition(), point)) {
127 closeIndex = otherIndex;
128 }
129 }
130
131 // we have found the closest sub-satellite point index
132 last = closeIndex;
133
134 // final adjustment
135 final TimeStampedPVCoordinates closest = pvGround.get(closeIndex);
136 final double alpha = neededMotion(closest, point);
137 return closest.getDate().shiftedBy(alpha / rateVSTime);
138
139 }
140
141 /** Find the index of a close sub-satellite point.
142 * @param start start index for the search
143 * @param point test point
144 * @return index of a sub-satellite point close to the test point
145 */
146 private int findClose(final int start, final Vector3D point) {
147 int current = start;
148 int previous = Integer.MIN_VALUE;
149 int maxLoop = 1000;
150 while (maxLoop-- > 0 && FastMath.abs(current - previous) > 1) {
151 previous = current;
152 final double alpha = neededMotion(pvGround.get(current), point);
153 final int shift = (int) FastMath.rint(alpha / rateVSIndices);
154 current = FastMath.max(0, FastMath.min(pvGround.size() - 1, current + shift));
155 }
156 return current;
157 }
158
159 /** Estimate angular motion needed to go past test point.
160 * <p>
161 * This estimation is quite crude. The sub-satellite point is properly on the
162 * ellipsoid surface, but we compute the angle assuming a spherical shape.
163 * </p>
164 * @param subSatellite current sub-satellite position-velocity
165 * @param point test point
166 * @return angular motion to go past test point (positive is
167 * test point is ahead, negative if it is behind)
168 */
169 private double neededMotion(final TimeStampedPVCoordinates subSatellite,
170 final Vector3D point) {
171
172 final Vector3D ssP = subSatellite.getPosition();
173 final Vector3D momentum = subSatellite.getMomentum();
174 final double y = Vector3D.dotProduct(point, Vector3D.crossProduct(momentum, ssP).normalize());
175 final double x = Vector3D.dotProduct(point, ssP.normalize());
176
177 return FastMath.atan2(y, x);
178
179 }
180
181 }