AbstractSolarActivityData.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 org.hipparchus.exception.DummyLocalizable;
  19. import org.orekit.data.DataProvidersManager;
  20. import org.orekit.data.DataSource;
  21. import org.orekit.errors.OrekitException;
  22. import org.orekit.errors.OrekitMessages;
  23. import org.orekit.models.earth.atmosphere.DTM2000InputParameters;
  24. import org.orekit.models.earth.atmosphere.NRLMSISE00InputParameters;
  25. import org.orekit.time.AbsoluteDate;
  26. import org.orekit.time.TimeScale;
  27. import org.orekit.time.TimeStamped;
  28. import org.orekit.utils.GenericTimeStampedCache;
  29. import org.orekit.utils.ImmutableTimeStampedCache;
  30. import org.orekit.utils.TimeStampedGenerator;

  31. import java.io.BufferedInputStream;
  32. import java.io.IOException;
  33. import java.io.InputStream;
  34. import java.text.ParseException;
  35. import java.util.ArrayList;
  36. import java.util.Collection;
  37. import java.util.List;
  38. import java.util.function.Function;
  39. import java.util.stream.Collectors;

  40. /**
  41.  * Abstract class for solar activity data.
  42.  *
  43.  * @param <L> type of the line parameters
  44.  * @param <D> type of the solar activity data loader
  45.  *
  46.  * @author Vincent Cucchietti
  47.  * @since 12.0
  48.  */
  49. public abstract class AbstractSolarActivityData<L extends AbstractSolarActivityDataLoader.LineParameters, D extends AbstractSolarActivityDataLoader<L>>
  50.         implements DTM2000InputParameters, NRLMSISE00InputParameters {

  51.     /** Size of the list. */
  52.     protected static final int N_NEIGHBORS = 2;

  53.     /** Serializable UID. */
  54.     private static final long serialVersionUID = 8804818166227680449L;

  55.     /** Weather data thread safe cache. */
  56.     private final transient GenericTimeStampedCache<L> cache;

  57.     /** Supported names. */
  58.     private final String supportedNames;

  59.     /** UTC time scale. */
  60.     private final TimeScale utc;

  61.     /** First available date. */
  62.     private final AbsoluteDate firstDate;

  63.     /** Last available date. */
  64.     private final AbsoluteDate lastDate;

  65.     /**
  66.      * @param supportedNames regular expression for supported AGI/CSSI space weather files names
  67.      * @param loader data loader
  68.      * @param dataProvidersManager provides access to auxiliary data files.
  69.      * @param utc UTC time scale
  70.      * @param maxSlots maximum number of independent cached time slots in the
  71.      * {@link GenericTimeStampedCache time-stamped cache}
  72.      * @param maxSpan maximum duration span in seconds of one slot in the {@link GenericTimeStampedCache time-stamped cache}
  73.      * @param maxInterval time interval above which a new slot is created in the
  74.      * {@link GenericTimeStampedCache time-stamped cache}
  75.      * @param minimumStep overriding minimum step designed for non-homogeneous tabulated values. To be used for example when
  76.      * caching monthly tabulated values. May be null.
  77.      */
  78.     protected AbstractSolarActivityData(final String supportedNames, final D loader,
  79.                                         final DataProvidersManager dataProvidersManager, final TimeScale utc,
  80.                                         final int maxSlots, final double maxSpan, final double maxInterval,
  81.                                         final double minimumStep) {
  82.         // Load data
  83.         dataProvidersManager.feed(supportedNames, loader);

  84.         // Create thread safe cache
  85.         this.cache = new GenericTimeStampedCache<>(N_NEIGHBORS, maxSlots, maxSpan, maxInterval,
  86.                                                    new SolarActivityGenerator(loader.getDataSet()), minimumStep);

  87.         // Initialise fields
  88.         this.supportedNames = supportedNames;
  89.         this.utc            = utc;
  90.         this.firstDate      = loader.getMinDate();
  91.         this.lastDate       = loader.getMaxDate();
  92.     }

  93.     /**
  94.      * Simple constructor.
  95.      *
  96.      * @param source source for the data
  97.      * @param loader data loader
  98.      * @param utc UTC time scale
  99.      * @param maxSlots maximum number of independent cached time slots in the
  100.      * {@link GenericTimeStampedCache time-stamped cache}
  101.      * @param maxSpan maximum duration span in seconds of one slot in the {@link GenericTimeStampedCache time-stamped cache}
  102.      * @param maxInterval time interval above which a new slot is created in the
  103.      * {@link GenericTimeStampedCache time-stamped cache}
  104.      * @param minimumStep overriding minimum step designed for non-homogeneous tabulated values. To be used for example when
  105.      * caching monthly tabulated values. May be null.
  106.      *
  107.      * @since 12.0
  108.      */
  109.     public AbstractSolarActivityData(final DataSource source, final D loader, final TimeScale utc, final int maxSlots,
  110.                                      final double maxSpan, final double maxInterval, final double minimumStep) {
  111.         try {
  112.             // Load file
  113.             try (InputStream is = source.getOpener().openStreamOnce();
  114.                     BufferedInputStream bis = new BufferedInputStream(is)) {
  115.                 loader.loadData(bis, source.getName());
  116.             }

  117.             // Create thread safe cache
  118.             this.cache = new GenericTimeStampedCache<>(N_NEIGHBORS, maxSlots, maxSpan, maxInterval,
  119.                                                        new SolarActivityGenerator(loader.getDataSet()), minimumStep);

  120.             // Initialise fields
  121.             this.supportedNames = source.getName();
  122.             this.utc            = utc;
  123.             this.firstDate      = loader.getMinDate();
  124.             this.lastDate       = loader.getMaxDate();
  125.         }
  126.         catch (IOException | ParseException ioe) {
  127.             throw new OrekitException(ioe, new DummyLocalizable(ioe.getMessage()));
  128.         }
  129.     }

  130.     /**
  131.      * Performs a linear interpolation between two values The weights are computed from the time delta between previous date,
  132.      * current date, next date.
  133.      *
  134.      * @param date current date
  135.      * @param solarActivityToDoubleMapper mapping function taking solar activity as input and returning a double
  136.      *
  137.      * @return the value interpolated for the current date
  138.      */
  139.     protected double getLinearInterpolation(final AbsoluteDate date, final Function<L, Double> solarActivityToDoubleMapper) {
  140.         // Create solar activity around current date
  141.         final LocalSolarActivity localSolarActivity = new LocalSolarActivity(date);

  142.         // Extract values
  143.         return getLinearInterpolation(localSolarActivity, solarActivityToDoubleMapper);
  144.     }

  145.     /**
  146.      * Performs a linear interpolation between two values The weights are computed from the time delta between previous date,
  147.      * current date, next date.
  148.      *
  149.      * @param localSolarActivity solar activity around current date
  150.      * @param solarActivityToDoubleMapper mapping function taking solar activity as input and returning a double
  151.      *
  152.      * @return the value interpolated for the current date
  153.      */
  154.     protected double getLinearInterpolation(final LocalSolarActivity localSolarActivity,
  155.                                             final Function<L, Double> solarActivityToDoubleMapper) {
  156.         // Extract values
  157.         final L      previousParameters = localSolarActivity.getPreviousParam();
  158.         final double previousValue      = solarActivityToDoubleMapper.apply(previousParameters);

  159.         final L      nextParameters = localSolarActivity.getNextParam();
  160.         final double nextValue      = solarActivityToDoubleMapper.apply(nextParameters);

  161.         // Perform a linear interpolation
  162.         final AbsoluteDate previousDate   = localSolarActivity.getPreviousParam().getDate();
  163.         final AbsoluteDate currentDate    = localSolarActivity.getNextParam().getDate();
  164.         final double       dt             = currentDate.durationFrom(previousDate);
  165.         final AbsoluteDate date           = localSolarActivity.getDate();
  166.         final double       previousWeight = currentDate.durationFrom(date) / dt;
  167.         final double       nextWeight     = date.durationFrom(previousDate) / dt;

  168.         // Returns the data interpolated at the date
  169.         return previousValue * previousWeight + nextValue * nextWeight;
  170.     }

  171.     /**
  172.      * Get underlying cache.
  173.      *
  174.      * @return cache
  175.      */
  176.     public GenericTimeStampedCache<L> getCache() {
  177.         return cache;
  178.     }

  179.     /**
  180.      * Get the supported names regular expression.
  181.      *
  182.      * @return the supported names.
  183.      */
  184.     public String getSupportedNames() {
  185.         return supportedNames;
  186.     }

  187.     /**
  188.      * Get the UTC timescale.
  189.      *
  190.      * @return UTC timescale
  191.      */
  192.     public TimeScale getUTC() {
  193.         return utc;
  194.     }

  195.     /** {@inheritDoc} */
  196.     @Override
  197.     public AbsoluteDate getMinDate() {
  198.         return firstDate;
  199.     }

  200.     /** {@inheritDoc} */
  201.     @Override
  202.     public AbsoluteDate getMaxDate() {
  203.         return lastDate;
  204.     }

  205.     /** Container for weather parameters around current date. Allows for thread safe use. */
  206.     protected class LocalSolarActivity implements TimeStamped {

  207.         /** Date. */
  208.         private final AbsoluteDate currentDate;

  209.         /** Previous parameters. */
  210.         private final L previousParam;

  211.         /** Next parameters. */
  212.         private final L nextParam;

  213.         /**
  214.          * Constructor.
  215.          *
  216.          * @param date current date
  217.          */
  218.         public LocalSolarActivity(final AbsoluteDate date) {
  219.             // Asked date is before earliest available data
  220.             if (date.durationFrom(firstDate) < 0) {
  221.                 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE, date, firstDate, lastDate,
  222.                                           firstDate.durationFrom(date));
  223.             }
  224.             // Asked date is after latest available data
  225.             if (date.durationFrom(lastDate) > 0) {
  226.                 throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER, date, firstDate, lastDate,
  227.                                           date.durationFrom(lastDate));
  228.             }

  229.             final List<L> neighbours = cache.getNeighbors(date).collect(Collectors.toList());

  230.             this.currentDate   = date;
  231.             this.previousParam = neighbours.get(0);
  232.             this.nextParam     = neighbours.get(1);
  233.         }

  234.         /** @return current date */
  235.         public AbsoluteDate getDate() {
  236.             return currentDate;
  237.         }

  238.         /** @return previous parameters */
  239.         public L getPreviousParam() {
  240.             return previousParam;
  241.         }

  242.         /** @return next parameters */
  243.         public L getNextParam() {
  244.             return nextParam;
  245.         }
  246.     }

  247.     /** Generator used in the weather data cache. */
  248.     protected class SolarActivityGenerator implements TimeStampedGenerator<L> {

  249.         /**
  250.          * Default time step to shift the date.
  251.          * <p>
  252.          * It is used so that, in the case where the earliest date is exactly at noon, we do not get the following interval
  253.          * [previous day; current day] but rather the expected interval [current day; next day]
  254.          */
  255.         private static final double STEP = 1;

  256.         /** Data set. */
  257.         private final ImmutableTimeStampedCache<L> data;

  258.         /**
  259.          * Constructor.
  260.          *
  261.          * @param dataSet weather data
  262.          */
  263.         protected SolarActivityGenerator(final Collection<L> dataSet) {
  264.             this.data = new ImmutableTimeStampedCache<>(N_NEIGHBORS, dataSet);
  265.         }

  266.         /** {@inheritDoc} */
  267.         @Override
  268.         public List<L> generate(final AbsoluteDate existingDate, final AbsoluteDate date) {
  269.             // No prior data in the cache
  270.             if (existingDate == null) {
  271.                 return data.getNeighbors(date).collect(Collectors.toList());
  272.             }
  273.             // Prior data in the cache, fill with data between date and existing date
  274.             if (date.isBefore(existingDate)) {
  275.                 return generateDataFromEarliestToLatestDates(date, existingDate);
  276.             }
  277.             return generateDataFromEarliestToLatestDates(existingDate, date);
  278.         }

  279.         /**
  280.          * Generate a list of parameters between earliest and latest dates.
  281.          *
  282.          * @param earliest earliest date
  283.          * @param latest latest date
  284.          *
  285.          * @return list of parameters between earliest and latest dates
  286.          */
  287.         public List<L> generateDataFromEarliestToLatestDates(final AbsoluteDate earliest, final AbsoluteDate latest) {
  288.             /* Gives first two parameters bracketing the earliest date
  289.              * A date shifted by step is used so that, in the case where the earliest date is exactly noon, we do not get the
  290.              * following interval [previous day; current day] but rather the expected interval [current day; next day] */
  291.             List<L> neighbours = data.getNeighbors(earliest.shiftedBy(STEP)).collect(Collectors.toList());

  292.             // Get next parameter until it brackets the latest date
  293.             AbsoluteDate  latestNeighbourDate = neighbours.get(1).getDate();
  294.             final List<L> params              = new ArrayList<>(neighbours);
  295.             while (latestNeighbourDate.isBefore(latest)) {
  296.                 neighbours = data.getNeighbors(latestNeighbourDate.shiftedBy(STEP)).collect(Collectors.toList());
  297.                 params.add(neighbours.get(1));
  298.                 latestNeighbourDate = neighbours.get(1).getDate();
  299.             }
  300.             return params;
  301.         }
  302.     }
  303. }