AntennaKey.java

  1. /* Copyright 2002-2025 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.sinex;

  18. import java.util.Arrays;
  19. import java.util.List;

  20. /** Key for antenna.
  21.  * @author Luc Maisonobe
  22.  * @since 13.0
  23.  */
  24. public class AntennaKey {

  25.     /** Constant matching other radome codes. */
  26.     public static final String OTHER_RADOME_CODE = "NONE";

  27.     /** Constant matching any serial numbers. */
  28.     public static final String ANY_SERIAL_NUMBER = "-----";

  29.     /** Antenna name. */
  30.     private final String name;

  31.     /** Radome code. */
  32.     private final String radomeCode;

  33.     /** Serial number. */
  34.     private final String serialNumber;

  35.     /** Simple constructor.
  36.      * <p>
  37.      * The Sinex file specification uses a single 20 characters field named "Antenna type"
  38.      * and described as "Antenna name and model" (Antex specification is similar). In
  39.      * practice this field contains a variable length name and the last four characters are
  40.      * a radome code, which may be set to {@link #OTHER_RADOME_CODE "NONE"} for a catch-all
  41.      * entry. Here, we separate this field into its two components, so we can provide
  42.      * {@link #matchingCandidates() fuzzy matching} by tweaking the radome code if needed.
  43.      * </p>
  44.      * @param name antenna name
  45.      * @param radomeCode radome code
  46.      * @param serialNumber serial number
  47.      */
  48.     public AntennaKey(final String name, final String radomeCode, final String serialNumber) {
  49.         this.name         = name;
  50.         this.radomeCode   = radomeCode;
  51.         this.serialNumber = serialNumber;
  52.     }

  53.     /** Get candidates for fuzzy matching of this antenna key.
  54.      * <p>
  55.      * Some Sinex files use specific keys in the SITE/ANTENNA block and catch-all
  56.      * keys in the SITE/GPS_PHASE_CENTER, SITE/GAL_PHASE_CENTER blocks. As
  57.      * an example, file JAX0MGXFIN_20202440000_01D_000_SOL.SNX contains the
  58.      * following entries related to antenna type ASH700936D_M:
  59.      * </p>
  60.      * <pre>
  61.      * SITE/ANTENNA
  62.      * AMU2  A ---- P 00:000:00000 00:000:00000 ASH700936D_M    SCIS 13569
  63.      * ARTU  A ---- P 00:000:00000 00:000:00000 ASH700936D_M    DOME CR130
  64.      * DRAG  A ---- P 00:000:00000 00:000:00000 ASH700936D_M    SNOW CR143
  65.      * PALM  A ---- P 00:000:00000 00:000:00000 ASH700936D_M    SCIS CR141
  66.      * SITE/GPS_PHASE_CENTER
  67.      * ASH700936D_M    NONE -----  .0910  .0004 -.0003  .1204 -.0001 -.0001 igs14_%Y%m
  68.      * ASH700936D_M    SCIS -----  .0879  .0005 -.0001  .1192  .0001 -.0001 igs14_%Y%m
  69.      * ASH700936D_M    SNOW -----  .0909  .0003 -.0002  .1192  .0001  .0001 igs14_%Y%m
  70.      * </pre>
  71.      * <p>
  72.      * Apart from the obvious formatting error of the last field in SITE/GPS_PHASE_CENTER,
  73.      * it appears there are no phase center data for the antenna used at ARTU site, because
  74.      * no radome code match "DOME". We consider here that a "close enough" entry would be
  75.      * to use {@link #OTHER_RADOME_CODE "NONE"} as the radome code, and {@link #ANY_SERIAL_NUMBER "-----"}
  76.      * as the serial number.
  77.      * </p>
  78.      * <p>
  79.      * Another example is file ESA0OPSFIN_20241850000_01D_01D_SOL.SNX which contains the
  80.      * following entries related to antenna type ASH701945G_M:
  81.      * </p>
  82.      * <pre>
  83.      * SITE/ANTENNA
  84.      * FAIR  A    1 P 24:184:86382 24:185:86382 ASH701945G_M    JPLA CR520    0
  85.      * KOKB  A    1 P 24:184:86382 24:185:86382 ASH701945G_M    NONE CR620    0
  86.      * SUTH  A    1 P 24:184:86382 24:185:86382 ASH701945G_M    NONE CR620    0
  87.      * SITE/GPS_PHASE_CENTER
  88.      * ASH701945G_M    NONE CR520 0.0895 0.0001 -.0001 0.1162 -.0007 -.0001 IGS20_2317
  89.      * ASH701945G_M    NONE CR620 0.0895 0.0001 -.0001 0.1162 -.0007 -.0001 IGS20_2317
  90.      * ASH701945G_M    NONE CR620 0.0895 0.0001 -.0001 0.1162 -.0007 -.0001 IGS20_2317
  91.      * SITE/GAL_PHASE_CENTER
  92.      * ASH701945G_M    NONE CR520 0.0895 0.0001 -.0001 0.1162 -.0007 -.0001 IGS20_2317
  93.      * ASH701945G_M    NONE CR520 0.1162 -.0007 -.0001 0.1162 -.0007 -.0001 IGS20_2317
  94.      * ASH701945G_M    NONE CR520 0.1162 -.0007 -.0001                      IGS20_2317
  95.      * ASH701945G_M    NONE CR620 0.0895 0.0001 -.0001 0.1162 -.0007 -.0001 IGS20_2317
  96.      * ASH701945G_M    NONE CR620 0.1162 -.0007 -.0001 0.1162 -.0007 -.0001 IGS20_2317
  97.      * ASH701945G_M    NONE CR620 0.1162 -.0007 -.0001                      IGS20_2317
  98.      * ASH701945G_M    NONE CR620 0.0895 0.0001 -.0001 0.1162 -.0007 -.0001 IGS20_2317
  99.      * ASH701945G_M    NONE CR620 0.1162 -.0007 -.0001 0.1162 -.0007 -.0001 IGS20_2317
  100.      * ASH701945G_M    NONE CR620 0.1162 -.0007 -.0001                      IGS20_2317
  101.      * </pre>
  102.      * <p>
  103.      * Here, the phase centers for serial number CR620 appear twice (fortunately with
  104.      * the same values). There are no phase center data for the antenna used at FAIR site,
  105.      * because no radome code match "JPLA". We consider here that a "close enough" entry
  106.      * would be to use {@link #OTHER_RADOME_CODE "NONE"}, and keep the provided serial number.
  107.      * </p>
  108.      * <p>
  109.      * The logic we adopted is to use the following candidates:
  110.      * </p>
  111.      * <table border="1" style="background-color:#f5f5dc;">
  112.      * <caption>Antenna key matching candidates</caption>
  113.      * <tr style="background-color:#c9d5c9;"><th>order</th><th>name</th><th>radome code</th><th>serial number</th></tr>
  114.      * <tr><td style="background-color:#c9d5c9; padding:5px">first candidate</td>
  115.      *     <td>{@link #getName()}</td><td>{@link #getRadomeCode()} </td><td>{@link #getSerialNumber()}</td></tr>
  116.      * <tr><td style="background-color:#c9d5c9; padding:5px">second candidate</td>
  117.      *     <td>{@link #getName()}</td><td>{@link #getRadomeCode()} </td><td>{@link #ANY_SERIAL_NUMBER "-----"}</td></tr>
  118.      * <tr><td style="background-color:#c9d5c9; padding:5px">third candidate</td>
  119.      *     <td>{@link #getName()}</td><td>{@link #OTHER_RADOME_CODE "NONE"} </td><td>{@link #getSerialNumber()}</td></tr>
  120.      * <tr><td style="background-color:#c9d5c9; padding:5px">fourth candidate</td>
  121.      *     <td>{@link #getName()}</td><td>{@link #OTHER_RADOME_CODE "NONE"} </td><td>{@link #ANY_SERIAL_NUMBER "-----"}</td></tr>
  122.      * </table>
  123.      * @return candidates for matching instance key, sorted from stricter to looser match
  124.      */
  125.     public List<AntennaKey> matchingCandidates() {
  126.         return Arrays.asList(this,
  127.                              new AntennaKey(getName(), radomeCode,        ANY_SERIAL_NUMBER),
  128.                              new AntennaKey(getName(), OTHER_RADOME_CODE, serialNumber),
  129.                              new AntennaKey(getName(), OTHER_RADOME_CODE, ANY_SERIAL_NUMBER));
  130.     }

  131.     /** Get the antenna name.
  132.      * @return antenna name
  133.      */
  134.     public String getName() {
  135.         return name;
  136.     }

  137.     /** Get the radome code.
  138.      * @return radome code
  139.      */
  140.     public String getRadomeCode() {
  141.         return radomeCode;
  142.     }

  143.     /** Get the serial number.
  144.      * @return serial number
  145.      */
  146.     public String getSerialNumber() {
  147.         return serialNumber;
  148.     }

  149.     @Override
  150.     public boolean equals(final Object object) {
  151.         if (object instanceof AntennaKey) {
  152.             final AntennaKey other = (AntennaKey) object;
  153.             return this.getName().equals(other.getName()) &&
  154.                    this.getRadomeCode().equals(other.getRadomeCode()) &&
  155.                    this.getSerialNumber().equals(other.getSerialNumber());
  156.         }
  157.         return false;
  158.     }

  159.     @Override
  160.     public int hashCode() {
  161.         return getName().hashCode() ^ getRadomeCode().hashCode() ^ getSerialNumber().hashCode();
  162.     }

  163. }