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.files.sinex;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.hipparchus.geometry.euclidean.threed.Vector3D;
25  import org.orekit.errors.OrekitException;
26  import org.orekit.errors.OrekitMessages;
27  import org.orekit.gnss.GnssSignal;
28  import org.orekit.models.earth.displacement.PsdCorrection;
29  import org.orekit.time.AbsoluteDate;
30  import org.orekit.utils.TimeSpanMap;
31  
32  /**
33   * Station model.
34   * <p>
35   * Since Orekit 11.1, this class handles multiple site antenna
36   * eccentricity.
37   * The {@link #getEccentricities(AbsoluteDate)} method can be
38   * used to access the site antenna eccentricity values for a
39   * given epoch.
40   * </p>
41   * @author Bryan Cazabonne
42   * @since 10.3
43   */
44  public class Station {
45  
46      /** Site code. */
47      private String siteCode;
48  
49      /** DOMES number. */
50      private String domes;
51  
52      /** Start of validity. */
53      private AbsoluteDate validFrom;
54  
55      /** End of validity. */
56      private AbsoluteDate validUntil;
57  
58      /** Eccentricity reference system. */
59      private ReferenceSystem eccRefSystem;
60  
61      /** TimeSpanMap of site antenna eccentricities. */
62      private final TimeSpanMap<Vector3D> eccentricitiesTimeSpanMap;
63  
64      /** Antenna key.
65       * @since 13.0
66       */
67      private final TimeSpanMap<AntennaKey> antennaKeysMap;
68  
69      /** Phase centers.
70       * @since 13.0
71       */
72      private final TimeSpanMap<Map<GnssSignal, Vector3D>> phaseCentersMap;
73  
74      /** Post-Seismic Deformation.
75       * @since 12.0
76       */
77      private final TimeSpanMap<List<PsdCorrection>> psdMap;
78  
79      /** Station position. */
80      private Vector3D position;
81  
82      /** Station velocity. */
83      private Vector3D velocity;
84  
85      /** Coordinates reference epoch. */
86      private AbsoluteDate epoch;
87  
88      /**
89       * Constructor.
90       */
91      public Station() {
92          this.eccentricitiesTimeSpanMap = new TimeSpanMap<>(null);
93          this.antennaKeysMap            = new TimeSpanMap<>(null);
94          this.phaseCentersMap           = new TimeSpanMap<>(null);
95          this.psdMap                    = new TimeSpanMap<>(null);
96          this.position                  = Vector3D.ZERO;
97          this.velocity                  = Vector3D.ZERO;
98      }
99  
100     /**
101      * Get the site code (station identifier).
102      * @return the site code
103      */
104     public String getSiteCode() {
105         return siteCode;
106     }
107 
108     /**
109      * Set the site code (station identifier).
110      * @param siteCode the site code to set
111      */
112     public void setSiteCode(final String siteCode) {
113         this.siteCode = siteCode;
114     }
115 
116     /**
117      * Get the site DOMES number.
118      * @return the DOMES number
119      */
120     public String getDomes() {
121         return domes;
122     }
123 
124     /**
125      * Set the DOMES number.
126      * @param domes the DOMES number to set
127      */
128     public void setDomes(final String domes) {
129         this.domes = domes;
130     }
131 
132     /**
133      * Get start of validity.
134      * @return start of validity
135      */
136     public AbsoluteDate getValidFrom() {
137         return validFrom;
138     }
139 
140     /**
141      * Set the start of validity.
142      * @param validFrom the start of validity to set
143      */
144     public void setValidFrom(final AbsoluteDate validFrom) {
145         this.validFrom = validFrom;
146     }
147 
148     /**
149      * Get end of validity.
150      * @return end of validity
151      */
152     public AbsoluteDate getValidUntil() {
153         return validUntil;
154     }
155 
156     /**
157      * Set the end of validity.
158      * @param validUntil the end of validity to set
159      */
160     public void setValidUntil(final AbsoluteDate validUntil) {
161         this.validUntil = validUntil;
162     }
163 
164     /**
165      * Get the reference system used to define the eccentricity vector (local or cartesian).
166      * @return the reference system used to define the eccentricity vector
167      */
168     public ReferenceSystem getEccRefSystem() {
169         return eccRefSystem;
170     }
171 
172     /**
173      * Set the reference system used to define the eccentricity vector (local or cartesian).
174      * @param eccRefSystem the reference system used to define the eccentricity vector
175      */
176     public void setEccRefSystem(final ReferenceSystem eccRefSystem) {
177         this.eccRefSystem = eccRefSystem;
178     }
179 
180     /**
181      * Get the station antenna eccentricities for the given epoch.
182      * <p>
183      * Vector convention: X-Y-Z or UP-NORTH-EAST.
184      * See {@link #getEccRefSystem()} method.
185      * <p>
186      * If there is no eccentricity values for the given epoch, an
187      * exception is thrown.
188      * @param date epoch
189      * @return station antenna eccentricities (m)
190      * @since 11.1
191      */
192     public Vector3D getEccentricities(final AbsoluteDate date) {
193         final Vector3D eccAtEpoch = eccentricitiesTimeSpanMap.get(date);
194         // If the entry is null, there is no valid eccentricity values for the input epoch
195         if (eccAtEpoch == null) {
196             // Throw an exception
197             throw new OrekitException(OrekitMessages.MISSING_STATION_DATA_FOR_EPOCH, date);
198         }
199         return eccAtEpoch;
200     }
201 
202     /**
203      * Get the TimeSpanMap of site antenna eccentricities.
204      * @return the TimeSpanMap of site antenna eccentricities
205      * @since 11.1
206      */
207     public TimeSpanMap<Vector3D> getEccentricitiesTimeSpanMap() {
208         return eccentricitiesTimeSpanMap;
209     }
210 
211     /** Add a station eccentricity vector entry valid before a limit date.<br>
212      * Using <code>addStationEccentricitiesValidBefore(entry, t)</code> will make <code>entry</code>
213      * valid in ]-∞, t[ (note the open bracket).
214      * @param entry station eccentricity vector entry
215      * @param latestValidityDate date before which the entry is valid
216      * (must be different from <b>all</b> dates already used for transitions)
217      * @since 11.1
218      */
219     public void addStationEccentricitiesValidBefore(final Vector3D entry, final AbsoluteDate latestValidityDate) {
220         eccentricitiesTimeSpanMap.addValidBefore(entry, latestValidityDate, false);
221     }
222 
223     /** Get the TimeSpanMap of Post-Seismic Deformation.
224      * @return the TimeSpanMap of Post-Seismic Deformation
225      * @since 12.1
226      */
227     public TimeSpanMap<List<PsdCorrection>> getPsdTimeSpanMap() {
228         return psdMap;
229     }
230 
231     /** Add a Post-Seismic Deformation entry valid after a limit date.<br>
232      * Using {@code addPsdCorrectionValidAfter(entry, t)} will make {@code entry}
233      * valid in [t, +∞[ (note the closed bracket).
234      * @param entry Post-Seismic Deformation entry
235      * @param earliestValidityDate date after which the entry is valid
236      * (must be different from <b>all</b> dates already used for transitions)
237      * @since 12.1
238      */
239     public void addPsdCorrectionValidAfter(final PsdCorrection entry, final AbsoluteDate earliestValidityDate) {
240 
241         // get the list of corrections active just after earthquake date
242         List<PsdCorrection> corrections = psdMap.get(earliestValidityDate.shiftedBy(1.0e-3));
243 
244         if (corrections == null ||
245             earliestValidityDate.durationFrom(corrections.get(0).getEarthquakeDate()) > 1.0e-3) {
246             // either this is the first earthquake we consider or
247             // this earthquake is after another one already considered
248             // we need to create a new list of corrections for this new earthquake
249             corrections = new ArrayList<>();
250             psdMap.addValidAfter(corrections, earliestValidityDate, false);
251         }
252 
253         // add the entry to the current list
254         corrections.add(entry);
255 
256     }
257 
258     /**
259      * Get the antenna key for the given epoch.
260      * If there is no antenna keys for the given epoch, an
261      * exception is thrown.
262      * @param date epoch
263      * @return antenna key
264      * @since 13.0
265      */
266     public AntennaKey getAntennaKey(final AbsoluteDate date) {
267         final AntennaKey keyAtEpoch = antennaKeysMap.get(date);
268         // If the entry is null, there is no valid type for the input epoch
269         if (keyAtEpoch == null) {
270             // Throw an exception
271             throw new OrekitException(OrekitMessages.MISSING_STATION_DATA_FOR_EPOCH, date);
272         }
273         return keyAtEpoch;
274     }
275 
276     /**
277      * Get the TimeSpanMap of site antenna type.
278      * @return the TimeSpanMap of site antenna type
279      * @since 12.0
280      */
281     public TimeSpanMap<AntennaKey> getAntennaKeyTimeSpanMap() {
282         return antennaKeysMap;
283     }
284 
285     /** Add a antenna key entry valid before a limit date.<br>
286      * Using <code>addAntennaKeyValidBefore(entry, t)</code> will make <code>entry</code>
287      * valid in ]-∞, t[ (note the open bracket).
288      * @param entry antenna key entry
289      * @param latestValidityDate date before which the entry is valid
290      * (must be different from <b>all</b> dates already used for transitions)
291      * @since 12.0
292      */
293     public void addAntennaKeyValidBefore(final AntennaKey entry, final AbsoluteDate latestValidityDate) {
294         antennaKeysMap.addValidBefore(entry, latestValidityDate, false);
295     }
296 
297     /**
298      * Get the TimeSpanMap of phase centers.
299      * @return the TimeSpanMap of phase centers
300      * @since 13.0
301      */
302     public TimeSpanMap<Map<GnssSignal, Vector3D>> getPhaseCentersMap() {
303         return phaseCentersMap;
304     }
305 
306     /**
307      * Get the phase centers for the given epoch.
308      * If there is no phase centers for the given epoch, an
309      * exception is thrown.
310      * @param date epoch
311      * @return phase centers
312      * @since 13.0
313      */
314     public Map<GnssSignal, Vector3D> getPhaseCenters(final AbsoluteDate date) {
315         final Map<GnssSignal, Vector3D> phaseCentersAtEpoch = phaseCentersMap.get(date);
316         // If the entry is null, there is no valid key for the input epoch
317         if (phaseCentersAtEpoch == null) {
318             // Throw an exception
319             throw new OrekitException(OrekitMessages.MISSING_STATION_DATA_FOR_EPOCH, date);
320         }
321         return phaseCentersAtEpoch;
322     }
323 
324     /**
325      * Get the station position.
326      * @return the station position (m)
327      */
328     public Vector3D getPosition() {
329         return position;
330     }
331 
332     /**
333      * Set the station position.
334      * @param position the position to set
335      */
336     public void setPosition(final Vector3D position) {
337         this.position = position;
338     }
339 
340     /**
341      * Get the station velocity.
342      * @return the station velocity (m/s)
343      */
344     public Vector3D getVelocity() {
345         return velocity;
346     }
347 
348     /**
349      * Set the station velocity.
350      * @param velocity the velocity to set
351      */
352     public void setVelocity(final Vector3D velocity) {
353         this.velocity = velocity;
354     }
355 
356     /**
357      * Get the coordinates reference epoch.
358      * @return the coordinates reference epoch
359      */
360     public AbsoluteDate getEpoch() {
361         return epoch;
362     }
363 
364     /**
365      * Set the coordinates reference epoch.
366      * @param epoch the epoch to set
367      */
368     public void setEpoch(final AbsoluteDate epoch) {
369         this.epoch = epoch;
370     }
371 
372     /** Eccentricity reference system. */
373     public enum ReferenceSystem {
374 
375         /** Local reference system Up, North, East. */
376         UNE("UNE"),
377 
378         /** Cartesian reference system X, Y, Z. */
379         XYZ("XYZ");
380 
381         /** Codes map. */
382         private static final Map<String, ReferenceSystem> CODES_MAP = new HashMap<>();
383         static {
384             for (final ReferenceSystem type : values()) {
385                 CODES_MAP.put(type.getName(), type);
386             }
387         }
388 
389         /** Name used to define the reference system in SINEX file. */
390         private final String name;
391 
392         /**
393          * Constructor.
394          * @param name name used to define the reference system in SINEX file
395          */
396         ReferenceSystem(final String name) {
397             this.name = name;
398         }
399 
400         /**
401          * Get the name used to define the reference system in SINEX file.
402          * @return the name
403          */
404         public String getName() {
405             return name;
406         }
407 
408         /**
409          * Get the eccentricity reference system corresponding to the given value.
410          * @param value given value
411          * @return the corresponding eccentricity reference system
412          */
413         public static ReferenceSystem getEccRefSystem(final String value) {
414             return CODES_MAP.get(value);
415         }
416 
417     }
418 
419 }
420