Station.java

/* Copyright 2002-2024 CS GROUP
 * 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.sinex;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.models.earth.displacement.PsdCorrection;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.TimeSpanMap;

/**
 * Station model.
 * <p>
 * Since Orekit 11.1, this class handles multiple site antenna
 * eccentricity.
 * The {@link #getEccentricities(AbsoluteDate)} method can be
 * used to access the site antenna eccentricity values for a
 * given epoch.
 * </p>
 * @author Bryan Cazabonne
 * @since 10.3
 */
public class Station {

    /** Site code. */
    private String siteCode;

    /** DOMES number. */
    private String domes;

    /** Start of validity. */
    private AbsoluteDate validFrom;

    /** End of validity. */
    private AbsoluteDate validUntil;

    /** Eccentricity reference system. */
    private ReferenceSystem eccRefSystem;

    /** TimeSpanMap of site antenna eccentricities. */
    private final TimeSpanMap<Vector3D> eccentricitiesTimeSpanMap;

    /** Antenna type.
     * @since 12.0
     */
    private final TimeSpanMap<String> antennaTypesMap;

    /** Post-Seismic Deformation.
     * @since 12.0
     */
    private final TimeSpanMap<List<PsdCorrection>> psdMap;

    /** Station position. */
    private Vector3D position;

    /** Station velocity. */
    private Vector3D velocity;

    /** Coordinates reference epoch. */
    private AbsoluteDate epoch;

    /**
     * Constructor.
     */
    public Station() {
        this.eccentricitiesTimeSpanMap = new TimeSpanMap<>(null);
        this.antennaTypesMap           = new TimeSpanMap<>(null);
        this.psdMap                    = new TimeSpanMap<>(null);
        this.position                  = Vector3D.ZERO;
        this.velocity                  = Vector3D.ZERO;
    }

    /**
     * Get the site code (station identifier).
     * @return the site code
     */
    public String getSiteCode() {
        return siteCode;
    }

    /**
     * Set the site code (station identifier).
     * @param siteCode the site code to set
     */
    public void setSiteCode(final String siteCode) {
        this.siteCode = siteCode;
    }

    /**
     * Get the site DOMES number.
     * @return the DOMES number
     */
    public String getDomes() {
        return domes;
    }

    /**
     * Set the DOMES number.
     * @param domes the DOMES number to set
     */
    public void setDomes(final String domes) {
        this.domes = domes;
    }

    /**
     * Get start of validity.
     * @return start of validity
     */
    public AbsoluteDate getValidFrom() {
        return validFrom;
    }

    /**
     * Set the start of validity.
     * @param validFrom the start of validity to set
     */
    public void setValidFrom(final AbsoluteDate validFrom) {
        this.validFrom = validFrom;
    }

    /**
     * Get end of validity.
     * @return end of validity
     */
    public AbsoluteDate getValidUntil() {
        return validUntil;
    }

    /**
     * Set the end of validity.
     * @param validUntil the end of validity to set
     */
    public void setValidUntil(final AbsoluteDate validUntil) {
        this.validUntil = validUntil;
    }

    /**
     * Get the reference system used to define the eccentricity vector (local or cartesian).
     * @return the reference system used to define the eccentricity vector
     */
    public ReferenceSystem getEccRefSystem() {
        return eccRefSystem;
    }

    /**
     * Set the reference system used to define the eccentricity vector (local or cartesian).
     * @param eccRefSystem the reference system used to define the eccentricity vector
     */
    public void setEccRefSystem(final ReferenceSystem eccRefSystem) {
        this.eccRefSystem = eccRefSystem;
    }

    /**
     * Get the station antenna eccentricities for the given epoch.
     * <p>
     * Vector convention: X-Y-Z or UP-NORTH-EAST.
     * See {@link #getEccRefSystem()} method.
     * <p>
     * If there is no eccentricity values for the given epoch, an
     * exception is thrown.
     * @param date epoch
     * @return station antenna eccentricities (m)
     * @since 11.1
     */
    public Vector3D getEccentricities(final AbsoluteDate date) {
        final Vector3D eccAtEpoch = eccentricitiesTimeSpanMap.get(date);
        // If the entry is null, there is no valid eccentricity values for the input epoch
        if (eccAtEpoch == null) {
            // Throw an exception
            throw new OrekitException(OrekitMessages.MISSING_STATION_DATA_FOR_EPOCH, date);
        }
        return eccAtEpoch;
    }

    /**
     * Get the TimeSpanMap of site antenna eccentricities.
     * @return the TimeSpanMap of site antenna eccentricities
     * @since 11.1
     */
    public TimeSpanMap<Vector3D> getEccentricitiesTimeSpanMap() {
        return eccentricitiesTimeSpanMap;
    }

    /** Add a station eccentricity vector entry valid before a limit date.<br>
     * Using <code>addStationEccentricitiesValidBefore(entry, t)</code> will make <code>entry</code>
     * valid in ]-∞, t[ (note the open bracket).
     * @param entry station eccentricity vector entry
     * @param latestValidityDate date before which the entry is valid
     * (must be different from <b>all</b> dates already used for transitions)
     * @since 11.1
     */
    public void addStationEccentricitiesValidBefore(final Vector3D entry, final AbsoluteDate latestValidityDate) {
        eccentricitiesTimeSpanMap.addValidBefore(entry, latestValidityDate, false);
    }

    /** Add a station eccentricity vector entry valid after a limit date.<br>
     * Using <code>addStationEccentricitiesValidAfter(entry, t)</code> will make <code>entry</code>
     * valid in [t, +∞[ (note the closed bracket).
     * @param entry station eccentricity vector entry
     * @param earliestValidityDate date after which the entry is valid
     * (must be different from <b>all</b> dates already used for transitions)
     * @since 11.1
     */
    public void addStationEccentricitiesValidAfter(final Vector3D entry, final AbsoluteDate earliestValidityDate) {
        eccentricitiesTimeSpanMap.addValidAfter(entry, earliestValidityDate, false);
    }

