KlobucharIonoCoefficientsLoader.java

  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.models.earth.ionosphere;

  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.nio.charset.StandardCharsets;
  23. import java.text.ParseException;
  24. import java.util.Locale;
  25. import java.util.regex.Pattern;

  26. import org.orekit.annotation.DefaultDataContext;
  27. import org.orekit.data.AbstractSelfFeedingLoader;
  28. import org.orekit.data.DataContext;
  29. import org.orekit.data.DataLoader;
  30. import org.orekit.data.DataProvidersManager;
  31. import org.orekit.errors.OrekitException;
  32. import org.orekit.errors.OrekitMessages;
  33. import org.orekit.time.DateComponents;

  34. /** Loads Klobuchar-Style ionospheric coefficients a given input stream.
  35.  * A stream contains the alphas and betas coefficient for a given day.
  36.  * <p>
  37.  * They are obtained from <a href="ftp://ftp.aiub.unibe.ch/CODE/">University of Bern Astronomical Institute ftp</a>.
  38.  * Find more on the files at the <a href="http://www.aiub.unibe.ch/research/code___analysis_center/klobuchar_style_ionospheric_coefficients/index_eng.html">Astronomical Institute site</a>.
  39.  * <p>
  40.  * The files are UNIX-style compressed (.Z) files.
  41.  * They have to be extracted to UTF-8 text files before being read by this loader.
  42.  * <p>
  43.  * After extraction, it is assumed they are named CGIMDDD0.YYN where DDD and YY substitute day of year and 2-digits year.
  44.  * <p>
  45.  * The format is always the same, with and example shown below. Only the last 2 lines contains the Klobuchar coefficients.
  46.  * <p>
  47.  * Example:
  48.  * </p>
  49.  * <pre>
  50.  *      2              NAVIGATION DATA     GPS                 RINEX VERSION / TYPE
  51.  * INXFIT V5.3         AIUB                06-JAN-17 09:12     PGM / RUN BY / DATE
  52.  * CODE'S KLOBUCHAR-STYLE IONOSPHERE MODEL FOR DAY 001, 2017   COMMENT
  53.  * Contact address: code(at)aiub.unibe.ch                      COMMENT
  54.  * Data archive:    ftp.unibe.ch/aiub/CODE/                    COMMENT
  55.  *                  www.aiub.unibe.ch/download/CODE/           COMMENT
  56.  * WARNING: USE DATA AT SOUTHERN POLAR REGION WITH CARE        COMMENT
  57.  *     1.2821D-08 -9.6222D-09 -3.5982D-07 -6.0901D-07          ION ALPHA
  58.  *     1.0840D+05 -1.3197D+05 -2.6331D+05  4.0570D+05          ION BETA
  59.  *                                                             END OF HEADER
  60.  * </pre>
  61.  *
  62.  * <p>It is not safe for multiple threads to share a single instance of this class.
  63.  *
  64.  * @author Maxime Journot
  65.  */
  66. public class KlobucharIonoCoefficientsLoader extends AbstractSelfFeedingLoader
  67.         implements DataLoader {

  68.     /** Default supported files name pattern. */
  69.     public static final String DEFAULT_SUPPORTED_NAMES = "CGIM*0\\.*N$";

  70.     /** Pattern for delimiting regular expressions. */
  71.     private static final Pattern SEPARATOR = Pattern.compile("\\s+");

  72.     /** The alpha coefficients loaded. */
  73.     private double[] alpha;

  74.     /** The beta coefficients loaded. */
  75.     private double[] beta;

  76.     /**
  77.      * Constructor with supported names given by user. This constructor uses the {@link
  78.      * DataContext#getDefault() default data context}.
  79.      *
  80.      * @param supportedNames regular expression that matches the names of the RINEX files
  81.      *                       with Klobuchar coefficients.
  82.      * @see #KlobucharIonoCoefficientsLoader(String, DataProvidersManager)
  83.      */
  84.     @DefaultDataContext
  85.     public KlobucharIonoCoefficientsLoader(final String supportedNames) {
  86.         this(supportedNames, DataContext.getDefault().getDataProvidersManager());
  87.     }

  88.     /**
  89.      * Constructor that uses user defined supported names and data context.
  90.      *
  91.      * @param supportedNames       regular expression that matches the names of the RINEX
  92.      *                             files with Klobuchar coefficients.
  93.      * @param dataProvidersManager provides access to auxiliary data files.
  94.      */
  95.     public KlobucharIonoCoefficientsLoader(final String supportedNames,
  96.                                            final DataProvidersManager dataProvidersManager) {
  97.         super(supportedNames, dataProvidersManager);
  98.         this.alpha = null;
  99.         this.beta = null;
  100.     }

  101.     /**
  102.      * Constructor with default supported names. This constructor uses the {@link
  103.      * DataContext#getDefault() default data context}.
  104.      *
  105.      * @see #KlobucharIonoCoefficientsLoader(String, DataProvidersManager)
  106.      * @see #KlobucharIonoCoefficientsLoader(String)
  107.      */
  108.     @DefaultDataContext
  109.     public KlobucharIonoCoefficientsLoader() {
  110.         this(DEFAULT_SUPPORTED_NAMES);
  111.     }

  112.     /** Returns the alpha coefficients array.
  113.      * @return the alpha coefficients array
  114.      */
  115.     public double[] getAlpha() {
  116.         return alpha.clone();
  117.     }

  118.     /** Returns the beta coefficients array.
  119.      * @return the beta coefficients array
  120.      */
  121.     public double[] getBeta() {
  122.         return beta.clone();
  123.     }

  124.     @Override
  125.     public String getSupportedNames() {
  126.         return super.getSupportedNames();
  127.     }

  128.     /** Load the data using supported names .
  129.      */
  130.     public void loadKlobucharIonosphericCoefficients() {
  131.         feed(this);

  132.         // Throw an exception if alphas or betas were not loaded properly
  133.         if (alpha == null || beta == null) {
  134.             throw new OrekitException(OrekitMessages.KLOBUCHAR_ALPHA_BETA_NOT_LOADED,
  135.                     getSupportedNames());
  136.         }
  137.     }

  138.     /** Load the data for a given day.
  139.      * @param dateComponents day given but its DateComponents
  140.      */
  141.     public void loadKlobucharIonosphericCoefficients(final DateComponents dateComponents) {

  142.         // The files are named CGIMDDD0.YYN where DDD and YY substitute day of year and 2-digits year
  143.         final int    doy        = dateComponents.getDayOfYear();
  144.         final String yearString = String.valueOf(dateComponents.getYear());

  145.         this.setSupportedNames(String.format(Locale.US, "CGIM%03d0.%2sN",
  146.                                              doy, yearString.substring(yearString.length() - 2)));

  147.         try {
  148.             this.loadKlobucharIonosphericCoefficients();
  149.         } catch (OrekitException oe) {
  150.             throw new OrekitException(oe,
  151.                                       OrekitMessages.KLOBUCHAR_ALPHA_BETA_NOT_AVAILABLE_FOR_DATE,
  152.                                       dateComponents.toString());
  153.         }
  154.     }

  155.     /** {@inheritDoc} */
  156.     public boolean stillAcceptsData() {
  157.         return true;
  158.     }

  159.     /** Load Klobuchar-Style ionospheric coefficients read from some file.
  160.      * @param input data input stream
  161.      * @param name name of the file (or zip entry)
  162.      * @exception IOException if data can't be read
  163.      * @exception ParseException if data can't be parsed
  164.      */
  165.     public void loadData(final InputStream input, final String name)
  166.         throws IOException, ParseException {

  167.         int lineNumber = 0;
  168.         String line = null;
  169.         // Open stream and parse data
  170.         try (BufferedReader br = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {

  171.             for (line = br.readLine(); line != null; line = br.readLine()) {
  172.                 ++lineNumber;
  173.                 line = line.trim();

  174.                 // Read alphas
  175.                 if (line.length() > 0 && line.endsWith("ALPHA")) {
  176.                     final String[] alpha_line = SEPARATOR.split(line);
  177.                     alpha = new double[4];
  178.                     for (int j = 0; j < 4; j++) {
  179.                         alpha[j] = Double.parseDouble(alpha_line[j].replace("D", "E"));
  180.                     }
  181.                 }

  182.                 // Read betas
  183.                 if (line.length() > 0 && line.endsWith("BETA")) {
  184.                     final String[] beta_line = SEPARATOR.split(line);
  185.                     beta = new double[4];
  186.                     for (int j = 0; j < 4; j++) {
  187.                         beta[j] = Double.parseDouble(beta_line[j].replace("D", "E"));
  188.                     }
  189.                 }
  190.             }

  191.         } catch (NumberFormatException nfe) {
  192.             throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  193.                                       lineNumber, name, line);
  194.         }

  195.         // Check that alphas and betas were found
  196.         if (alpha == null || beta == null) {
  197.             throw new OrekitException(OrekitMessages.NO_KLOBUCHAR_ALPHA_BETA_IN_FILE, name);
  198.         }

  199.     }
  200. }