CCIRLoader.java
- /* Copyright 2002-2025 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.models.earth.ionosphere.nequick;
- import org.hipparchus.util.FastMath;
- import org.orekit.data.DataSource;
- import org.orekit.errors.OrekitException;
- import org.orekit.errors.OrekitMessages;
- import org.orekit.time.DateComponents;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.Reader;
- import java.util.Locale;
- import java.util.regex.Pattern;
- /**
- * Parser for CCIR files.
- * <p>
- * Numerical grid maps which describe the regular variation of the ionosphere. They are used to derive other variables
- * such as critical frequencies and transmission factors.
- * </p> <p>
- * The coefficients correspond to low and high solar activity conditions.
- * </p> <p>
- * The CCIR file naming convention is ccirXX.asc where each XX means month + 10.
- * </p> <p>
- * Coefficients are store into tow arrays, F2 and Fm3. F2 coefficients are used for the computation of the F2 layer
- * critical frequency. Fm3 for the computation of the F2 layer maximum usable frequency factor. The size of these two
- * arrays is fixed and discussed into the section 2.5.3.2 of the reference document.
- * </p>
- * @author Bryan Cazabonne
- * @since 13.0
- */
- class CCIRLoader {
- /** Total number of F2 coefficients contained in the file. */
- private static final int NUMBER_F2_COEFFICIENTS = 1976;
- /** Pattern for delimiting regular expressions. */
- private static final Pattern SEPARATOR = Pattern.compile("\\s+");
- /** Rows number for F2 and Fm3 arrays. */
- private static final int ROWS = 2;
- /** Columns number for F2 array. */
- private static final int TOTAL_COLUMNS_F2 = 76;
- /** Columns number for Fm3 array. */
- private static final int TOTAL_COLUMNS_FM3 = 49;
- /** Depth of F2 array. */
- private static final int DEPTH_F2 = 13;
- /** Depth of Fm3 array. */
- private static final int DEPTH_FM3 = 9;
- /** F2 coefficients used for the computation of the F2 layer critical frequency. */
- private double[][][] parsedF2;
- /** Fm3 coefficients used for the computation of the F2 layer maximum usable frequency factor. */
- private double[][][] parsedFm3;
- /**
- * Build a new instance.
- */
- CCIRLoader() {
- this.parsedF2 = new double[ROWS][TOTAL_COLUMNS_F2][DEPTH_F2];
- this.parsedFm3 = new double[ROWS][TOTAL_COLUMNS_FM3][DEPTH_FM3];
- }
- /**
- * Get the F2 coefficients used for the computation of the F2 layer critical frequency.
- *
- * @return the F2 coefficients
- */
- public double[][][] getF2() {
- return parsedF2.clone();
- }
- /**
- * Get the Fm3 coefficients used for the computation of the F2 layer maximum usable frequency factor.
- *
- * @return the F2 coefficients
- */
- public double[][][] getFm3() {
- return parsedFm3.clone();
- }
- /**
- * Load the data for a given month.
- *
- * @param dateComponents month given but its DateComponents
- */
- public void loadCCIRCoefficients(final DateComponents dateComponents) {
- // The files are named ccirXX.asc where XX substitute the month of the year + 10
- final int currentMonth = dateComponents.getMonth();
- final String fileName = String.format(Locale.US, "/assets/org/orekit/nequick/ccir%02d.asc",
- currentMonth + 10);
- loadData(new DataSource(fileName, () -> CCIRLoader.class.getResourceAsStream(fileName)));
- }
- /** Load data.
- * @param dataSource data source
- */
- public void loadData(final DataSource dataSource) {
- // Placeholders for parsed data
- int lineNumber = 0;
- int index = 0;
- int currentRowF2 = 0;
- int currentColumnF2 = 0;
- int currentDepthF2 = 0;
- int currentRowFm3 = 0;
- int currentColumnFm3 = 0;
- int currentDepthFm3 = 0;
- String line = null;
- try (Reader r = dataSource.getOpener().openReaderOnce();
- BufferedReader br = new BufferedReader(r)) {
- for (line = br.readLine(); line != null; line = br.readLine()) {
- ++lineNumber;
- line = line.trim();
- // Read grid data
- if (!line.isEmpty()) {
- final String[] ccir_line = SEPARATOR.split(line);
- for (final String field : ccir_line) {
- if (index < NUMBER_F2_COEFFICIENTS) {
- // Parse F2 coefficients
- if (currentDepthF2 >= DEPTH_F2 && currentColumnF2 < (TOTAL_COLUMNS_F2 - 1)) {
- currentDepthF2 = 0;
- currentColumnF2++;
- } else if (currentDepthF2 >= DEPTH_F2 && currentColumnF2 >= (TOTAL_COLUMNS_F2 - 1)) {
- currentDepthF2 = 0;
- currentColumnF2 = 0;
- currentRowF2++;
- }
- parsedF2[currentRowF2][currentColumnF2][currentDepthF2++] = Double.parseDouble(field);
- index++;
- } else {
- // Parse Fm3 coefficients
- if (currentDepthFm3 >= DEPTH_FM3 && currentColumnFm3 < (TOTAL_COLUMNS_FM3 - 1)) {
- currentDepthFm3 = 0;
- currentColumnFm3++;
- } else if (currentDepthFm3 >= DEPTH_FM3 && currentColumnFm3 >= (TOTAL_COLUMNS_FM3 - 1)) {
- currentDepthFm3 = 0;
- currentColumnFm3 = 0;
- currentRowFm3++;
- }
- parsedFm3[currentRowFm3][currentColumnFm3][currentDepthFm3++] = Double.parseDouble(field);
- index++;
- }
- }
- }
- }
- } catch (IOException ioe) {
- throw new OrekitException(ioe, OrekitMessages.NEQUICK_F2_FM3_NOT_LOADED, dataSource.getName());
- } catch (NumberFormatException nfe) {
- throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
- lineNumber, dataSource.getName(), line);
- }
- checkDimensions(currentRowF2, currentColumnF2, currentDepthF2, parsedF2, dataSource.getName());
- checkDimensions(currentRowFm3, currentColumnFm3, currentDepthFm3, parsedFm3, dataSource.getName());
- }
- /** Check dimensions.
- * @param currentRow current row index
- * @param currentColumn current column index
- * @param currentDepth current depth index
- * @param array storage array
- * @param name data source name
- */
- private void checkDimensions(final int currentRow, final int currentColumn, final int currentDepth,
- final double[][][] array, final String name) {
- // just three equality tests
- // written in a way test coverage doesn't complain about missing cases…
- if (FastMath.max(FastMath.max(FastMath.abs(currentRow - (array.length - 1)),
- FastMath.abs(currentColumn - (array[0].length - 1))),
- FastMath.abs(currentDepth - array[0][0].length)) != 0) {
- throw new OrekitException(OrekitMessages.NEQUICK_F2_FM3_NOT_LOADED, name);
- }
- }
- }