    /** Get the TimeSpanMap of Post-Seismic Deformation.
     * @return the TimeSpanMap of Post-Seismic Deformation
     * @since 12.1
     */
    public TimeSpanMap<List<PsdCorrection>> getPsdTimeSpanMap() {
        return psdMap;
    }

    /** Add a Post-Seismic Deformation entry valid after a limit date.<br>
     * Using {@code addPsdCorrectionValidAfter(entry, t)} will make {@code entry}
     * valid in [t, +∞[ (note the closed bracket).
     * @param entry Post-Seismic Deformation entry
     * @param earliestValidityDate date after which the entry is valid
     * (must be different from <b>all</b> dates already used for transitions)
     * @since 12.1
     */
    public void addPsdCorrectionValidAfter(final PsdCorrection entry, final AbsoluteDate earliestValidityDate) {

        // get the list of corrections active just after earthquake date
        List<PsdCorrection> corrections = psdMap.get(earliestValidityDate.shiftedBy(1.0e-3));

        if (corrections == null ||
            earliestValidityDate.durationFrom(corrections.get(0).getEarthquakeDate()) > 1.0e-3) {
            // either this is the first earthquake we consider or
            // this earthquake is after another one already considered
            // we need to create a new list of corrections for this new earthquake
            corrections = new ArrayList<>();
            psdMap.addValidAfter(corrections, earliestValidityDate, false);
        }

        // add the entry to the current list
        corrections.add(entry);

    }

    /**
     * Get the antenna type for the given epoch.
     * If there is no antenna types for the given epoch, an
     * exception is thrown.
     * @param date epoch
     * @return antenna type
     * @since 12.0
     */
    public String getAntennaType(final AbsoluteDate date) {
        final String typeAtEpoch = antennaTypesMap.get(date);
        // If the entry is null, there is no valid type for the input epoch
        if (typeAtEpoch == null) {
            // Throw an exception
            throw new OrekitException(OrekitMessages.MISSING_STATION_DATA_FOR_EPOCH, date);
        }
        return typeAtEpoch;
    }

    /**
     * Get the TimeSpanMap of site antenna type.
     * @return the TimeSpanMap of site antenna type
     * @since 12.0
     */
    public TimeSpanMap<String> getAntennaTypeTimeSpanMap() {
        return antennaTypesMap;
    }

    /** Add a antenna type entry valid before a limit date.<br>
     * Using <code>addAntennaTypeValidBefore(entry, t)</code> will make <code>entry</code>
     * valid in ]-∞, t[ (note the open bracket).
     * @param entry antenna type entry
     * @param latestValidityDate date before which the entry is valid
     * (must be different from <b>all</b> dates already used for transitions)
     * @since 12.0
     */
    public void addAntennaTypeValidBefore(final String entry, final AbsoluteDate latestValidityDate) {
        antennaTypesMap.addValidBefore(entry, latestValidityDate, false);
    }

    /** Add a antenna type entry valid after a limit date.<br>
     * Using <code>addAntennaTypeValidAfter(entry, t)</code> will make <code>entry</code>
     * valid in [t, +∞[ (note the closed bracket).
     * @param entry antenna type entry
     * @param earliestValidityDate date after which the entry is valid
     * (must be different from <b>all</b> dates already used for transitions)
     * @since 12.0
     */
    public void addAntennaTypeValidAfter(final String entry, final AbsoluteDate earliestValidityDate) {
        antennaTypesMap.addValidAfter(entry, earliestValidityDate, false);
    }

    /**
     * Get the station position.
     * @return the station position (m)
     */
    public Vector3D getPosition() {
        return position;
    }

    /**
     * Set the station position.
     * @param position the position to set
     */
    public void setPosition(final Vector3D position) {
        this.position = position;
    }

    /**
     * Get the station velocity.
     * @return the station velocity (m/s)
     */
    public Vector3D getVelocity() {
        return velocity;
    }

    /**
     * Set the station velocity.
     * @param velocity the velocity to set
     */
    public void setVelocity(final Vector3D velocity) {
        this.velocity = velocity;
    }

    /**
     * Get the coordinates reference epoch.
     * @return the coordinates reference epoch
     */
    public AbsoluteDate getEpoch() {
        return epoch;
    }

    /**
     * Set the coordinates reference epoch.
     * @param epoch the epoch to set
     */
    public void setEpoch(final AbsoluteDate epoch) {
        this.epoch = epoch;
    }

    /** Eccentricity reference system. */
    public enum ReferenceSystem {

        /** Local reference system Up, North, East. */
        UNE("UNE"),

        /** Cartesian reference system X, Y, Z. */
        XYZ("XYZ");

        /** Codes map. */
        private static final Map<String, ReferenceSystem> CODES_MAP = new HashMap<>();
        static {
            for (final ReferenceSystem type : values()) {
                CODES_MAP.put(type.getName(), type);
            }
        }

        /** Name used to define the reference system in SINEX file. */
        private final String name;

        /**
         * Constructor.
         * @param name name used to define the reference system in SINEX file
         */
        ReferenceSystem(final String name) {
            this.name = name;
        }

        /**
         * Get the name used to define the reference system in SINEX file.
         * @return the name
         */
        public String getName() {
            return name;
        }

        /**
         * Get the eccentricity reference system corresponding to the given value.
         * @param value given value
         * @return the corresponding eccentricity reference system
         */
        public static ReferenceSystem getEccRefSystem(final String value) {
            return CODES_MAP.get(value);
        }

    }

}