RinexObservation.java
- /* Copyright 2022-2025 Thales Alenia Space
- * Licensed to CS GROUP (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.orekit.files.rinex.observation;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Iterator;
- import java.util.List;
- import org.hipparchus.util.FastMath;
- import org.orekit.errors.OrekitIllegalArgumentException;
- import org.orekit.errors.OrekitMessages;
- import org.orekit.files.rinex.RinexFile;
- import org.orekit.time.AbsoluteDate;
- import org.orekit.time.ClockOffset;
- import org.orekit.time.SampledClockModel;
- /** Container for Rinex observation file.
- * @author Luc Maisonobe
- * @since 12.0
- */
- public class RinexObservation extends RinexFile<RinexObservationHeader> {
- /** Observations. */
- private final List<ObservationDataSet> observations;
- /** Simple constructor.
- */
- public RinexObservation() {
- super(new RinexObservationHeader());
- this.observations = new ArrayList<>();
- }
- /** Get an unmodifiable view of the observations.
- * @return unmodifiable view of the observations
- * @see #bundleByDates()
- */
- public List<ObservationDataSet> getObservationDataSets() {
- return Collections.unmodifiableList(observations);
- }
- /** Get an iterable view of observations bundled by common date.
- * <p>
- * The observations are the same as the ones provided by {@link #getObservationDataSets()},
- * but instead of one single list covering the whole Rinex file, several lists
- * are made available, all observations withing each list sharing a common date
- * </p>
- * @return an iterable view of observations bundled by common date
- * @see #getObservationDataSets()
- * @since 13.0
- */
- public Iterable<List<ObservationDataSet>> bundleByDates() {
- return BundlingIterator::new;
- }
- /** Add an observations data set.
- * <p>
- * Observations must be added chronologically, within header date range, and separated
- * by an integer multiple of the {@link RinexObservationHeader#getInterval() interval}
- * (ideally one interval, but entries at same dates and missing entries are allowed so
- * any non-negative integer is allowed).
- * </p>
- * @param observationsDataSet observations data set
- */
- public void addObservationDataSet(final ObservationDataSet observationsDataSet) {
- final RinexObservationHeader header = getHeader();
- final AbsoluteDate current = observationsDataSet.getDate();
- // check interval from previous observation
- if (!observations.isEmpty()) {
- final AbsoluteDate previous = observations.get(observations.size() - 1).getDate();
- final double factor = current.durationFrom(previous) / header.getInterval();
- final double acceptable = FastMath.max(0.0, FastMath.rint(factor));
- if (FastMath.abs(factor - acceptable) > 0.01) {
- throw new OrekitIllegalArgumentException(OrekitMessages.INCONSISTENT_SAMPLING_DATE,
- previous.shiftedBy(acceptable * header.getInterval()),
- current);
- }
- }
- // check global range
- final AbsoluteDate first = header.getTFirstObs();
- final AbsoluteDate last = header.getTLastObs();
- if (!current.isBetweenOrEqualTo(first, last)) {
- throw new OrekitIllegalArgumentException(OrekitMessages.OUT_OF_RANGE_DATE,
- current, first, last);
- }
- observations.add(observationsDataSet);
- }
- /** Extract the receiver clock model.
- * @param nbInterpolationPoints number of points to use in interpolation
- * @return extracted clock model or null if all {@link
- * ObservationDataSet#getRcvrClkOffset() clock offsets} are zero
- * @since 12.1
- */
- public SampledClockModel extractClockModel(final int nbInterpolationPoints) {
- final List<ClockOffset> sample = new ArrayList<>();
- boolean someNonZero = false;
- AbsoluteDate previous = null;
- for (final ObservationDataSet ods : observations) {
- if (previous == null || ods.getDate().durationFrom(previous) > 0.5 * getHeader().getInterval()) {
- // this is a new date
- sample.add(new ClockOffset(ods.getDate(), ods.getRcvrClkOffset(),
- Double.NaN, Double.NaN));
- someNonZero |= ods.getRcvrClkOffset() != 0;
- }
- previous = ods.getDate();
- }
- // build a clock model only if at least some non-zero offsets have been found
- return someNonZero ?
- new SampledClockModel(sample, nbInterpolationPoints) :
- null;
- }
- /** Iterator providing {@link ObservationDataSet} bundled by dates.
- * @since 13.0
- */
- private class BundlingIterator implements Iterator<List<ObservationDataSet>> {
- /** Ratio for dates comparisons tolerance. */
- private static final double RATIO = 0.01;
- /** Tolerance for dates comparisons. */
- private final double tolerance;
- /** Index of next bundle. */
- private int next;
- /** Build an iterator starting at first observations data set.
- */
- BundlingIterator() {
- this.tolerance = RATIO * getHeader().getInterval();
- this.next = 0;
- }
- /** {@inheritDoc} */
- @Override
- public boolean hasNext() {
- return next < observations.size();
- }
- /** {@inheritDoc} */
- @Override
- public List<ObservationDataSet> next() {
- // common date for all observation data sets in this bundle
- final AbsoluteDate bundleDate = observations.get(next).getDate();
- final int start = next;
- while (next < observations.size() &&
- FastMath.abs(observations.get(next).getDate().durationFrom(bundleDate)) <= tolerance) {
- // we can include next observation in the current bundle
- ++next;
- }
- // return the bundle of observations that share the same date
- return observations.subList(start, next);
- }
- }
- }