1   /* Copyright 2002-2012 Space Applications Services
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.files.sp3;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.LinkedHashMap;
22  import java.util.List;
23  import java.util.Locale;
24  import java.util.Map;
25  import java.util.function.Function;
26  
27  import org.hipparchus.geometry.euclidean.threed.Vector3D;
28  import org.orekit.files.general.EphemerisFile;
29  import org.orekit.frames.Frame;
30  import org.orekit.gnss.TimeSystem;
31  import org.orekit.propagation.BoundedPropagator;
32  import org.orekit.time.AbsoluteDate;
33  import org.orekit.utils.CartesianDerivativesFilter;
34  import org.orekit.utils.TimeStampedPVCoordinates;
35  
36  /**
37   * Represents a parsed SP3 orbit file.
38   * @author Thomas Neidhart
39   * @author Evan Ward
40   */
41  public class SP3
42      implements EphemerisFile<SP3.SP3Coordinate, SP3.SP3Ephemeris> {
43      /** String representation of the center of ephemeris coordinate system. **/
44      public static final String SP3_FRAME_CENTER_STRING = "EARTH";
45  
46      /** File type indicator. */
47      public enum SP3FileType {
48          /** GPS only file. */
49          GPS,
50          /** Mixed file. */
51          MIXED,
52          /** GLONASS only file. */
53          GLONASS,
54          /** LEO only file. */
55          LEO,
56          /** Galileo only file. */
57          GALILEO,
58          /** SBAS only file. */
59          SBAS,
60          /** IRNSS only file. */
61          IRNSS,
62          /** COMPASS only file. */
63          COMPASS,
64          /** QZSS only file. */
65          QZSS,
66          /** undefined file format. */
67          UNDEFINED
68      }
69  
70      /** Orbit type indicator. */
71      public enum SP3OrbitType {
72          /** fitted. */
73          FIT,
74          /** extrapolated or predicted. */
75          EXT,
76          /** broadcast. */
77          BCT,
78          /** fitted after applying a Helmert transformation. */
79          HLM,
80          /** other type, defined by SP3 file producing agency.
81           * @since 9.3
82           */
83          OTHER;
84  
85          /** Parse a string to get the type.
86           * @param s string to parse
87           * @return the type corresponding to the string
88           */
89          public static SP3OrbitType parseType(final String s) {
90              final String normalizedString = s.trim().toUpperCase(Locale.US);
91              if ("EST".equals(normalizedString)) {
92                  return FIT;
93              } else if ("BHN".equals(normalizedString)) {
94                  // ESOC navigation team uses BHN for files produced
95                  // by their main parameter estimation program Bahn
96                  return FIT;
97              } else if ("PRO".equals(normalizedString)) {
98                  // ESOC navigation team uses PRO for files produced
99                  // by their orbit propagation program Propag
100                 return EXT;
101             } else {
102                 try {
103                     return valueOf(normalizedString);
104                 } catch (IllegalArgumentException iae) {
105                     return OTHER;
106                 }
107             }
108         }
109 
110     }
111 
112     /** File type. */
113     private SP3FileType type;
114 
115     /** Time system. */
116     private TimeSystem timeSystem;
117 
118     /** Epoch of the file. */
119     private AbsoluteDate epoch;
120 
121     /** GPS week. */
122     private int gpsWeek;
123 
124     /** Seconds of the current GPS week. */
125     private double secondsOfWeek;
126 
127     /** Julian day. */
128     private int julianDay;
129 
130     /** Day fraction. */
131     private double dayFraction;
132 
133     /** Time-interval between epochs. */
134     private double epochInterval;
135 
136     /** Number of epochs. */
137     private int numberOfEpochs;
138 
139     /** Coordinate system. */
140     private String coordinateSystem;
141 
142     /** Data used indicator. */
143     private String dataUsed;
144 
145     /** Orbit type. */
146     private SP3OrbitType orbitType;
147 
148     /** Key for orbit type.
149      * @since 9.3
150      */
151     private String orbitTypeKey;
152 
153     /** Agency providing the file. */
154     private String agency;
155 
156     /** Indicates if data contains velocity or not. */
157     private CartesianDerivativesFilter filter;
158 
159     /** Standard gravitational parameter in m^3 / s^2. */
160     private final double mu;
161 
162     /** Number of samples to use when interpolating. */
163     private final int interpolationSamples;
164 
165     /** Maps {@link #coordinateSystem} to a {@link Frame}. */
166     private final Function<? super String, ? extends Frame> frameBuilder;
167 
168     /** A map containing satellite information. */
169     private Map<String, SP3Ephemeris> satellites;
170 
171     /**
172      * Create a new SP3 file object.
173      *
174      * @param mu                   is the standard gravitational parameter in m^3 / s^2.
175      * @param interpolationSamples number of samples to use in interpolation.
176      * @param frameBuilder         for constructing a reference frame from the identifier
177      */
178     public SP3(final double mu,
179                    final int interpolationSamples,
180                    final Function<? super String, ? extends Frame> frameBuilder) {
181         this.mu = mu;
182         this.interpolationSamples = interpolationSamples;
183         this.frameBuilder = frameBuilder;
184         // must be linked has map to preserve order of satellites in the file.
185         satellites = new LinkedHashMap<>();
186     }
187 
188     /**
189      * Set the derivatives filter.
190      *
191      * @param filter that indicates which derivatives of position are available.
192      */
193     public void setFilter(final CartesianDerivativesFilter filter) {
194         this.filter = filter;
195     }
196 
197     /** Returns the {@link SP3FileType} associated with this SP3 file.
198      * @return the file type for this SP3 file
199      */
200     public SP3FileType getType() {
201         return type;
202     }
203 
204     /** Set the file type for this SP3 file.
205      * @param fileType the file type to be set
206      */
207     public void setType(final SP3FileType fileType) {
208         this.type = fileType;
209     }
210 
211     /** Returns the {@link TimeSystem} used to time-stamp position entries.
212      * @return the {@link TimeSystem} of the orbit file
213      */
214     public TimeSystem getTimeSystem() {
215         return timeSystem;
216     }
217 
218     /** Set the time system used in this SP3 file.
219      * @param system the time system to be set
220      */
221     public void setTimeSystem(final TimeSystem system) {
222         this.timeSystem = system;
223     }
224 
225     /** Returns the data used indicator from the SP3 file.
226      * @return the data used indicator (unparsed)
227      */
228     public String getDataUsed() {
229         return dataUsed;
230     }
231 
232     /** Set the data used indicator for this SP3 file.
233      * @param data the data used indicator to be set
234      */
235     public void setDataUsed(final String data) {
236         this.dataUsed = data;
237     }
238 
239     /** Returns the start epoch of the orbit file.
240      * @return the start epoch
241      */
242     public AbsoluteDate getEpoch() {
243         return epoch;
244     }
245 
246     /** Set the epoch of the SP3 file.
247      * @param time the epoch to be set
248      */
249     public void setEpoch(final AbsoluteDate time) {
250         this.epoch = time;
251     }
252 
253     /** Returns the GPS week as contained in the SP3 file.
254      * @return the GPS week of the SP3 file
255      */
256     public int getGpsWeek() {
257         return gpsWeek;
258     }
259 
260     /** Set the GPS week of the SP3 file.
261      * @param week the GPS week to be set
262      */
263     public void setGpsWeek(final int week) {
264         this.gpsWeek = week;
265     }
266 
267     /** Returns the seconds of the GPS week as contained in the SP3 file.
268      * @return the seconds of the GPS week
269      */
270     public double getSecondsOfWeek() {
271         return secondsOfWeek;
272     }
273 
274     /** Set the seconds of the GPS week for this SP3 file.
275      * @param seconds the seconds to be set
276      */
277     public void setSecondsOfWeek(final double seconds) {
278         this.secondsOfWeek = seconds;
279     }
280 
281     /** Returns the julian day for this SP3 file.
282      * @return the julian day
283      */
284     public int getJulianDay() {
285         return julianDay;
286     }
287 
288     /** Set the julian day for this SP3 file.
289      * @param day the julian day to be set
290      */
291     public void setJulianDay(final int day) {
292         this.julianDay = day;
293     }
294 
295     /** Returns the day fraction for this SP3 file.
296      * @return the day fraction
297      */
298     public double getDayFraction() {
299         return dayFraction;
300     }
301 
302     /** Set the day fraction for this SP3 file.
303      * @param fraction the day fraction to be set
304      */
305     public void setDayFraction(final double fraction) {
306         this.dayFraction = fraction;
307     }
308 
309     /** Returns the time interval between epochs (in seconds).
310      * @return the time interval between epochs
311      */
312     public double getEpochInterval() {
313         return epochInterval;
314     }
315 
316     /** Set the epoch interval for this SP3 file.
317      * @param interval the interval between orbit entries
318      */
319     public void setEpochInterval(final double interval) {
320         this.epochInterval = interval;
321     }
322 
323     /** Returns the number of epochs contained in this orbit file.
324      * @return the number of epochs
325      */
326     public int getNumberOfEpochs() {
327         return numberOfEpochs;
328     }
329 
330     /** Set the number of epochs as contained in the SP3 file.
331      * @param epochCount the number of epochs to be set
332      */
333     public void setNumberOfEpochs(final int epochCount) {
334         this.numberOfEpochs = epochCount;
335     }
336 
337     /** Returns the coordinate system of the entries in this orbit file.
338      * @return the coordinate system
339      */
340     public String getCoordinateSystem() {
341         return coordinateSystem;
342     }
343 
344     /** Set the coordinate system used for the orbit entries.
345      * @param system the coordinate system to be set
346      */
347     public void setCoordinateSystem(final String system) {
348         this.coordinateSystem = system;
349     }
350 
351     /** Returns the {@link SP3OrbitType} for this SP3 file.
352      * @return the orbit type
353      */
354     public SP3OrbitType getOrbitType() {
355         return orbitType;
356     }
357 
358     /** Returns the orbit type key for this SP3 file.
359      * @return the orbit type key
360      * @since 9.3
361      */
362     public String getOrbitTypeKey() {
363         return orbitTypeKey;
364     }
365 
366     /** Set the orbit type key for this SP3 file.
367      * @param oTypeKey the orbit type key to be set
368      * @since 9.3
369      */
370     public void setOrbitTypeKey(final String oTypeKey) {
371         this.orbitTypeKey = oTypeKey;
372         this.orbitType    = SP3OrbitType.parseType(oTypeKey);
373     }
374 
375     /** Returns the agency that prepared this SP3 file.
376      * @return the agency
377      */
378     public String getAgency() {
379         return agency;
380     }
381 
382     /** Set the agency string for this SP3 file.
383      * @param agencyStr the agency string to be set
384      */
385     public void setAgency(final String agencyStr) {
386         this.agency = agencyStr;
387     }
388 
389     /** Add a new satellite with a given identifier to the list of
390      * stored satellites.
391      * @param satId the satellite identifier
392      */
393     public void addSatellite(final String satId) {
394         // only add satellites which have not been added before
395         satellites.putIfAbsent(satId, new SP3Ephemeris(satId));
396     }
397 
398     @Override
399     public Map<String, SP3Ephemeris> getSatellites() {
400         return Collections.unmodifiableMap(satellites);
401     }
402 
403     /** Get the number of satellites contained in this orbit file.
404      * @return the number of satellites
405      */
406     public int getSatelliteCount() {
407         return satellites.size();
408     }
409 
410     /**
411      * Set the formal accuracy for a satellite.
412      *
413      * @param index    is the index of the satellite.
414      * @param accuracy of the satellite, in m.
415      */
416     public void setAccuracy(final int index, final double accuracy) {
417         int n = index;
418         for (final SP3Ephemeris ephemeris : satellites.values()) {
419             if (n == 0) {
420                 ephemeris.setAccuracy(accuracy);
421                 return;
422             }
423             n--;
424         }
425     }
426 
427     /**
428      * Get the formal accuracy for a satellite.
429      *
430      * @param index    is the index of the satellite.
431      * @return accuracy of the satellite, in m.
432      */
433     public double getAccuracy(final int index) {
434         int n = index;
435         for (final SP3Ephemeris ephemeris : satellites.values()) {
436             if (n == 0) {
437                 return ephemeris.getAccuracy();
438             }
439             n--;
440         }
441         return Double.NaN;
442     }
443 
444     /** Tests whether a satellite with the given id is contained in this orbit
445      * file.
446      * @param satId the satellite id
447      * @return {@code true} if the satellite is contained in the file,
448      *         {@code false} otherwise
449      */
450     public boolean containsSatellite(final String satId) {
451         return satellites.containsKey(satId);
452     }
453 
454     /**
455      * Adds a new P/V coordinate for a given satellite.
456      *
457      * @param satId the satellite identifier
458      * @param coord the P/V coordinate of the satellite
459      */
460     public void addSatelliteCoordinate(final String satId, final SP3Coordinate coord) {
461         satellites.get(satId).coordinates.add(coord);
462     }
463 
464     /** An ephemeris for a single satellite in a SP3 file. */
465     public class SP3Ephemeris
466         implements  EphemerisFile.SatelliteEphemeris<SP3Coordinate, SP3Ephemeris>,
467                     EphemerisFile.EphemerisSegment<SP3Coordinate> {
468 
469         /** Satellite ID. */
470         private final String id;
471         /** Ephemeris Data. */
472         private final List<SP3Coordinate> coordinates;
473         /** Accuracy in m. */
474         private double accuracy;
475 
476         /**
477          * Create an ephemeris for a single satellite.
478          *
479          * @param id of the satellite.
480          */
481         public SP3Ephemeris(final String id) {
482             this.id = id;
483             this.coordinates = new ArrayList<>();
484         }
485 
486         @Override
487         public String getId() {
488             return this.id;
489         }
490 
491         @Override
492         public double getMu() {
493             return mu;
494         }
495 
496         @Override
497         public Frame getFrame() {
498             return frameBuilder.apply(SP3_FRAME_CENTER_STRING);
499         }
500 
501         @Override
502         public int getInterpolationSamples() {
503             return interpolationSamples;
504         }
505 
506         @Override
507         public CartesianDerivativesFilter getAvailableDerivatives() {
508             return filter;
509         }
510 
511         @Override
512         public List<SP3Coordinate> getCoordinates() {
513             return Collections.unmodifiableList(this.coordinates);
514         }
515 
516         /** Returns a list containing only {@code this}. */
517         @Override
518         public List<SP3Ephemeris> getSegments() {
519             return Collections.singletonList(this);
520         }
521 
522         @Override
523         public AbsoluteDate getStart() {
524             return coordinates.get(0).getDate();
525         }
526 
527         @Override
528         public AbsoluteDate getStop() {
529             return coordinates.get(coordinates.size() - 1).getDate();
530         }
531 
532         @Override
533         public BoundedPropagator getPropagator() {
534             return EphemerisSegment.super.getPropagator();
535         }
536 
537         /**
538          * Set the accuracy for this satellite.
539          *
540          * @param accuracy in m.
541          */
542         public void setAccuracy(final double accuracy) {
543             this.accuracy = accuracy;
544         }
545 
546         /**
547          * Get the formal accuracy for this satellite.
548          *
549          * <p>The accuracy is limited by the SP3 standard to be a power of 2 in mm.
550          * The value returned here is in meters.</p>
551          *
552          * @return magnitude of one standard deviation, in m.
553          */
554         public double getAccuracy() {
555             return accuracy;
556         }
557 
558     }
559 
560     /** A single record of position clock and possibly derivatives in an SP3 file. */
561     public static class SP3Coordinate extends TimeStampedPVCoordinates {
562 
563         /** Serializable UID. */
564         private static final long serialVersionUID = 20161116L;
565         /** Clock correction in s. */
566         private final double clock;
567         /** Clock rate in s / s. */
568         private final double clockRate;
569 
570         /**
571          * Create a coordinate with only position.
572          *
573          * @param date     of validity.
574          * @param position of the satellite.
575          * @param clock    correction in s.
576          */
577         public SP3Coordinate(final AbsoluteDate date,
578                              final Vector3D position,
579                              final double clock) {
580             this(date, position, Vector3D.ZERO, clock, 0);
581         }
582 
583         /**
584          * Create a coordinate with position and velocity.
585          *
586          * @param date      of validity.
587          * @param position  of the satellite.
588          * @param velocity  of the satellite.
589          * @param clock     correction in s.
590          * @param clockRate in s / s.
591          */
592         public SP3Coordinate(final AbsoluteDate date,
593                              final Vector3D position,
594                              final Vector3D velocity,
595                              final double clock,
596                              final double clockRate) {
597             super(date, position, velocity, Vector3D.ZERO);
598             this.clock = clock;
599             this.clockRate = clockRate;
600         }
601 
602         /**
603          * Returns the clock correction value.
604          *
605          * @return the clock correction in s.
606          */
607         public double getClockCorrection() {
608             return clock;
609         }
610 
611         /**
612          * Returns the clock rate.
613          *
614          * @return the clock rate of change in s/s.
615          */
616         public double getClockRateChange() {
617             return clockRate;
618         }
619 
620     }
621 
622 }