JB2008SpaceEnvironmentData.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.atmosphere.data;

  18. import java.io.BufferedInputStream;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.text.ParseException;
  22. import java.util.List;
  23. import java.util.stream.Collectors;

  24. import org.hipparchus.exception.DummyLocalizable;
  25. import org.orekit.annotation.DefaultDataContext;
  26. import org.orekit.data.DataContext;
  27. import org.orekit.data.DataProvidersManager;
  28. import org.orekit.data.DataSource;
  29. import org.orekit.errors.OrekitException;
  30. import org.orekit.errors.OrekitMessages;
  31. import org.orekit.models.earth.atmosphere.JB2008InputParameters;
  32. import org.orekit.time.AbsoluteDate;
  33. import org.orekit.time.TimeScale;
  34. import org.orekit.utils.Constants;
  35. import org.orekit.utils.ImmutableTimeStampedCache;


  36. /**
  37.  * This class provides a container for the solar indices data required by the JB2008
  38.  * atmospheric model. This container only stores information provided in the SOLFSMY and DTCFILE text file
  39.  * provided by Space Environment Technologies. Therefore it doesn't provide  the geomagnetic storm
  40.  * indices available in the SOLRESAP file.
  41.  * The {@link org.orekit.data.DataLoader} implementations and the parsing are handled by
  42.  * the {@link SOLFSMYDataLoader} {@link DtcDataLoader} classes.
  43.  * <p>
  44.  * Data are available on Space Environment Technologies'
  45.  * <a href="http://sol.spacenvironment.net/jb2008">website</a>.
  46.  *
  47.  * The work done for this class is based on the CssiSpaceWeatherData class
  48.  * by ClĂ©ment Jonglez, the JB2008 interface by Pascal Parraud, and corrections for
  49.  * the CssiSpaceWeatherData implementation by Bryan Cazabonne and Evan Ward.
  50.  *
  51.  * @author Louis Aucouturier
  52.  * @since 11.2
  53.  */
  54. public class JB2008SpaceEnvironmentData implements JB2008InputParameters {

  55.     /** Default regular expression for supported names that works with test and published files for the SOLFSMY file. */
  56.     public static final String DEFAULT_SUPPORTED_NAMES_SOLFSMY = "(SOLFSMY)(.*)((\\.txt)|(\\.TXT))";

  57.     /** Default regular expression for supported names that works with test and published files for the DTCFILE file. */
  58.     public static final String DEFAULT_SUPPORTED_NAMES_DTC = "DTCFILE.TXT";

  59.     /** Serializable UID. */
  60.     private static final long serialVersionUID = 7735042547323407578L;

  61.     /** Size of the list. */
  62.     private static final int N_NEIGHBORS = 2;

  63.     /** Data set for SOLFSMY file. */
  64.     private final transient ImmutableTimeStampedCache<SOLFSMYDataLoader.LineParameters> dataSOL;

  65.     /** Data set for DTCFILE file. */
  66.     private final transient ImmutableTimeStampedCache<DtcDataLoader.LineParameters> dataDTC;

  67.     /** First available date. */
  68.     private final AbsoluteDate firstDate;

  69.     /** Last available date. */
  70.     private final AbsoluteDate lastDate;

  71.     /** Previous set of solar activity parameters. */
  72.     private SOLFSMYDataLoader.LineParameters previousParamSOL;

  73.     /** Current set of solar activity parameters. */
  74.     private SOLFSMYDataLoader.LineParameters nextParamSOL;

  75.     /** Previous set of solar activity parameters. */
  76.     private DtcDataLoader.LineParameters previousParamDTC;

  77.     /** Current set of solar activity parameters. */
  78.     private DtcDataLoader.LineParameters nextParamDTC;

  79.     /**
  80.      * Simple constructor. This constructor uses the default data context.
  81.      *
  82.      * @param supportedNamesSOL regular expression for SOLFSMY space weather files names
  83.      * with variations allowed between SOLFSMY and the file extension.
  84.      * @param supportedNamesDTC regular expression for DTCFILE files names
  85.      * with variations allowed between DTCFILE and the file extension.
  86.      */
  87.     @DefaultDataContext
  88.     public JB2008SpaceEnvironmentData(final String supportedNamesSOL, final String supportedNamesDTC) {
  89.         this(supportedNamesSOL, supportedNamesDTC, DataContext.getDefault().getDataProvidersManager(),
  90.                 DataContext.getDefault().getTimeScales().getUTC());
  91.     }

  92.     /**
  93.      * Constructor that allows specifying the source of the SOLFSMY space weather
  94.      * file.
  95.      * This constructor takes a supplementary argument, the supported names for DTCFILE,
  96.      * in order to setup the second loader.
  97.      *
  98.      * @param supportedNamesSOL regular expression for SOLFSMY space weather files names
  99.      * with variations allowed between SOLFSMY and the file extension.
  100.      * @param supportedNamesDTC regular expression for DTCFILE files names
  101.      * with variations allowed between DTCFILE and the file extension.
  102.      * @param dataProvidersManager provides access to auxiliary data files.
  103.      * @param utc                  UTC time scale.
  104.      */
  105.     public JB2008SpaceEnvironmentData(final String supportedNamesSOL,
  106.                                       final String supportedNamesDTC,
  107.                                       final DataProvidersManager dataProvidersManager,
  108.                                       final TimeScale utc) {

  109.         // Load SOLFSMY data
  110.         final SOLFSMYDataLoader loaderSOL = new SOLFSMYDataLoader(utc);
  111.         dataProvidersManager.feed(supportedNamesSOL, loaderSOL);
  112.         dataSOL = new ImmutableTimeStampedCache<>(N_NEIGHBORS, loaderSOL.getDataSet());

  113.         // Load DTC data
  114.         final DtcDataLoader loaderDTC = new DtcDataLoader(utc);
  115.         dataProvidersManager.feed(supportedNamesDTC, loaderDTC);
  116.         dataDTC = new ImmutableTimeStampedCache<>(N_NEIGHBORS, loaderDTC.getDataSet());

  117.         // Because the two files are generated by the same organism,
  118.         // the first and last epochs are identical between the two files
  119.         firstDate = loaderSOL.getMinDate();
  120.         lastDate  = loaderSOL.getMaxDate();

  121.     }

  122.     /**
  123.      * Simple constructor. This constructor uses the {@link DataContext#getDefault()
  124.      * default data context}.
  125.      * @param sourceSolfsmy source for the SOLFSMY data
  126.      * @param sourceDtc     source for the DTC data
  127.      * @since 12.0
  128.      */
  129.     @DefaultDataContext
  130.     public JB2008SpaceEnvironmentData(final DataSource sourceSolfsmy,
  131.                                       final DataSource sourceDtc) {
  132.         this(sourceSolfsmy, sourceDtc, DataContext.getDefault().getTimeScales().getUTC());
  133.     }

  134.     /**
  135.      * Constructor that allows specifying the source of the SOLFSMY space weather
  136.      * file.
  137.      * This constructor takes a supplementary argument, the source for DTCFILE,
  138.      * in order to setup the second loader.
  139.      *
  140.      * @param sourceSolfsmy source for the SOLFSMY data
  141.      * @param sourceDtc     source for the DTC data
  142.      * @param utc           UTC time scale
  143.      * @since 12.0
  144.      */
  145.     public JB2008SpaceEnvironmentData(final DataSource sourceSolfsmy,
  146.                                       final DataSource sourceDtc,
  147.                                       final TimeScale utc) {
  148.         try {

  149.             // Load SOLFSMY data
  150.             final SOLFSMYDataLoader loaderSOL = new SOLFSMYDataLoader(utc);
  151.             try (InputStream is = sourceSolfsmy.getOpener().openStreamOnce();
  152.                  BufferedInputStream bis = new BufferedInputStream(is)) {
  153.                 loaderSOL.loadData(bis, sourceSolfsmy.getName());
  154.             }

  155.             // Load DTC data
  156.             final DtcDataLoader loaderDTC = new DtcDataLoader(utc);
  157.             try (InputStream is = sourceDtc.getOpener().openStreamOnce();
  158.                  BufferedInputStream bis = new BufferedInputStream(is)) {
  159.                 loaderDTC.loadData(bis, sourceDtc.getName());
  160.             }

  161.             // Initialise fields
  162.             dataSOL = new ImmutableTimeStampedCache<>(N_NEIGHBORS, loaderSOL.getDataSet());
  163.             dataDTC = new ImmutableTimeStampedCache<>(N_NEIGHBORS, loaderDTC.getDataSet());
  164.             // Because the two files are generated by the same organism,
  165.             // the first and last epochs are identical between the two files
  166.             firstDate = loaderSOL.getMinDate();
  167.             lastDate  = loaderSOL.getMaxDate();

  168.         } catch (IOException | ParseException ioe) {
  169.             throw new OrekitException(ioe, new DummyLocalizable(ioe.getMessage()));
  170.         }

  171.     }

  172.     /** {@inheritDoc} */
  173.     public AbsoluteDate getMinDate() {
  174.         return firstDate;
  175.     }

  176.     /** {@inheritDoc} */
  177.     public AbsoluteDate getMaxDate() {
  178.         return lastDate;
  179.     }

  180.     /**
  181.      * Find the data bracketing a specified date in dataSOL.
  182.      *
  183.      * @param date date to bracket
  184.      */
  185.     private void bracketDateSOL(final AbsoluteDate date) {
  186.         /**
  187.          * The presence of the shift in dates for checks on the validity of dates
  188.          * is here to enforce the lag on the parameters (5-day lag max for Y10 parameters).
  189.          * This lag is already present in the date parameter.
  190.          */
  191.         final AbsoluteDate firstDateUsefulSOL = firstDate.shiftedBy(-5 * Constants.JULIAN_DAY);
  192.         if (date.durationFrom(firstDateUsefulSOL) < 0) {
  193.             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE,
  194.                     date, firstDateUsefulSOL, lastDate, firstDateUsefulSOL.durationFrom(date));
  195.         }
  196.         if (date.durationFrom(lastDate) > 0) {
  197.             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER,
  198.                     date, firstDateUsefulSOL, lastDate, date.durationFrom(lastDate));
  199.         }

  200.         // don't search if the cached selection is fine
  201.         if (previousParamSOL != null && date.durationFrom(previousParamSOL.getDate()) > 0 &&
  202.                 date.durationFrom(nextParamSOL.getDate()) <= 0) {
  203.             return;
  204.         }

  205.         final List<SOLFSMYDataLoader.LineParameters> neigbors = dataSOL.getNeighbors(date).collect(Collectors.toList());
  206.         previousParamSOL = neigbors.get(0);
  207.         nextParamSOL = neigbors.get(1);

  208.     }

  209.     /**
  210.      * Find the data bracketing a specified date in dataDTC.
  211.      *
  212.      * @param date date to bracket
  213.      */
  214.     private void bracketDateDTC(final AbsoluteDate date) {
  215.         // No data lag
  216.         final AbsoluteDate firstDateUsefulDTC = firstDate;
  217.         if (date.durationFrom(firstDateUsefulDTC) < 0) {
  218.             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE,
  219.                     date, firstDateUsefulDTC, lastDate, firstDateUsefulDTC.durationFrom(date));
  220.         }
  221.         if (date.durationFrom(lastDate) > 0) {
  222.             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER,
  223.                     date, firstDateUsefulDTC, lastDate, date.durationFrom(lastDate));
  224.         }

  225.         // don't search if the cached selection is fine
  226.         if (previousParamDTC != null && date.durationFrom(previousParamDTC.getDate()) > 0 &&
  227.                 date.durationFrom(nextParamDTC.getDate()) <= 0) {
  228.             return;
  229.         }

  230.         final List<DtcDataLoader.LineParameters> neigbors = dataDTC.getNeighbors(date).collect(Collectors.toList());
  231.         previousParamDTC = neigbors.get(0);
  232.         nextParamDTC = neigbors.get(1);

  233.     }

  234.     /**
  235.      * Performs a linear interpolation between two values The weights are computed
  236.      * from the time delta between previous date, current date, next date.
  237.      *
  238.      * @param date          the current date
  239.      * @param previousValue the value at previous date
  240.      * @param nextValue     the value at next date
  241.      * @return the value interpolated for the current date
  242.      */
  243.     private double getLinearInterpolationSOL(final AbsoluteDate date, final double previousValue, final double nextValue) {
  244.         // perform a linear interpolation
  245.         return linearInterpolation(date, previousValue, previousParamSOL.getDate(), nextValue, nextParamSOL.getDate());
  246.     }

  247.     /**
  248.      * Performs a linear interpolation between two values The weights are computed
  249.      * from the time delta between previous date, current date, next date.
  250.      *
  251.      * @param date          the current date
  252.      * @param previousValue the value at previous date
  253.      * @param nextValue     the value at next date
  254.      * @return the value interpolated for the current date
  255.      */
  256.     private double getLinearInterpolationDTC(final AbsoluteDate date, final double previousValue, final double nextValue) {
  257.         // perform a linear interpolation
  258.         return linearInterpolation(date, previousValue, previousParamDTC.getDate(), nextValue, nextParamDTC.getDate());
  259.     }

  260.     /**
  261.      * Linear interpolation.
  262.      * @param date the current date
  263.      * @param previousValue the value at previous date
  264.      * @param previousDate the previous date
  265.      * @param nextValue the value at next date
  266.      * @param nextDate the next date
  267.      * @return the inteprolated value
  268.      */
  269.     private double linearInterpolation(final AbsoluteDate date,
  270.                                        final double previousValue, final AbsoluteDate previousDate,
  271.                                        final double nextValue, final AbsoluteDate nextDate) {
  272.         // perform a linear interpolation
  273.         final double dt = nextDate.durationFrom(previousDate);
  274.         final double previousWeight = nextDate.durationFrom(date) / dt;
  275.         final double nextWeight = date.durationFrom(previousDate) / dt;

  276.         // returns the data interpolated at the date
  277.         return previousValue * previousWeight + nextValue * nextWeight;
  278.     }

  279.     /** {@inheritDoc} */
  280.     public double getF10(final AbsoluteDate date) {
  281.         // The date is shifted by 1 day as described in the JB2008 Model with a 1-day lag.
  282.         final AbsoluteDate workDate = date.shiftedBy(-Constants.JULIAN_DAY);
  283.         bracketDateSOL(workDate);
  284.         return getLinearInterpolationSOL(workDate, previousParamSOL.getF10(), nextParamSOL.getF10());
  285.     }

  286.     /** {@inheritDoc} */
  287.     public double getF10B(final AbsoluteDate date) {
  288.         // The date is shifted by 1 day as described in the JB2008 Model with a 1-day lag.
  289.         final AbsoluteDate workDate = date.shiftedBy(-Constants.JULIAN_DAY);
  290.         bracketDateSOL(workDate);
  291.         return getLinearInterpolationSOL(workDate, previousParamSOL.getF10B(), nextParamSOL.getF10B());
  292.     }

  293.     /** {@inheritDoc} */
  294.     public double getS10(final AbsoluteDate date) {
  295.         // The date is shifted by 1 day as described in the JB2008 Model with a 1-day lag.
  296.         final AbsoluteDate workDate = date.shiftedBy(-Constants.JULIAN_DAY);
  297.         bracketDateSOL(workDate);
  298.         return getLinearInterpolationSOL(workDate, previousParamSOL.getS10(), nextParamSOL.getS10());
  299.     }

  300.     /** {@inheritDoc} */
  301.     public double getS10B(final AbsoluteDate date) {
  302.         // The date is shifted by 1 day as described in the JB2008 Model with a 1-day lag.
  303.         final AbsoluteDate workDate = date.shiftedBy(-Constants.JULIAN_DAY);
  304.         bracketDateSOL(workDate);
  305.         return getLinearInterpolationSOL(workDate, previousParamSOL.getS10B(), nextParamSOL.getS10B());
  306.     }

  307.     /** {@inheritDoc} */
  308.     public double getXM10(final AbsoluteDate date) {
  309.         // The date is shifted by 2 day as described in the JB2008 Model with a 2-day lag.
  310.         final AbsoluteDate workDate = date.shiftedBy(-2.0 * Constants.JULIAN_DAY);
  311.         bracketDateSOL(workDate);
  312.         return getLinearInterpolationSOL(workDate, previousParamSOL.getXM10(), nextParamSOL.getXM10());
  313.     }

  314.     /** {@inheritDoc} */
  315.     public double getXM10B(final AbsoluteDate date) {
  316.         // The date is shifted by 2 day as described in the JB2008 Model with a 2-day lag.
  317.         final AbsoluteDate workDate = date.shiftedBy(-2.0 * Constants.JULIAN_DAY);
  318.         bracketDateSOL(workDate);;
  319.         return getLinearInterpolationSOL(workDate, previousParamSOL.getXM10B(), nextParamSOL.getXM10B());
  320.     }

  321.     /** {@inheritDoc} */
  322.     public double getY10(final AbsoluteDate date) {
  323.         // The date is shifted by 5 day as described in the JB2008 Model with a 5-day lag.
  324.         final AbsoluteDate workDate = date.shiftedBy(-5.0 * Constants.JULIAN_DAY);
  325.         bracketDateSOL(workDate);
  326.         return getLinearInterpolationSOL(workDate, previousParamSOL.getY10(), nextParamSOL.getY10());
  327.     }

  328.     /** {@inheritDoc} */
  329.     public double getY10B(final AbsoluteDate date) {
  330.         // The date is shifted by 5 day as described in the JB2008 Model with a 5-day lag.
  331.         final AbsoluteDate workDate = date.shiftedBy(-5.0 * Constants.JULIAN_DAY);
  332.         bracketDateSOL(workDate);
  333.         return getLinearInterpolationSOL(workDate, previousParamSOL.getY10B(), nextParamSOL.getY10B());
  334.     }

  335.     /** {@inheritDoc} */
  336.     public double getDSTDTC(final AbsoluteDate date) {
  337.         bracketDateDTC(date);
  338.         return getLinearInterpolationDTC(date, previousParamDTC.getDSTDTC(), nextParamDTC.getDSTDTC());
  339.     }

  340. }