CssiSpaceWeatherData.java
- /* Copyright 2020-2025 Clément Jonglez
- * 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.
- * Clément Jonglez 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.atmosphere.data;
- import org.orekit.annotation.DefaultDataContext;
- import org.orekit.data.DataContext;
- import org.orekit.data.DataProvidersManager;
- import org.orekit.data.DataSource;
- import org.orekit.models.earth.atmosphere.data.CssiSpaceWeatherDataLoader.LineParameters;
- import org.orekit.time.AbsoluteDate;
- import org.orekit.time.TimeScale;
- import org.orekit.utils.Constants;
- import org.orekit.utils.GenericTimeStampedCache;
- import org.orekit.utils.OrekitConfiguration;
- /**
- * This class provides three-hourly and daily solar activity data needed by atmospheric models: F107 solar flux, Ap and Kp
- * indexes. The {@link org.orekit.data.DataLoader} implementation and the parsing is handled by the class
- * {@link CssiSpaceWeatherDataLoader}.
- * <p>
- * The data are retrieved through space weather files offered by AGI/CSSI on the AGI
- * <a href="https://ftp.agi.com/pub/DynamicEarthData/SpaceWeather-All-v1.2.txt">FTP</a> as
- * well as on the CelesTrack <a href="http://celestrak.com/SpaceData/">website</a>. These files are updated several times a
- * day by using several sources mentioned in the
- * <a href="http://celestrak.com/SpaceData/SpaceWx-format.php">Celestrak space
- * weather data documentation</a>.
- * </p>
- *
- * @author Clément Jonglez
- * @author Vincent Cucchietti
- * @since 10.2
- */
- public class CssiSpaceWeatherData extends AbstractSolarActivityData<LineParameters, CssiSpaceWeatherDataLoader> {
- /** Default regular expression for supported names that works with all officially published files. */
- public static final String DEFAULT_SUPPORTED_NAMES = "^S(?:pace)?W(?:eather)?-(?:All)?.*\\.txt$";
- /** Serializable UID. */
- private static final long serialVersionUID = 4249411710645968978L;
- /** Date of last data before the prediction starts. */
- private final AbsoluteDate lastObservedDate;
- /** Date of last daily prediction before the monthly prediction starts. */
- private final AbsoluteDate lastDailyPredictedDate;
- /**
- * Simple constructor. This constructor uses the default data context.
- * <p>
- * The original file names provided by AGI/CSSI are of the form: SpaceWeather-All-v1.2.txt
- * (<a href="https://ftp.agi.com/pub/DynamicEarthData/SpaceWeather-All-v1.2.txt">AGI's ftp</a>). So a recommended regular
- * expression for the supported names that works with all published files is: {@link #DEFAULT_SUPPORTED_NAMES}.
- * <p>
- * It provides a default configuration for the thread safe cache :
- * <ul>
- * <li>Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}</li>
- * <li>Max span : {@code Constants.JULIAN_DAY}</li>
- * <li>Max span interval : {@code 0}</li>
- * </ul>
- *
- * @param supportedNames regular expression for supported AGI/CSSI space weather files names
- */
- @DefaultDataContext
- public CssiSpaceWeatherData(final String supportedNames) {
- this(supportedNames, DataContext.getDefault().getDataProvidersManager(),
- DataContext.getDefault().getTimeScales().getUTC());
- }
- /**
- * Constructor that allows specifying the source of the CSSI space weather file.
- * <p>
- * It provides a default configuration for the thread safe cache :
- * <ul>
- * <li>Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}</li>
- * <li>Max span : {@code Constants.JULIAN_DAY}</li>
- * <li>Max span interval : {@code 0}</li>
- * </ul>
- *
- * @param supportedNames regular expression for supported AGI/CSSI space weather files names
- * @param dataProvidersManager provides access to auxiliary data files.
- * @param utc UTC time scale.
- */
- public CssiSpaceWeatherData(final String supportedNames, final DataProvidersManager dataProvidersManager,
- final TimeScale utc) {
- this(supportedNames, new CssiSpaceWeatherDataLoader(utc), dataProvidersManager, utc);
- }
- /**
- * Constructor that allows specifying the source of the CSSI space weather file.
- * <p>
- * It provides a default configuration for the thread safe cache :
- * <ul>
- * <li>Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}</li>
- * <li>Max span : {@code Constants.JULIAN_DAY}</li>
- * <li>Max span interval : {@code 0}</li>
- * </ul>
- *
- * @param supportedNames regular expression for supported AGI/CSSI space weather files names
- * @param loader data loader
- * @param dataProvidersManager provides access to auxiliary data files.
- * @param utc UTC time scale
- */
- public CssiSpaceWeatherData(final String supportedNames, final CssiSpaceWeatherDataLoader loader,
- final DataProvidersManager dataProvidersManager, final TimeScale utc) {
- this(supportedNames, loader, dataProvidersManager, utc, OrekitConfiguration.getCacheSlotsNumber(),
- Constants.JULIAN_DAY, 0);
- }
- /**
- * Constructor that allows specifying the source of the CSSI space weather file and customizable thread safe cache
- * configuration.
- *
- * @param supportedNames regular expression for supported AGI/CSSI space weather files names
- * @param loader data loader
- * @param dataProvidersManager provides access to auxiliary data files.
- * @param utc UTC time scale
- * @param maxSlots maximum number of independent cached time slots in the
- * {@link GenericTimeStampedCache time-stamped cache}
- * @param maxSpan maximum duration span in seconds of one slot in the {@link GenericTimeStampedCache time-stamped cache}
- * @param maxInterval time interval above which a new slot is created in the
- * {@link GenericTimeStampedCache time-stamped cache}
- */
- public CssiSpaceWeatherData(final String supportedNames, final CssiSpaceWeatherDataLoader loader,
- final DataProvidersManager dataProvidersManager, final TimeScale utc, final int maxSlots,
- final double maxSpan, final double maxInterval) {
- super(supportedNames, loader, dataProvidersManager, utc, maxSlots, maxSpan, maxInterval, Constants.JULIAN_DAY);
- // Initialise fields
- this.lastObservedDate = loader.getLastObservedDate();
- this.lastDailyPredictedDate = loader.getLastDailyPredictedDate();
- }
- /**
- * Simple constructor which use the {@link DataContext#getDefault() default data context}.
- * <p>
- * It provides a default configuration for the thread safe cache :
- * <ul>
- * <li>Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}</li>
- * <li>Max span : {@code Constants.JULIAN_DAY}</li>
- * <li>Max span interval : {@code 0}</li>
- * </ul>
- *
- * @param source source for the data
- *
- * @since 12.0
- */
- @DefaultDataContext
- public CssiSpaceWeatherData(final DataSource source) {
- this(source, DataContext.getDefault().getTimeScales().getUTC());
- }
- /**
- * Simple constructor.
- * <p>
- * It provides a default configuration for the thread safe cache :
- * <ul>
- * <li>Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}</li>
- * <li>Max span : {@code Constants.JULIAN_DAY}</li>
- * <li>Max span interval : {@code 0}</li>
- * </ul>
- *
- * @param source source for the data
- * @param utc UTC time scale
- *
- * @since 12.0
- */
- public CssiSpaceWeatherData(final DataSource source, final TimeScale utc) {
- this(source, new CssiSpaceWeatherDataLoader(utc), utc);
- }
- /**
- * Simple constructor.
- * <p>
- * It provides a default configuration for the thread safe cache :
- * <ul>
- * <li>Number of slots : {@code OrekitConfiguration.getCacheSlotsNumber()}</li>
- * <li>Max span : {@code Constants.JULIAN_DAY}</li>
- * <li>Max span interval : {@code 0}</li>
- * </ul>
- *
- * @param source source for the data
- * @param loader data loader
- * @param utc UTC time scale
- *
- * @since 12.0
- */
- public CssiSpaceWeatherData(final DataSource source, final CssiSpaceWeatherDataLoader loader, final TimeScale utc) {
- this(source, loader, utc, OrekitConfiguration.getCacheSlotsNumber(), Constants.JULIAN_DAY, 0);
- }
- /**
- * Simple constructor with customizable thread safe cache configuration.
- *
- * @param source source for the data
- * @param loader data loader
- * @param utc UTC time scale
- * @param maxSlots maximum number of independent cached time slots in the
- * {@link GenericTimeStampedCache time-stamped cache}
- * @param maxSpan maximum duration span in seconds of one slot in the {@link GenericTimeStampedCache time-stamped cache}
- * @param maxInterval time interval above which a new slot is created in the
- * {@link GenericTimeStampedCache time-stamped cache}
- *
- * @since 12.0
- */
- public CssiSpaceWeatherData(final DataSource source, final CssiSpaceWeatherDataLoader loader, final TimeScale utc,
- final int maxSlots, final double maxSpan, final double maxInterval) {
- super(source, loader, utc, maxSlots, maxSpan, maxInterval, Constants.JULIAN_DAY);
- this.lastObservedDate = loader.getLastObservedDate();
- this.lastDailyPredictedDate = loader.getLastDailyPredictedDate();
- }
- /** {@inheritDoc} */
- public double getInstantFlux(final AbsoluteDate date) {
- return getLinearInterpolation(date, LineParameters::getF107Obs);
- }
- /** {@inheritDoc} */
- public double getMeanFlux(final AbsoluteDate date) {
- return getAverageFlux(date);
- }
- /** {@inheritDoc} */
- public double getThreeHourlyKP(final AbsoluteDate date) {
- if (date.compareTo(lastObservedDate) <= 0) {
- /* If observation data is available, it contains three-hourly data */
- final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date);
- final LineParameters previousParam = localSolarActivity.getPreviousParam();
- final double hourOfDay = date.offsetFrom(previousParam.getDate(), getUTC()) / 3600;
- int i_kp = (int) (hourOfDay / 3);
- if (i_kp >= 8) {
- /* hourOfDay can take the value 24.0 at midnight due to floating point precision
- * when bracketing the dates or during a leap second because the hour of day is
- * computed in UTC view */
- i_kp = 7;
- }
- return previousParam.getThreeHourlyKp(i_kp);
- } else {
- /* Only predictions are available, there are no three-hourly data */
- return get24HoursKp(date);
- }
- }
- /** {@inheritDoc} */
- public double get24HoursKp(final AbsoluteDate date) {
- // Get the neighboring solar activity
- final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date);
- if (date.compareTo(lastDailyPredictedDate) <= 0) {
- // Daily data is available, just taking the daily average
- return localSolarActivity.getPreviousParam().getKpSum() / 8;
- } else {
- // Only monthly data is available, better interpolate between two months
- return getLinearInterpolation(localSolarActivity, lineParam -> lineParam.getKpSum() / 8);
- }
- }
- /** {@inheritDoc} */
- public double getDailyFlux(final AbsoluteDate date) {
- // Getting the value for the previous day
- return getDailyFluxOnDay(date.shiftedBy(-Constants.JULIAN_DAY));
- }
- /** {@inheritDoc} */
- public double getAverageFlux(final AbsoluteDate date) {
- // Get the neighboring solar activity
- final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date);
- if (date.compareTo(lastDailyPredictedDate) <= 0) {
- return localSolarActivity.getPreviousParam().getCtr81Obs();
- } else {
- // Only monthly data is available, better interpolate between two months
- return getLinearInterpolation(localSolarActivity, LineParameters::getCtr81Obs);
- }
- }
- /** {@inheritDoc} */
- public double[] getAp(final AbsoluteDate date) {
- final double[] apArray = new double[7];
- apArray[0] = getDailyAp(date);
- apArray[1] = getThreeHourlyAp(date);
- apArray[2] = getThreeHourlyAp(date.shiftedBy(-3.0 * 3600.0));
- apArray[3] = getThreeHourlyAp(date.shiftedBy(-6.0 * 3600.0));
- apArray[4] = getThreeHourlyAp(date.shiftedBy(-9.0 * 3600.0));
- apArray[5] = get24HoursAverageAp(date.shiftedBy(-12.0 * 3600.0));
- apArray[6] = get24HoursAverageAp(date.shiftedBy(-36.0 * 3600.0));
- return apArray;
- }
- /**
- * Gets the daily flux on the current day.
- *
- * @param date the current date
- *
- * @return the daily F10.7 flux (observed)
- */
- private double getDailyFluxOnDay(final AbsoluteDate date) {
- // Get the neighboring solar activity
- final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date);
- if (date.compareTo(lastDailyPredictedDate) <= 0) {
- // Getting the value for the previous day
- return localSolarActivity.getPreviousParam().getF107Obs();
- } else {
- // Only monthly data is available, better interpolate between two months
- return getLinearInterpolation(localSolarActivity, LineParameters::getF107Obs);
- }
- }
- /**
- * Gets the value of the three-hourly Ap index for the given date.
- *
- * @param date the current date
- *
- * @return the current three-hourly Ap index
- */
- private double getThreeHourlyAp(final AbsoluteDate date) {
- if (date.compareTo(lastObservedDate.shiftedBy(Constants.JULIAN_DAY)) < 0) {
- // If observation data is available, it contains three-hourly data.
- // Get the neighboring solar activity
- final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date);
- final LineParameters previousParam = localSolarActivity.getPreviousParam();
- final double hourOfDay = date.offsetFrom(previousParam.getDate(), getUTC()) / 3600;
- int i_ap = (int) (hourOfDay / 3);
- if (i_ap >= 8) {
- /* hourOfDay can take the value 24.0 at midnight due to floating point precision
- * when bracketing the dates or during a leap second because the hour of day is
- * computed in UTC view */
- i_ap = 7;
- }
- return previousParam.getThreeHourlyAp(i_ap);
- } else {
- /* Only predictions are available, there are no three-hourly data */
- return getDailyAp(date);
- }
- }
- /**
- * Gets the running average of the 8 three-hourly Ap indices prior to current time If three-hourly data is available, the
- * result is different than getDailyAp.
- *
- * @param date the current date
- *
- * @return the 24 hours running average of the Ap index
- */
- private double get24HoursAverageAp(final AbsoluteDate date) {
- if (date.compareTo(lastDailyPredictedDate) <= 0) {
- // Computing running mean
- double apSum = 0.0;
- for (int i = 0; i < 8; i++) {
- apSum += getThreeHourlyAp(date.shiftedBy(-3.0 * 3600 * i));
- }
- return apSum / 8;
- } else {
- /* Only monthly predictions are available, no need to compute the average from
- * three hourly data */
- return getDailyAp(date);
- }
- }
- /**
- * Get the daily Ap index for the given date.
- *
- * @param date the current date
- *
- * @return the daily Ap index
- */
- private double getDailyAp(final AbsoluteDate date) {
- // Get the neighboring solar activity
- final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date);
- if (date.compareTo(lastDailyPredictedDate) <= 0) {
- // Daily data is available, just taking the daily average
- return localSolarActivity.getPreviousParam().getApAvg();
- } else {
- // Only monthly data is available, better interpolate between two months
- return getLinearInterpolation(localSolarActivity, LineParameters::getApAvg);
- }
- }
- }