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.ionosphere;
18
19 import java.util.Collections;
20 import java.util.List;
21
22 import org.hipparchus.CalculusFieldElement;
23 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
24 import org.hipparchus.geometry.euclidean.threed.Vector3D;
25 import org.hipparchus.util.FastMath;
26 import org.orekit.frames.FieldStaticTransform;
27 import org.orekit.frames.StaticTransform;
28 import org.orekit.frames.TopocentricFrame;
29 import org.orekit.propagation.FieldSpacecraftState;
30 import org.orekit.propagation.SpacecraftState;
31 import org.orekit.time.AbsoluteDate;
32 import org.orekit.time.FieldAbsoluteDate;
33 import org.orekit.utils.ParameterDriver;
34
35 /**
36 * An estimated ionospheric model. The ionospheric delay is computed according to the formula:
37 * <p>
38 * 40.3
39 * δ = -------- * STEC with, STEC = VTEC * F(elevation)
40 * f²
41 * </p>
42 * With:
43 * <ul>
44 * <li>f: The frequency of the signal in Hz.</li>
45 * <li>STEC: The Slant Total Electron Content in TECUnits.</li>
46 * <li>VTEC: The Vertical Total Electron Content in TECUnits.</li>
47 * <li>F(elevation): A mapping function which depends on satellite elevation.</li>
48 * </ul>
49 * The VTEC is estimated as a {@link ParameterDriver}
50 *
51 * @author Bryan Cazabonne
52 * @since 10.2
53 */
54 public class EstimatedIonosphericModel implements IonosphericModel, IonosphericDelayModel {
55
56 /** Name of the parameter of this model: the Vertical Total Electron Content. */
57 public static final String VERTICAL_TOTAL_ELECTRON_CONTENT = "vertical total electron content";
58
59 /** Ionospheric delay factor. */
60 private static final double FACTOR = 40.3e16;
61
62 /** Ionospheric mapping Function model. */
63 private final transient IonosphericMappingFunction model;
64
65 /** Driver for the Vertical Total Electron Content.*/
66 private final transient ParameterDriver vtec;
67
68
69 /**
70 * Build a new instance.
71 * @param model ionospheric mapping function
72 * @param vtecValue value of the Vertical Total Electron Content in TECUnits
73 */
74 public EstimatedIonosphericModel(final IonosphericMappingFunction model, final double vtecValue) {
75 this.model = model;
76 this.vtec = new ParameterDriver(EstimatedIonosphericModel.VERTICAL_TOTAL_ELECTRON_CONTENT,
77 vtecValue, FastMath.scalb(1.0, 3), 0.0, 1000.0);
78 }
79
80 /** {@inheritDoc} */
81 @Override
82 public double pathDelay(final SpacecraftState state, final TopocentricFrame baseFrame,
83 final double frequency, final double[] parameters) {
84 return pathDelay(state, baseFrame, state.getDate(), frequency, parameters);
85 }
86
87 /** {@inheritDoc} */
88 @Override
89 public double pathDelay(final SpacecraftState state,
90 final TopocentricFrame baseFrame, final AbsoluteDate receptionDate,
91 final double frequency, final double[] parameters) {
92
93 // we use transform from base frame to inert frame and invert it
94 // because base frame could be peered with inertial frame (hence improving performances with caching)
95 // but the reverse is almost never used
96 final StaticTransform base2Inert = baseFrame.getStaticTransformTo(state.getFrame(), receptionDate);
97 final Vector3D position = base2Inert.getInverse().transformPosition(state.getPosition());
98
99 // Elevation in radians
100 final double elevation = position.getDelta();
101
102 // Only consider measures above the horizon
103 if (elevation > 0.0) {
104 // Delay
105 return pathDelay(elevation, frequency, parameters);
106 }
107
108 return 0.0;
109 }
110
111 /**
112 * Calculates the ionospheric path delay for the signal path from a ground
113 * station to a satellite.
114 * <p>
115 * The path delay is computed for any elevation angle.
116 * </p>
117 * @param elevation elevation of the satellite in radians
118 * @param frequency frequency of the signal in Hz
119 * @param parameters ionospheric model parameters
120 * @return the path delay due to the ionosphere in m
121 */
122 public double pathDelay(final double elevation, final double frequency, final double[] parameters) {
123 // Square of the frequency
124 final double freq2 = frequency * frequency;
125 // Mapping factor
126 final double fz = model.mappingFactor(elevation);
127 // "Slant" Total Electron Content
128 final double stec = parameters[0] * fz;
129 // Delay computation
130 final double alpha = FACTOR / freq2;
131 return alpha * stec;
132 }
133
134 /** {@inheritDoc} */
135 @Override
136 public <T extends CalculusFieldElement<T>> T pathDelay(final FieldSpacecraftState<T> state, final TopocentricFrame baseFrame,
137 final double frequency, final T[] parameters) {
138 return pathDelay(state, baseFrame, state.getDate(), frequency, parameters);
139 }
140
141 /** {@inheritDoc} */
142 @Override
143 public <T extends CalculusFieldElement<T>> T pathDelay(final FieldSpacecraftState<T> state,
144 final TopocentricFrame baseFrame, final FieldAbsoluteDate<T> receptionDate,
145 final double frequency, final T[] parameters) {
146
147 // we use transform from base frame to inert frame and invert it
148 // because base frame could be peered with inertial frame (hence improving performances with caching)
149 // but the reverse is almost never used
150 final FieldStaticTransform<T> base2Inert = baseFrame.getStaticTransformTo(state.getFrame(), receptionDate);
151 final FieldVector3D<T> position = base2Inert.getInverse().transformPosition(state.getPosition());
152
153 // Elevation in radians
154 final T elevation = position.getDelta();
155
156 if (elevation.getReal() > 0.0) {
157 // Delay
158 return pathDelay(elevation, frequency, parameters);
159 }
160
161 return elevation.getField().getZero();
162 }
163
164 /**
165 * Calculates the ionospheric path delay for the signal path from a ground
166 * station to a satellite.
167 * <p>
168 * The path delay is computed for any elevation angle.
169 * </p>
170 * @param <T> type of the elements
171 * @param elevation elevation of the satellite in radians
172 * @param frequency frequency of the signal in Hz
173 * @param parameters ionospheric model parameters at state date
174 * @return the path delay due to the ionosphere in m
175 */
176 public <T extends CalculusFieldElement<T>> T pathDelay(final T elevation, final double frequency, final T[] parameters) {
177 // Square of the frequency
178 final double freq2 = frequency * frequency;
179 // Mapping factor
180 final T fz = model.mappingFactor(elevation);
181 // "Slant" Total Electron Content
182 final T stec = parameters[0].multiply(fz);
183 // Delay computation
184 final double alpha = FACTOR / freq2;
185 return stec.multiply(alpha);
186 }
187
188 @Override
189 public List<ParameterDriver> getParametersDrivers() {
190 return Collections.singletonList(vtec);
191 }
192
193 }