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 org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.orekit.errors.OrekitException;
21  import org.orekit.errors.OrekitMessages;
22  import org.orekit.gnss.GnssSignal;
23  import org.orekit.gnss.SatInSystem;
24  import org.orekit.models.earth.displacement.PsdCorrection;
25  import org.orekit.time.AbsoluteDate;
26  import org.orekit.time.TimeScales;
27  import org.orekit.utils.TimeSpanMap;
28  
29  import java.util.HashMap;
30  import java.util.Map;
31  
32  /** Parse information for Solution INdependent EXchange (SINEX) files.
33   * @author Bryan Cazabonne
34   * @author Luc Maisonobe
35   * @since 13.0
36   */
37  public class SinexParseInfo extends ParseInfo<Sinex> {
38  
39      /** Satellites antennas. */
40      private final Map<SatInSystem, Map<GnssSignal, Vector3D>> satellitesPhaseCenters;
41  
42      /** Stations phase centers. */
43      private final Map<AntennaKey, Map<GnssSignal, Vector3D>> stationsPhaseCenters;
44  
45      /** Station data. */
46      private final Map<String, Station> stations;
47  
48      /** Earth Orientation Parameters data. */
49      private final Map<AbsoluteDate, SinexEopEntry> eop;
50  
51      /** Station position X coordinate. */
52      private double px;
53  
54      /** Station position Y coordinate. */
55      private double py;
56  
57      /** Station position Z coordinate. */
58      private double pz;
59  
60      /** Station velocity X coordinate. */
61      private double vx;
62  
63      /** Station velocity Y coordinate. */
64      private double vy;
65  
66      /** Station velocity Z coordinate. */
67      private double vz;
68  
69      /** Correction axis. */
70      private PsdCorrection.Axis axis;
71  
72      /** Correction time evolution. */
73      private PsdCorrection.TimeEvolution evolution;
74  
75      /** Correction amplitude. */
76      private double amplitude;
77  
78      /** Correction relaxation time. */
79      private double relaxationTime;
80  
81      /** Phase centers. */
82      private final Map<GnssSignal, Vector3D> phaseCenters;
83  
84      /** Simple constructor.
85       * @param timeScales time scales
86       */
87      SinexParseInfo(final TimeScales timeScales) {
88          super(timeScales);
89          this.satellitesPhaseCenters = new HashMap<>();
90          this.stationsPhaseCenters   = new HashMap<>();
91          this.stations               = new HashMap<>();
92          this.eop                    = new HashMap<>();
93          this.phaseCenters           = new HashMap<>();
94      }
95  
96      /** {@inheritDoc} */
97      @Override
98      void newSource(final String name) {
99          super.newSource(name);
100         resetPosition();
101         resetVelocity();
102         resetPsdCorrection();
103     }
104 
105     /** Add satellite phase center.
106      * @param satInSystem satellite id
107      * @param signal signal
108      * @param phaseCenter phase center
109      */
110     void addSatellitePhaseCenter(final SatInSystem satInSystem, final GnssSignal signal, final Vector3D phaseCenter) {
111         satellitesPhaseCenters.
112             computeIfAbsent(satInSystem, s -> new HashMap<>()).
113             put(signal, phaseCenter);
114     }
115 
116     /** Add station phase center.
117      * @param key antenna key
118      * @param phaseCenter phase center
119      * @param signals signals to use in order
120      */
121     void addStationPhaseCenter(final AntennaKey key, final Vector3D phaseCenter, final GnssSignal[] signals) {
122         phaseCenters.put(signals[phaseCenters.size()], phaseCenter);
123         if (phaseCenters.size() == signals.length) {
124             // we have parsed all expected signals
125             stationsPhaseCenters.computeIfAbsent(key, k -> new HashMap<>()).putAll(phaseCenters);
126             phaseCenters.clear();
127         }
128     }
129 
130     /** Add station.
131      * @param station station to add
132      */
133     void addStation(final Station station) {
134         stations.putIfAbsent(station.getSiteCode(), station);
135     }
136 
137     /** Get station from current line.
138      * @param index index of station in current line
139      * @return station
140      */
141     Station getCurrentLineStation(final int index) {
142         return stations.get(parseString(index, 4));
143     }
144 
145     /** Get start date from current line.
146      * @return start date
147      */
148     AbsoluteDate getCurrentLineStartDate() {
149         return stringEpochToAbsoluteDate(parseString(16, 12), true);
150     }
151 
152     /** Get end date from current line.
153      * @return end date
154      */
155     AbsoluteDate getCurrentLineEndDate() {
156         return stringEpochToAbsoluteDate(parseString(29, 12), false);
157     }
158 
159     /** Set station position X coordinate.
160      * @param x station position X coordinate
161      * @param station station
162      * @param epoch   coordinates epoch
163      */
164     void setPx(final double x, final Station station, final AbsoluteDate epoch) {
165         this.px = x;
166         finalizePositionIfComplete(station, epoch);
167     }
168 
169     /** Set station position Y coordinate.
170      * @param y station position Y coordinate
171      * @param station station
172      * @param epoch   coordinates epoch
173      */
174     void setPy(final double y, final Station station, final AbsoluteDate epoch) {
175         this.py = y;
176         finalizePositionIfComplete(station, epoch);
177     }
178 
179     /** Set station position Z coordinate.
180      * @param z station position Z coordinate
181      * @param station station
182      * @param epoch   coordinates epoch
183      */
184     void setPz(final double z, final Station station, final AbsoluteDate epoch) {
185         this.pz = z;
186         finalizePositionIfComplete(station, epoch);
187     }
188 
189     /** Finalize station position if complete.
190      * @param station station
191      * @param epoch   coordinates epoch
192      */
193     private void finalizePositionIfComplete(final Station station, final AbsoluteDate epoch) {
194         if (!Double.isNaN(px + py + pz)) {
195             // all coordinates are available, position is complete
196             station.setEpoch(epoch);
197             station.setPosition(new Vector3D(px, py, pz));
198             resetPosition();
199         }
200     }
201 
202     /** Reset position.
203      */
204     void resetPosition() {
205         px = Double.NaN;
206         py = Double.NaN;
207         pz = Double.NaN;
208     }
209 
210     /** Set station velocity X coordinate.
211      * @param x station velocity X coordinate
212      * @param station station
213      */
214     void setVx(final double x, final Station station) {
215         this.vx = x;
216         finalizeVelocityIfComplete(station);
217     }
218 
219     /** Set station velocity Y coordinate.
220      * @param y station velocity Y coordinate
221      * @param station station
222      */
223     void setVy(final double y, final Station station) {
224         this.vy = y;
225         finalizeVelocityIfComplete(station);
226     }
227 
228     /** Set station velocity Z coordinate.
229      * @param z station velocity Z coordinate
230      * @param station station
231      */
232     void setVz(final double z, final Station station) {
233         this.vz = z;
234         finalizeVelocityIfComplete(station);
235     }
236 
237     /** Finalize station velocity if complete.
238      * @param station station
239      */
240     private void finalizeVelocityIfComplete(final Station station) {
241         if (!Double.isNaN(vx + vy + vz)) {
242             // all coordinates are available, velocity is complete
243             station.setVelocity(new Vector3D(vx, vy, vz));
244             resetVelocity();
245         }
246     }
247 
248     /** Reset velocity.
249      */
250     void resetVelocity() {
251         vx = Double.NaN;
252         vy = Double.NaN;
253         vz = Double.NaN;
254     }
255 
256     /** Set correction axis.
257      * @param axis correction axis
258      */
259     void setAxis(final PsdCorrection.Axis axis) {
260         this.axis = axis;
261     }
262 
263     /** Set correction time evolution.
264      * @param evolution correction time evolution
265      */
266     void setEvolution(final PsdCorrection.TimeEvolution evolution) {
267         this.evolution = evolution;
268     }
269 
270     /** Set correction amplitude.
271      * @param correctionAmplitude correction amplitude
272      * @param station station
273      * @param epoch   coordinates epoch
274      */
275     void setAmplitude(final double correctionAmplitude, final Station station, final AbsoluteDate epoch) {
276         this.amplitude = correctionAmplitude;
277         finalizePsdCorrectionIfComplete(station, epoch);
278     }
279 
280     /** Set correction relaxation time.
281      * @param correctionRelaxationTime correction relaxation time
282      * @param station station
283      * @param epoch   coordinates epoch
284      */
285     void setRelaxationTime(final double correctionRelaxationTime,
286                            final Station station, final AbsoluteDate epoch) {
287         this.relaxationTime = correctionRelaxationTime;
288         finalizePsdCorrectionIfComplete(station, epoch);
289     }
290 
291     /** Finalize a Post-Seismic Deformation correction model if complete.
292      * @param station station
293      * @param epoch   coordinates epoch
294      */
295     private void finalizePsdCorrectionIfComplete(final Station station, final AbsoluteDate epoch) {
296         if (!Double.isNaN(amplitude + relaxationTime)) {
297             // both amplitude and relaxation time are available, correction is complete
298             final PsdCorrection correction = new PsdCorrection(axis, evolution, epoch, amplitude, relaxationTime);
299             station.addPsdCorrectionValidAfter(correction, epoch);
300             resetPsdCorrection();
301         }
302     }
303 
304     /** Reset Post-Seismic Deformation correction model.
305      */
306     private void resetPsdCorrection() {
307         axis           = null;
308         evolution      = null;
309         amplitude      = Double.NaN;
310         relaxationTime = Double.NaN;
311     }
312 
313     /** Create EOP entry.
314      * @param date EOP date
315      * @return EOP entry at date, creating it if needed
316      */
317     SinexEopEntry createEOPEntry(final AbsoluteDate date) {
318         return eop.computeIfAbsent(date, SinexEopEntry::new);
319     }
320 
321     /** {@inheritDoc} */
322     @Override
323     protected Sinex build() {
324 
325         // set up phase centers for stations
326         for (final Station station : stations.values()) {
327 
328             // time span map we need to populate
329             final TimeSpanMap<Map<GnssSignal, Vector3D>> phaseCentersMap = station.getPhaseCentersMap();
330 
331             if (station.getAntennaKeyTimeSpanMap().getSpansNumber() > 1) {
332                 for (TimeSpanMap.Span<AntennaKey> keySpan = station.getAntennaKeyTimeSpanMap().getFirstNonNullSpan();
333                      keySpan != null; keySpan = keySpan.next()) {
334 
335                     // get the existing map for this span
336                     Map<GnssSignal, Vector3D> centers =
337                         phaseCentersMap.get(AbsoluteDate.createMedian(keySpan.getStart(), keySpan.getEnd()));
338                     if (centers == null) {
339                         // this is the first time we process this time span
340                         centers = new HashMap<>();
341                         phaseCentersMap.addValidBetween(centers, keySpan.getStart(), keySpan.getEnd());
342                     }
343 
344                     if (keySpan.getData() != null) {
345 
346                         // try to identify the closest match for antenna
347                         AntennaKey closestKey = null;
348                         for (final AntennaKey candidate : keySpan.getData().matchingCandidates()) {
349                             if (stationsPhaseCenters.containsKey(candidate)) {
350                                 closestKey = candidate;
351                                 break;
352                             }
353                         }
354                         if (closestKey == null) {
355                             throw new OrekitException(OrekitMessages.UNKNOWN_GNSS_ANTENNA,
356                                                       keySpan.getData().getName(),
357                                                       keySpan.getData().getRadomeCode(),
358                                                       keySpan.getData().getSerialNumber());
359                         }
360 
361                         // add the phase centers for the closest key
362                         centers.putAll(stationsPhaseCenters.get(closestKey));
363 
364                     }
365 
366                 }
367             }
368 
369         }
370 
371         return new Sinex(getTimeScales(), getCreationDate(), getStartDate(), getEndDate(),
372                          satellitesPhaseCenters, stations, eop);
373     }
374 
375 }