1 /* Copyright 2002-2024 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
18 package org.orekit.models.earth.ionosphere;
19
20 import java.io.BufferedReader;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.nio.charset.StandardCharsets;
25 import java.text.ParseException;
26 import java.util.regex.Pattern;
27
28 import org.orekit.annotation.DefaultDataContext;
29 import org.orekit.data.AbstractSelfFeedingLoader;
30 import org.orekit.data.DataContext;
31 import org.orekit.data.DataLoader;
32 import org.orekit.data.DataProvidersManager;
33 import org.orekit.errors.OrekitException;
34 import org.orekit.errors.OrekitMessages;
35 import org.orekit.time.DateComponents;
36
37 /** Loads Klobuchar-Style ionospheric coefficients a given input stream.
38 * A stream contains the alphas and betas coefficient for a given day.
39 * <p>
40 * They are obtained from <a href="ftp://ftp.aiub.unibe.ch/CODE/">University of Bern Astronomical Institute ftp</a>.
41 * 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>.
42 * <p>
43 * The files are UNIX-style compressed (.Z) files.
44 * They have to be extracted to UTF-8 text files before being read by this loader.
45 * <p>
46 * After extraction, it is assumed they are named CGIMDDD0.YYN where DDD and YY substitute day of year and 2-digits year.
47 * <p>
48 * The format is always the same, with and example shown below. Only the last 2 lines contains the Klobuchar coefficients.
49 * <p>
50 * Example:
51 * </p>
52 * <pre>
53 * 2 NAVIGATION DATA GPS RINEX VERSION / TYPE
54 * INXFIT V5.3 AIUB 06-JAN-17 09:12 PGM / RUN BY / DATE
55 * CODE'S KLOBUCHAR-STYLE IONOSPHERE MODEL FOR DAY 001, 2017 COMMENT
56 * Contact address: code(at)aiub.unibe.ch COMMENT
57 * Data archive: ftp.unibe.ch/aiub/CODE/ COMMENT
58 * www.aiub.unibe.ch/download/CODE/ COMMENT
59 * WARNING: USE DATA AT SOUTHERN POLAR REGION WITH CARE COMMENT
60 * 1.2821D-08 -9.6222D-09 -3.5982D-07 -6.0901D-07 ION ALPHA
61 * 1.0840D+05 -1.3197D+05 -2.6331D+05 4.0570D+05 ION BETA
62 * END OF HEADER
63 * </pre>
64 *
65 * <p>It is not safe for multiple threads to share a single instance of this class.
66 *
67 * @author Maxime Journot
68 */
69 public class KlobucharIonoCoefficientsLoader extends AbstractSelfFeedingLoader
70 implements DataLoader {
71
72 /** Default supported files name pattern. */
73 public static final String DEFAULT_SUPPORTED_NAMES = "CGIM*0\\.*N$";
74
75 /** Pattern for delimiting regular expressions. */
76 private static final Pattern SEPARATOR = Pattern.compile("\\s+");
77
78 /** The alpha coefficients loaded. */
79 private double[] alpha;
80
81 /** The beta coefficients loaded. */
82 private double[] beta;
83
84 /**
85 * Constructor with supported names given by user. This constructor uses the {@link
86 * DataContext#getDefault() default data context}.
87 *
88 * @param supportedNames regular expression that matches the names of the RINEX files
89 * with Klobuchar coefficients.
90 * @see #KlobucharIonoCoefficientsLoader(String, DataProvidersManager)
91 */
92 @DefaultDataContext
93 public KlobucharIonoCoefficientsLoader(final String supportedNames) {
94 this(supportedNames, DataContext.getDefault().getDataProvidersManager());
95 }
96
97 /**
98 * Constructor that uses user defined supported names and data context.
99 *
100 * @param supportedNames regular expression that matches the names of the RINEX
101 * files with Klobuchar coefficients.
102 * @param dataProvidersManager provides access to auxiliary data files.
103 */
104 public KlobucharIonoCoefficientsLoader(final String supportedNames,
105 final DataProvidersManager dataProvidersManager) {
106 super(supportedNames, dataProvidersManager);
107 this.alpha = null;
108 this.beta = null;
109 }
110
111 /**
112 * Constructor with default supported names. This constructor uses the {@link
113 * DataContext#getDefault() default data context}.
114 *
115 * @see #KlobucharIonoCoefficientsLoader(String, DataProvidersManager)
116 * @see #KlobucharIonoCoefficientsLoader(String)
117 */
118 @DefaultDataContext
119 public KlobucharIonoCoefficientsLoader() {
120 this(DEFAULT_SUPPORTED_NAMES);
121 }
122
123 /** Returns the alpha coefficients array.
124 * @return the alpha coefficients array
125 */
126 public double[] getAlpha() {
127 return alpha.clone();
128 }
129
130 /** Returns the beta coefficients array.
131 * @return the beta coefficients array
132 */
133 public double[] getBeta() {
134 return beta.clone();
135 }
136
137 @Override
138 public String getSupportedNames() {
139 return super.getSupportedNames();
140 }
141
142 /** Load the data using supported names .
143 */
144 public void loadKlobucharIonosphericCoefficients() {
145 feed(this);
146
147 // Throw an exception if alphas or betas were not loaded properly
148 if (alpha == null || beta == null) {
149 throw new OrekitException(OrekitMessages.KLOBUCHAR_ALPHA_BETA_NOT_LOADED,
150 getSupportedNames());
151 }
152 }
153
154 /** Load the data for a given day.
155 * @param dateComponents day given but its DateComponents
156 */
157 public void loadKlobucharIonosphericCoefficients(final DateComponents dateComponents) {
158
159 // The files are named CGIMDDD0.YYN where DDD and YY substitute day of year and 2-digits year
160 final int doy = dateComponents.getDayOfYear();
161 final String yearString = String.valueOf(dateComponents.getYear());
162
163 this.setSupportedNames(String.format("CGIM%03d0.%2sN", doy, yearString.substring(yearString.length() - 2)));
164
165 try {
166 this.loadKlobucharIonosphericCoefficients();
167 } catch (OrekitException oe) {
168 throw new OrekitException(oe,
169 OrekitMessages.KLOBUCHAR_ALPHA_BETA_NOT_AVAILABLE_FOR_DATE,
170 dateComponents.toString());
171 }
172 }
173
174 /** {@inheritDoc} */
175 public boolean stillAcceptsData() {
176 return true;
177 }
178
179 /** Load Klobuchar-Style ionospheric coefficients read from some file.
180 * @param input data input stream
181 * @param name name of the file (or zip entry)
182 * @exception IOException if data can't be read
183 * @exception ParseException if data can't be parsed
184 */
185 public void loadData(final InputStream input, final String name)
186 throws IOException, ParseException {
187
188 int lineNumber = 0;
189 String line = null;
190 // Open stream and parse data
191 try (BufferedReader br = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
192
193 for (line = br.readLine(); line != null; line = br.readLine()) {
194 ++lineNumber;
195 line = line.trim();
196
197 // Read alphas
198 if (line.length() > 0 && line.endsWith("ALPHA")) {
199 final String[] alpha_line = SEPARATOR.split(line);
200 alpha = new double[4];
201 for (int j = 0; j < 4; j++) {
202 alpha[j] = Double.parseDouble(alpha_line[j].replace("D", "E"));
203 }
204 }
205
206 // Read betas
207 if (line.length() > 0 && line.endsWith("BETA")) {
208 final String[] beta_line = SEPARATOR.split(line);
209 beta = new double[4];
210 for (int j = 0; j < 4; j++) {
211 beta[j] = Double.parseDouble(beta_line[j].replace("D", "E"));
212 }
213 }
214 }
215
216 } catch (NumberFormatException nfe) {
217 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
218 lineNumber, name, line);
219 }
220
221 // Check that alphas and betas were found
222 if (alpha == null || beta == null) {
223 throw new OrekitException(OrekitMessages.NO_KLOBUCHAR_ALPHA_BETA_IN_FILE, name);
224 }
225
226 }
227 }