1 /* Copyright 2002-2024 Thales Alenia Space
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.rinex.observation;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22
23 import org.hipparchus.util.FastMath;
24 import org.orekit.errors.OrekitIllegalArgumentException;
25 import org.orekit.errors.OrekitMessages;
26 import org.orekit.files.rinex.RinexFile;
27 import org.orekit.time.AbsoluteDate;
28 import org.orekit.time.ClockOffset;
29 import org.orekit.time.SampledClockModel;
30
31 /** Container for Rinex observation file.
32 * @author Luc Maisonobe
33 * @since 12.0
34 */
35 public class RinexObservation extends RinexFile<RinexObservationHeader> {
36
37 /** Observations. */
38 private final List<ObservationDataSet> observations;
39
40 /** Simple constructor.
41 */
42 public RinexObservation() {
43 super(new RinexObservationHeader());
44 this.observations = new ArrayList<>();
45 }
46
47 /** Get an unmodifiable view of the observations.
48 * @return unmodifiable view of the observations
49 */
50 public List<ObservationDataSet> getObservationDataSets() {
51 return Collections.unmodifiableList(observations);
52 }
53
54 /** Add an observations data set.
55 * <p>
56 * Observations must be added chronologically, within header date range, and separated
57 * by an integer multiple of the {@link RinexObservationHeader#getInterval() interval}
58 * (ideally one interval, but entries at same dates and missing entries are allowed so
59 * any non-negative integer is allowed).
60 * </p>
61 * @param observationsDataSet observations data set
62 */
63 public void addObservationDataSet(final ObservationDataSet observationsDataSet) {
64
65 final RinexObservationHeader header = getHeader();
66 final AbsoluteDate current = observationsDataSet.getDate();
67
68 // check interval from previous observation
69 if (!observations.isEmpty()) {
70 final AbsoluteDate previous = observations.get(observations.size() - 1).getDate();
71 final double factor = current.durationFrom(previous) / header.getInterval();
72 final double acceptable = FastMath.max(0.0, FastMath.rint(factor));
73 if (FastMath.abs(factor - acceptable) > 0.01) {
74 throw new OrekitIllegalArgumentException(OrekitMessages.INCONSISTENT_SAMPLING_DATE,
75 previous.shiftedBy(acceptable * header.getInterval()),
76 current);
77 }
78 }
79
80 // check global range
81 final AbsoluteDate first = header.getTFirstObs();
82 final AbsoluteDate last = header.getTLastObs();
83 if (!current.isBetweenOrEqualTo(first, last)) {
84 throw new OrekitIllegalArgumentException(OrekitMessages.OUT_OF_RANGE_DATE,
85 current, first, last);
86 }
87
88 observations.add(observationsDataSet);
89
90 }
91
92 /** Extract the receiver clock model.
93 * @param nbInterpolationPoints number of points to use in interpolation
94 * @return extracted clock model or null if all {@link
95 * ObservationDataSet#getRcvrClkOffset() clock offsets} are zero
96 * @since 12.1
97 */
98 public SampledClockModel extractClockModel(final int nbInterpolationPoints) {
99 final List<ClockOffset> sample = new ArrayList<>();
100 boolean someNonZero = false;
101 AbsoluteDate previous = null;
102 for (final ObservationDataSet ods : observations) {
103 if (previous == null || ods.getDate().durationFrom(previous) > 0.5 * getHeader().getInterval()) {
104 // this is a new date
105 sample.add(new ClockOffset(ods.getDate(), ods.getRcvrClkOffset(),
106 Double.NaN, Double.NaN));
107 someNonZero |= ods.getRcvrClkOffset() != 0;
108 }
109 previous = ods.getDate();
110 }
111
112 // build a clock model only if at least some non-zero offsets have been found
113 return someNonZero ?
114 new SampledClockModel(sample, nbInterpolationPoints) :
115 null;
116
117 }
118
119 }