View Javadoc
1   /* Copyright 2002-2015 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  
18  package fr.cs.examples.propagation;
19  
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.IOException;
23  import java.io.PrintStream;
24  import java.net.URISyntaxException;
25  import java.text.DecimalFormat;
26  import java.text.DecimalFormatSymbols;
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.Locale;
30  
31  import org.apache.commons.math3.geometry.euclidean.threed.Line;
32  import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
33  import org.apache.commons.math3.util.FastMath;
34  import org.orekit.attitudes.LofOffset;
35  import org.orekit.bodies.BodyShape;
36  import org.orekit.bodies.GeodeticPoint;
37  import org.orekit.bodies.OneAxisEllipsoid;
38  import org.orekit.errors.OrekitException;
39  import org.orekit.errors.PropagationException;
40  import org.orekit.frames.FramesFactory;
41  import org.orekit.frames.LOFType;
42  import org.orekit.frames.Transform;
43  import org.orekit.orbits.CircularOrbit;
44  import org.orekit.orbits.Orbit;
45  import org.orekit.orbits.PositionAngle;
46  import org.orekit.propagation.Propagator;
47  import org.orekit.propagation.SpacecraftState;
48  import org.orekit.propagation.analytical.EcksteinHechlerPropagator;
49  import org.orekit.propagation.analytical.tle.TLE;
50  import org.orekit.propagation.analytical.tle.TLEPropagator;
51  import org.orekit.propagation.sampling.OrekitFixedStepHandler;
52  import org.orekit.time.AbsoluteDate;
53  import org.orekit.time.TimeScale;
54  import org.orekit.time.TimeScalesFactory;
55  import org.orekit.utils.Constants;
56  import org.orekit.utils.IERSConventions;
57  import org.orekit.utils.PVCoordinates;
58  
59  import fr.cs.examples.Autoconfiguration;
60  import fr.cs.examples.KeyValueFileParser;
61  
62  /** Orekit tutorial for track corridor display.
63   * @author Luc Maisonobe
64   */
65  public class TrackCorridor {
66  
67      /** Program entry point.
68       * @param args program arguments
69       */
70      public static void main(String[] args) {
71          try {
72  
73              // configure Orekit
74              Autoconfiguration.configureOrekit();
75  
76              // input/out
77              File input  = new File(TrackCorridor.class.getResource("/track-corridor.in").toURI().getPath());
78              File output = new File(input.getParentFile(), "track-corridor.csv");
79  
80              new TrackCorridor().run(input, output, ",");
81  
82              System.out.println("corridor saved as file " + output);
83  
84          } catch (URISyntaxException use) {
85              System.err.println(use.getLocalizedMessage());
86              System.exit(1);
87          } catch (IOException ioe) {
88              System.err.println(ioe.getLocalizedMessage());
89              System.exit(1);
90          } catch (IllegalArgumentException iae) {
91              System.err.println(iae.getLocalizedMessage());
92              System.exit(1);
93          } catch (OrekitException oe) {
94              System.err.println(oe.getLocalizedMessage());
95              System.exit(1);
96          }
97      }
98  
99      /** Input parameter keys. */
100     private static enum ParameterKey {
101 
102         TLE_LINE1,
103         TLE_LINE2,
104         ORBIT_CIRCULAR_DATE,
105         ORBIT_CIRCULAR_A,
106         ORBIT_CIRCULAR_EX,
107         ORBIT_CIRCULAR_EY,
108         ORBIT_CIRCULAR_I,
109         ORBIT_CIRCULAR_RAAN,
110         ORBIT_CIRCULAR_ALPHA,
111         START_DATE,
112         DURATION,
113         STEP,
114         ANGULAR_OFFSET;
115 
116     }
117 
118     private void run(final File input, final File output, final String separator)
119             throws IOException, IllegalArgumentException, OrekitException {
120 
121         // read input parameters
122         KeyValueFileParser<ParameterKey> parser =
123                 new KeyValueFileParser<ParameterKey>(ParameterKey.class);
124         parser.parseInput(new FileInputStream(input));
125         TimeScale utc = TimeScalesFactory.getUTC();
126 
127         Propagator propagator;
128         if (parser.containsKey(ParameterKey.TLE_LINE1)) {
129             propagator = createPropagator(parser.getString(ParameterKey.TLE_LINE1),
130                                           parser.getString(ParameterKey.TLE_LINE2));
131         } else {
132             propagator = createPropagator(parser.getDate(ParameterKey.ORBIT_CIRCULAR_DATE, utc),
133                                           parser.getDouble(ParameterKey.ORBIT_CIRCULAR_A),
134                                           parser.getDouble(ParameterKey.ORBIT_CIRCULAR_EX),
135                                           parser.getDouble(ParameterKey.ORBIT_CIRCULAR_EY),
136                                           parser.getAngle(ParameterKey.ORBIT_CIRCULAR_I),
137                                           parser.getAngle(ParameterKey.ORBIT_CIRCULAR_RAAN),
138                                           parser.getAngle(ParameterKey.ORBIT_CIRCULAR_ALPHA));
139         }
140 
141         // simulation properties
142         AbsoluteDate start = parser.getDate(ParameterKey.START_DATE, utc);
143         double duration    = parser.getDouble(ParameterKey.DURATION);
144         double step        = parser.getDouble(ParameterKey.STEP);
145         double angle       = parser.getAngle(ParameterKey.ANGULAR_OFFSET);
146 
147         // set up a handler to gather all corridor points
148         CorridorHandler handler = new CorridorHandler(angle);
149         propagator.setMasterMode(step, handler);
150 
151         // perform propagation, letting the step handler populate the corridor
152         propagator.propagate(start, start.shiftedBy(duration));
153 
154         // retrieve the built corridor
155         List<CorridorPoint> corridor = handler.getCorridor();
156 
157         // create a 7 columns csv file representing the corridor in the user home directory, with
158         // date in column 1 (in ISO-8601 format)
159         // left limit latitude in column 2 and left limit longitude in column 3
160         // center track latitude in column 4 and center track longitude in column 5
161         // right limit latitude in column 6 and right limit longitude in column 7
162         DecimalFormat format = new DecimalFormat("#00.00000", new DecimalFormatSymbols(Locale.US));
163         PrintStream stream = new PrintStream(output);
164         for (CorridorPoint p : corridor) {
165             stream.println(p.getDate() + separator +
166                            format.format(FastMath.toDegrees(p.getLeft().getLatitude()))    + separator +
167                            format.format(FastMath.toDegrees(p.getLeft().getLongitude()))   + separator +
168                            format.format(FastMath.toDegrees(p.getCenter().getLatitude()))  + separator +
169                            format.format(FastMath.toDegrees(p.getCenter().getLongitude())) + separator +
170                            format.format(FastMath.toDegrees(p.getRight().getLatitude()))   + separator +
171                            format.format(FastMath.toDegrees(p.getRight().getLongitude())));
172         }
173         stream.close();
174 
175     }
176 
177     /** Create an orbit propagator for a circular orbit
178      * @param a  semi-major axis (m)
179      * @param ex e cos(ω), first component of circular eccentricity vector
180      * @param ey e sin(ω), second component of circular eccentricity vector
181      * @param i inclination (rad)
182      * @param raan right ascension of ascending node (Ω, rad)
183      * @param alpha  an + ω, mean latitude argument (rad)
184      * @param date date of the orbital parameters
185      * @return an orbit propagator
186      * @exception OrekitException if propagator cannot be built
187      */
188     private Propagator createPropagator(final AbsoluteDate date,
189                                         final double a, final double ex, final double ey,
190                                         final double i, final double raan,
191                                         final double alpha)
192         throws OrekitException {
193 
194         // create orbit
195         Orbit initialOrbit = new CircularOrbit(a, ex, ey, i, raan, alpha, PositionAngle.MEAN,
196                                                FramesFactory.getEME2000(), date,
197                                                Constants.EIGEN5C_EARTH_MU);
198 
199         // create propagator
200         Propagator propagator =
201                 new EcksteinHechlerPropagator(initialOrbit,
202                                               new LofOffset(initialOrbit.getFrame(), LOFType.TNW),
203                                               Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
204                                               Constants.EIGEN5C_EARTH_MU,  Constants.EIGEN5C_EARTH_C20,
205                                               Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40,
206                                               Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60);
207 
208         return propagator;
209 
210     }
211 
212     /** Create an orbit propagator for a TLE orbit
213      * @param line1 firs line of the TLE
214      * @param line2 second line of the TLE
215      * @return an orbit propagator
216      * @exception OrekitException if the TLE lines are corrupted (wrong checksums ...)
217      */
218     private Propagator createPropagator(final String line1, final String line2)
219         throws OrekitException {
220 
221         // create pseudo-orbit
222         TLE tle = new TLE(line1, line2);
223 
224         // create propagator
225         Propagator propagator = TLEPropagator.selectExtrapolator(tle);
226 
227         return propagator;
228 
229     }
230 
231     /** Step handler storing corridor points. */
232     private static class CorridorHandler implements OrekitFixedStepHandler {
233 
234         /** Earth model. */
235         private final BodyShape earth;
236 
237         /** Radial offset from satellite to some distant point at specified angular offset. */
238         private final double deltaR;
239 
240         /** Cross-track offset from satellite to some distant point at specified angular offset. */
241         private final double deltaC;
242 
243         /** Corridor. */
244         private final List<CorridorPoint> corridor;
245 
246         /** simple constructor.
247          * @param angle angular offset of corridor boundaries
248          * @exception OrekitException if Earth frame cannot be built
249          */
250         public CorridorHandler(final double angle) throws OrekitException {
251 
252             // set up Earth model
253             earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
254                                          Constants.WGS84_EARTH_FLATTENING,
255                                          FramesFactory.getITRF(IERSConventions.IERS_2010, false));
256 
257             // set up position offsets, using Earth radius as an arbitrary distance
258             deltaR = Constants.WGS84_EARTH_EQUATORIAL_RADIUS * FastMath.cos(angle);
259             deltaC = Constants.WGS84_EARTH_EQUATORIAL_RADIUS * FastMath.sin(angle);
260 
261             // prepare an empty corridor
262             corridor = new ArrayList<TrackCorridor.CorridorPoint>();
263 
264         }
265 
266         /** {@inheritDoc} */
267         public void init(final SpacecraftState s0, final AbsoluteDate t) {
268         }
269 
270         /** {@inheritDoc} */
271         public void handleStep(SpacecraftState currentState, boolean isLast)
272             throws PropagationException {
273             try {
274 
275                 // compute sub-satellite track
276                 AbsoluteDate  date    = currentState.getDate();
277                 PVCoordinates pvInert = currentState.getPVCoordinates();
278                 Transform     t       = currentState.getFrame().getTransformTo(earth.getBodyFrame(), date);
279                 Vector3D      p       = t.transformPosition(pvInert.getPosition());
280                 Vector3D      v       = t.transformVector(pvInert.getVelocity());
281                 GeodeticPoint center  = earth.transform(p, earth.getBodyFrame(), date);
282 
283                 // compute left and right corridor points
284                 Vector3D      nadir      = p.normalize().negate();
285                 Vector3D      crossTrack = p.crossProduct(v).normalize();
286                 Line          leftLine   = new Line(p, new Vector3D(1.0, p, deltaR, nadir,  deltaC, crossTrack), 1.0e-10);
287                 GeodeticPoint left       = earth.getIntersectionPoint(leftLine, p, earth.getBodyFrame(), date);
288                 Line          rightLine  = new Line(p, new Vector3D(1.0, p, deltaR, nadir, -deltaC, crossTrack), 1.0e-10);
289                 GeodeticPoint right      = earth.getIntersectionPoint(rightLine, p, earth.getBodyFrame(), date);
290 
291                 // add the corridor points
292                 corridor.add(new CorridorPoint(date, left, center, right));
293 
294             } catch (OrekitException oe) {
295                 throw new PropagationException(oe);
296             }
297         }
298 
299         /** Get the corridor.
300          * @return build corridor
301          */
302         public List<CorridorPoint> getCorridor() {
303             return corridor;
304         }
305 
306     }
307 
308     /** Container for corridor points. */
309     private static class CorridorPoint {
310 
311         /** Point date. */
312         private final AbsoluteDate date;
313 
314         /** Left limit. */
315         private final GeodeticPoint left;
316 
317         /** Central track point. */
318         private final GeodeticPoint center;
319 
320         /** Right limit. */
321         private final GeodeticPoint right;
322 
323         /** Simple constructor.
324          * @param date point date
325          * @param left left limit
326          * @param center central track point
327          * @param right right limit
328          */
329         public CorridorPoint(AbsoluteDate date, GeodeticPoint left,
330                              GeodeticPoint center, GeodeticPoint right) {
331             this.date   = date;
332             this.left   = left;
333             this.center = center;
334             this.right  = right;
335         }
336 
337         /** Get point date. */
338         public AbsoluteDate getDate() {
339             return date;
340         }
341 
342         /** Get left limit. */
343         public GeodeticPoint getLeft() {
344             return left;
345         }
346 
347         /** Get central track point. */
348         public GeodeticPoint getCenter() {
349             return center;
350         }
351 
352         /** Get right limit. */
353         public GeodeticPoint getRight() {
354             return right;
355         }
356 
357     }
358 
359 }