1   /* Copyright 2013-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.rugged.refraction;
18  
19  import org.hipparchus.analysis.interpolation.BilinearInterpolatingFunction;
20  import org.hipparchus.geometry.euclidean.threed.Vector3D;
21  import org.orekit.rugged.errors.RuggedException;
22  import org.orekit.rugged.errors.RuggedMessages;
23  import org.orekit.rugged.intersection.IntersectionAlgorithm;
24  import org.orekit.rugged.linesensor.LineSensor;
25  import org.orekit.rugged.linesensor.SensorPixel;
26  import org.orekit.rugged.utils.NormalizedGeodeticPoint;
27  
28  /**
29   * Base class for atmospheric refraction model.
30   * @author Sergio Esteves
31   * @author Guylaine Prat
32   * @since 2.0
33   */
34  public abstract class AtmosphericRefraction {
35  
36      /** Flag to tell if we must compute the correction.
37       * By default: computation is set up.
38       * @since 2.1
39       */
40      private boolean mustBeComputed;
41  
42      /** The current atmospheric parameters.
43       * @since 2.1
44       */
45      private AtmosphericComputationParameters atmosphericParams;
46  
47      /** Bilinear interpolating function for pixel (used by inverse location).
48       * @since 2.1
49      */
50      private BilinearInterpolatingFunction bifPixel;
51  
52      /** Bilinear interpolating function of line (used by inverse location).
53       * @since 2.1
54      */
55      private BilinearInterpolatingFunction bifLine;
56  
57      /**
58       * Default constructor.
59       */
60      protected AtmosphericRefraction() {
61          // Set up the atmospheric parameters ... with lazy evaluation of the grid (done only if necessary)
62          this.atmosphericParams = new AtmosphericComputationParameters();
63          this.mustBeComputed    = true;
64          this.bifPixel          = null;
65          this.bifLine           = null;
66      }
67  
68      /** Apply correction to the intersected point with an atmospheric refraction model.
69       * @param satPos satellite position, in <em>body frame</em>
70       * @param satLos satellite line of sight, in <em>body frame</em>
71       * @param rawIntersection intersection point before refraction correction
72       * @param algorithm intersection algorithm
73       * @return corrected point with the effect of atmospheric refraction
74       * {@link org.orekit.rugged.utils.ExtendedEllipsoid#pointAtAltitude(Vector3D, Vector3D, double)} or see
75       * {@link org.orekit.rugged.intersection.IntersectionAlgorithm#refineIntersection(org.orekit.rugged.utils.ExtendedEllipsoid, Vector3D, Vector3D, NormalizedGeodeticPoint)}
76       */
77      public abstract NormalizedGeodeticPoint applyCorrection(Vector3D satPos, Vector3D satLos, NormalizedGeodeticPoint rawIntersection,
78                                              IntersectionAlgorithm algorithm);
79  
80      /** Deactivate computation (needed for the inverse location computation).
81       * @since 2.1
82       */
83      public void deactivateComputation() {
84          this.mustBeComputed = false;
85      }
86  
87      /** Reactivate computation (needed for the inverse location computation).
88       * @since 2.1
89       */
90      public void reactivateComputation() {
91          this.mustBeComputed = true;
92      }
93  
94      /** Tell if the computation must be performed.
95       * @return true if computation must be performed; false otherwise
96       * @since 2.1
97       */
98      public boolean mustBeComputed() {
99          return mustBeComputed;
100     }
101 
102     /** Configuration of the interpolation grid. This grid is associated to the given sensor,
103      * with the given min and max lines.
104      * @param sensor line sensor
105      * @param minLine min line defined for the inverse location
106      * @param maxLine max line defined for the inverse location
107      * @since 2.1
108      */
109     public void configureCorrectionGrid(final LineSensor sensor, final int minLine, final int maxLine) {
110 
111         atmosphericParams.configureCorrectionGrid(sensor, minLine, maxLine);
112     }
113 
114    /** Check if the current atmospheric parameters are the same as the asked ones.
115     * @param sensorName the asked sensor name
116     * @param minLine the asked min line
117     * @param maxLine the asked max line
118     * @return true if same context; false otherwise
119     * @since 2.1
120     */
121     public Boolean isSameContext(final String sensorName, final int minLine, final int maxLine) {
122 
123         return Double.compare(atmosphericParams.getMinLineSensor(), minLine) == 0 &&
124                Double.compare(atmosphericParams.getMaxLineSensor(), maxLine) == 0 &&
125                atmosphericParams.getSensorName().compareTo(sensorName) == 0;
126     }
127 
128     /** Get the computation parameters.
129      * @return the AtmosphericComputationParameters
130      * @since 2.1
131      */
132     public AtmosphericComputationParameters getComputationParameters() {
133         return atmosphericParams;
134     }
135 
136     /** Set the grid steps in pixel and line (used to compute inverse location).
137      * Overwrite the default values, for time optimization for instance.
138      * @param pixelStep pixel step for the inverse location computation
139      * @param lineStep line step for the inverse location computation
140      * @since 2.1
141      */
142     public void setGridSteps(final int pixelStep, final int lineStep) {
143         atmosphericParams.setGridSteps(pixelStep, lineStep);
144     }
145 
146     /**
147      * Set the margin for computation of inverse location with atmospheric refraction correction.
148      * Overwrite the default value DEFAULT_INVLOC_MARGIN.
149      * No check is done about this margin. A recommended value is around 1.
150      * @param inverseLocMargin margin in pixel size to compute inverse location with atmospheric refraction correction.
151      * @since 3.0
152      */
153     public void setInverseLocMargin(final double inverseLocMargin) {
154         atmosphericParams.setInverseLocMargin(inverseLocMargin);
155     }
156 
157     /** Compute the correction functions for pixel and lines.
158      * The corrections are computed for pixels and lines, on a regular grid at sensor level.
159      * The corrections are based on the difference on grid nodes (where direct loc is known with atmosphere refraction)
160      * and the sensor pixel found by inverse loc without atmosphere refraction.
161      * The bilinear interpolating functions are then computed for pixel and for line.
162      * Need to be computed only once for a given sensor with the same minLine and maxLine.
163      * @param sensorPixelGridInverseWithout inverse location grid WITHOUT atmospheric refraction
164      * @since 2.1
165      */
166     public void computeGridCorrectionFunctions(final SensorPixel[][] sensorPixelGridInverseWithout) {
167 
168         final int nbPixelGrid = atmosphericParams.getNbPixelGrid();
169         final int nbLineGrid = atmosphericParams.getNbLineGrid();
170         final double[] pixelGrid = atmosphericParams.getUgrid();
171         final double[] lineGrid = atmosphericParams.getVgrid();
172 
173         // Initialize the needed diff functions
174         final double[][] gridDiffPixel = new double[nbPixelGrid][nbLineGrid];
175         final double[][] gridDiffLine = new double[nbPixelGrid][nbLineGrid];
176 
177         // Compute the difference between grids nodes WITH - without atmosphere
178         for (int lineIndex = 0; lineIndex < nbLineGrid; lineIndex++) {
179             for (int pixelIndex = 0; pixelIndex < nbPixelGrid; pixelIndex++) {
180 
181                 if (sensorPixelGridInverseWithout[pixelIndex][lineIndex] != null) {
182                     final double diffLine = lineGrid[lineIndex] - sensorPixelGridInverseWithout[pixelIndex][lineIndex].getLineNumber();
183                     final double diffPixel = pixelGrid[pixelIndex] - sensorPixelGridInverseWithout[pixelIndex][lineIndex].getPixelNumber();
184                     gridDiffPixel[pixelIndex][lineIndex] = diffPixel;
185                     gridDiffLine[pixelIndex][lineIndex] = diffLine;
186 
187                 } else {
188                     // Impossible to find the sensor pixel in the given range lines
189                     throw new RuggedException(RuggedMessages.SENSOR_PIXEL_NOT_FOUND_IN_RANGE_LINES,
190                                               atmosphericParams.getMinLineSensor(), atmosphericParams.getMaxLineSensor());
191                 }
192             }
193         }
194         // Definition of the interpolating function for pixel and for line
195         this.bifPixel = new BilinearInterpolatingFunction(pixelGrid, lineGrid, gridDiffPixel);
196         this.bifLine = new BilinearInterpolatingFunction(pixelGrid, lineGrid, gridDiffLine);
197     }
198 
199     /**
200      * @return the bilinear interpolating function for pixel correction
201      */
202     public BilinearInterpolatingFunction getBifPixel() {
203         return bifPixel;
204     }
205 
206     /**
207      * @return the bilinear interpolating function for line correction
208      */
209     public BilinearInterpolatingFunction getBifLine() {
210         return bifLine;
211     }
212 }