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
18 package org.orekit.models.earth.atmosphere.data;
19
20 import java.io.BufferedReader;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.Serializable;
25 import java.nio.charset.StandardCharsets;
26 import java.text.ParseException;
27 import java.util.NoSuchElementException;
28 import java.util.SortedSet;
29 import java.util.TreeSet;
30 import java.util.regex.Pattern;
31 import java.util.HashSet;
32 import java.util.Set;
33
34 import org.orekit.data.DataLoader;
35 import org.orekit.errors.OrekitException;
36 import org.orekit.errors.OrekitMessages;
37 import org.orekit.time.AbsoluteDate;
38 import org.orekit.time.ChronologicalComparator;
39 import org.orekit.time.TimeScale;
40 import org.orekit.time.TimeStamped;
41 import org.orekit.utils.Constants;
42
43
44 /**
45 * This class reads solar activity data from DTCFILE files for the class
46 * {@link JB2008SpaceEnvironmentData}. The code in this class is based of the
47 * CssiSpaceWeatherDataLoader class.
48 * The DTCFILE file contain pre-computed data from Space Environment using the Dst indices
49 * as well as Ap indices. This computation can be realised using the Fortran code provided
50 * by Space Environment Technologies. See <a href="https://sol.spacenvironment.net/JB2008/indices/DTCFILE.TXT">
51 * this link</a> for more information.
52 * <p>
53 * The data is provided by Space Environment Technologies through their website
54 * <a href="https://sol.spacenvironment.net/JB2008/indices/DTCFILE.TXT">Link</a>.
55 * </p>
56 * The work done for this class is based on the CssiSpaceWeatherDataLoader class
57 * by Clément Jonglez, the JB2008 interface by Pascal Parraud, and corrections for
58 * DataLoader implementation by Bryan Cazabonne and Evan Ward .
59 *
60 * @author Louis Aucouturier
61 * @since 11.2
62 */
63 public class DtcDataLoader implements DataLoader {
64
65 /** Container class for Solar activity indexes. */
66 public static class LineParameters implements TimeStamped, Serializable {
67
68 /** Serializable UID. */
69 private static final long serialVersionUID = 8239275953453087629L;
70
71 /** Entry date. */
72 private final AbsoluteDate date;
73
74 /** dTc temperature correction data. */
75 private final double dtc;
76
77 /**
78 * Constructor.
79 * @param date entry date
80 * @param dtc Temperature correction for geomagnetic storms
81 */
82 public LineParameters(final AbsoluteDate date, final double dtc) {
83 this.date = date;
84 this.dtc = dtc;
85 }
86
87 @Override
88 public AbsoluteDate getDate() {
89 return date;
90 }
91
92 /**
93 * Get the DSTDTC parameter.
94 * <p>
95 * It represents the temperature correction for geomagnetic storms.
96 * </p>
97 * @return dTc Temperature correction for geomagnetic storms
98 */
99 public double getDSTDTC() {
100 return dtc;
101 }
102
103 }
104
105 /** Pattern for regular data. */
106 private static final Pattern PATTERN_SPACE = Pattern.compile("\\s+");
107
108 /** UTC time scale. */
109 private final TimeScale utc;
110
111 /** First available date. */
112 private AbsoluteDate firstDate;
113
114 /** Last available date. */
115 private AbsoluteDate lastDate;
116
117 /** Data set. */
118 private SortedSet<LineParameters> set;
119
120 /**
121 * Constructor.
122 * @param utc UTC time scale
123 */
124 public DtcDataLoader(final TimeScale utc) {
125 this.utc = utc;
126 firstDate = null;
127 lastDate = null;
128 set = new TreeSet<>(new ChronologicalComparator());
129 }
130
131 /**
132 * Getter for the data set.
133 * @return the data set
134 */
135 public SortedSet<LineParameters> getDataSet() {
136 return set;
137 }
138
139 /**
140 * Gets the available data range minimum date.
141 * @return the minimum date.
142 */
143 public AbsoluteDate getMinDate() {
144 return firstDate;
145 }
146
147 /**
148 * Gets the available data range maximum date.
149 * @return the maximum date.
150 */
151 public AbsoluteDate getMaxDate() {
152 return lastDate;
153 }
154
155 /** {@inheritDoc} */
156 public void loadData(final InputStream input, final String name)
157 throws IOException, ParseException, OrekitException {
158
159 int lineNumber = 0;
160 String line = null;
161 final int nHours = 24;
162 final Set<AbsoluteDate> parsedEpochs = new HashSet<>();
163
164 try (BufferedReader br = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
165
166 final CommonLineReader reader = new CommonLineReader(br);
167
168 for (line = reader.readLine(); line != null; line = reader.readLine()) {
169 lineNumber++;
170 if (!reader.isEmptyLine()) {
171 /** extract the data from the line
172 * The data is extracted from substrings as the spacing between
173 * columns is constant.
174 */
175
176 /**
177 * The date is expressed as a year and the day-number in this year.
178 * Then the dTc is expressed in each column at a different hour, with
179 * column 4 being the first hour of the day and column 28 the last hour
180 * of the day.
181 * Each column is converted to a single LineParameters object.
182 */
183
184 final String[] splitLine = PATTERN_SPACE.split(line);
185 final int year = Integer.parseInt(splitLine[1]);
186 final int dayYear = Integer.parseInt(splitLine[2]);
187 final AbsoluteDate initDate = new AbsoluteDate(year, 1, 1, utc);
188 final AbsoluteDate currDate = initDate.shiftedBy((dayYear - 1) * Constants.JULIAN_DAY);
189
190 for (int i = 0; i < nHours; i++) {
191
192 final AbsoluteDate date = currDate.shiftedBy(3600 * i);
193
194 if (parsedEpochs.add(date)) { // Checking if entry doesn't exist yet
195 final double dtc = Integer.parseInt(splitLine[3 + i]);
196 set.add(new LineParameters(date, dtc));
197 }
198 }
199 }
200 }
201 } catch (NumberFormatException nfe) {
202 throw new OrekitException(nfe, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, lineNumber, name, line);
203 }
204
205 try {
206 firstDate = set.first().getDate();
207 lastDate = set.last().getDate();
208 } catch (NoSuchElementException nse) {
209 throw new OrekitException(nse, OrekitMessages.NO_DATA_IN_FILE, name);
210 }
211 }
212
213 /** {@inheritDoc} */
214 public boolean stillAcceptsData() {
215 return true;
216 }
217 }