1   /* Copyright 2022-2025 Thales Alenia Space
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.propagation.analytical.gnss.data;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.Field;
21  import org.hipparchus.analysis.differentiation.FieldGradient;
22  import org.hipparchus.analysis.differentiation.FieldGradientField;
23  import org.hipparchus.analysis.differentiation.Gradient;
24  import org.hipparchus.util.FastMath;
25  import org.hipparchus.util.MathArrays;
26  import org.orekit.time.TimeInterval;
27  import org.orekit.utils.Constants;
28  import org.orekit.utils.drivers.ParameterDriver;
29  
30  import java.util.Arrays;
31  import java.util.List;
32  
33  /** Factory for non-Keplerian drivers.
34   * @since 14.0
35   */
36  public class NonKeplerianDriversFactory {
37  
38      /** Name for time parameter. */
39      public static final String TIME = "GnssTime";
40  
41      /** Name for change rate in semi-major axis parameter. */
42      public static final String A_DOT = "GnssADot";
43  
44      /** Name for delta of satellite mean motion. */
45      public static final String DELTA_N0 = "GnssDeltaN0";
46  
47      /** Name for change rate in Δn₀. */
48      public static final String DELTA_N0_DOT = "GnssDeltaN0Dot";
49  
50      /** Name for inclination rate parameter. */
51      public static final String INCLINATION_RATE = "GnssInclinationRate";
52  
53      /** Name for longitude rate parameter. */
54      public static final String LONGITUDE_RATE = "GnssLongitudeRate";
55  
56      /** Name for cosine of latitude argument harmonic parameter. */
57      public static final String LATITUDE_COSINE = "GnssLatitudeCosine";
58  
59      /** Name for sine of latitude argument harmonic parameter. */
60      public static final String LATITUDE_SINE = "GnssLatitudeSine";
61  
62      /** Name for cosine of orbit radius harmonic parameter. */
63      public static final String RADIUS_COSINE = "GnssRadiusCosine";
64  
65      /** Name for sine of orbit radius harmonic parameter. */
66      public static final String RADIUS_SINE = "GnssRadiusSine";
67  
68      /** Name for cosine of inclination harmonic parameter. */
69      public static final String INCLINATION_COSINE = "GnssInclinationCosine";
70  
71      /** Name for sine of inclination harmonic parameter. */
72      public static final String INCLINATION_SINE = "GnssInclinationSine";
73  
74      /** Name for zero-th order clock correction parameter. */
75      public static final String AF0 = "GnssClock0";
76  
77      /** Name for first order clock correction parameter. */
78      public static final String AF1 = "GnssClock1";
79  
80      /** Name for second order clock correction parameter. */
81      public static final String AF2 = "GnssClock2";
82  
83      /** Index of time in the list returned by {@link #getParametersDrivers()}. */
84      public static final int TIME_INDEX = 0;
85  
86      /** Index of change rate in semi-major axis parameter in the list returned by {@link #getParametersDrivers()}. */
87      public static final int A_DOT_INDEX = TIME_INDEX + 1;
88  
89      /** Index of delta of satellite mean motion in the list returned by {@link #getParametersDrivers()}. */
90      public static final int DELTA_N0_INDEX = A_DOT_INDEX + 1;
91  
92      /** Index of change rate in Δn₀ in the list returned by {@link #getParametersDrivers()}. */
93      public static final int DELTA_N0_DOT_INDEX = DELTA_N0_INDEX + 1;
94  
95      /** Index of inclination rate in the list returned by {@link #getParametersDrivers()}. */
96      public static final int I_DOT_INDEX = DELTA_N0_DOT_INDEX + 1;
97  
98      /** Index of longitude rate in the list returned by {@link #getParametersDrivers()}. */
99      public static final int OMEGA_DOT_INDEX = I_DOT_INDEX + 1;
100 
101     /** Index of cosine on latitude argument in the list returned by {@link #getParametersDrivers()}. */
102     public static final int CUC_INDEX = OMEGA_DOT_INDEX + 1;
103 
104     /** Index of sine on latitude argument in the list returned by {@link #getParametersDrivers()}. */
105     public static final int CUS_INDEX = CUC_INDEX + 1;
106 
107     /** Index of cosine on radius in the list returned by {@link #getParametersDrivers()}. */
108     public static final int CRC_INDEX = CUS_INDEX + 1;
109 
110     /** Index of sine on radius in the list returned by {@link #getParametersDrivers()}. */
111     public static final int CRS_INDEX = CRC_INDEX + 1;
112 
113     /** Index of cosine on inclination in the list returned by {@link #getParametersDrivers()}. */
114     public static final int CIC_INDEX = CRS_INDEX + 1;
115 
116     /** Index of sine on inclination in the list returned by {@link #getParametersDrivers()}. */
117     public static final int CIS_INDEX = CIC_INDEX + 1;
118 
119     /** Index of zero-th order clock correction in the list returned by {@link #getParametersDrivers()}. */
120     public static final int AF0_INDEX = CIS_INDEX + 1;
121 
122     /** Index of first order clock correction in the list returned by {@link #getParametersDrivers()}. */
123     public static final int AF1_INDEX = AF0_INDEX + 1;
124 
125     /** Index of second order clock correction in the list returned by {@link #getParametersDrivers()}. */
126     public static final int AF2_INDEX = AF1_INDEX + 1;
127 
128     /** Size of parameters array. */
129     public static final int SIZE = AF2_INDEX + 1;
130 
131     /** Reference time. */
132     private final ParameterDriver timeDriver;
133 
134     /** Change rate in semi-major axis (m/s). */
135     private final ParameterDriver aDotDriver;
136 
137     /** Delta of satellite mean motion. */
138     private final ParameterDriver deltaN0Driver;
139 
140     /** Change rate in Δn₀. */
141     private final ParameterDriver deltaN0DotDriver;
142 
143     /** Inclination rate (rad/s). */
144     private final ParameterDriver iDotDriver;
145 
146     /** Rate of right ascension (rad/s). */
147     private final ParameterDriver domDriver;
148 
149     /** Amplitude of the cosine harmonic correction term to the argument of latitude. */
150     private final ParameterDriver cucDriver;
151 
152     /** Amplitude of the sine harmonic correction term to the argument of latitude. */
153     private final ParameterDriver cusDriver;
154 
155     /** Amplitude of the cosine harmonic correction term to the orbit radius. */
156     private final ParameterDriver crcDriver;
157 
158     /** Amplitude of the sine harmonic correction term to the orbit radius. */
159     private final ParameterDriver crsDriver;
160 
161     /** Amplitude of the cosine harmonic correction term to the inclination. */
162     private final ParameterDriver cicDriver;
163 
164     /** Amplitude of the sine harmonic correction term to the inclination. */
165     private final ParameterDriver cisDriver;
166 
167     /** SV zero-th order clock correction (s). */
168     private final ParameterDriver af0Driver;
169 
170     /** SV first order clock correction (s/s). */
171     private final ParameterDriver af1Driver;
172 
173     /** SV second order clock correction (s/s²). */
174     private final ParameterDriver af2Driver;
175 
176     /** Simple constructor.
177      */
178     public NonKeplerianDriversFactory() {
179 
180         // propagation drivers
181         this.timeDriver       = new ParameterDriver(TIME, 0.0, FastMath.scalb(1.0, -10),
182                                                     0, 7 * Constants.JULIAN_DAY, TimeInterval.UNLIMITED);
183         this.aDotDriver       = new ParameterDriver(A_DOT, 0.0, FastMath.scalb(1.0, -10),
184                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
185                                                     TimeInterval.UNLIMITED);
186         this.deltaN0Driver    = new ParameterDriver(DELTA_N0, 0.0, FastMath.scalb(1.0, -36),
187                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
188                                                     TimeInterval.UNLIMITED);
189         this.deltaN0DotDriver = new ParameterDriver(DELTA_N0_DOT, 0.0, FastMath.scalb(1.0, -46),
190                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
191                                                     TimeInterval.UNLIMITED);
192         this.iDotDriver       = new ParameterDriver(INCLINATION_RATE, 0.0, FastMath.scalb(1.0, -34),
193                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
194                                                     TimeInterval.UNLIMITED);
195         this.domDriver        = new ParameterDriver(LONGITUDE_RATE, 0.0, FastMath.scalb(1.0, -34),
196                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
197                                                     TimeInterval.UNLIMITED);
198         this.cucDriver        = new ParameterDriver(LATITUDE_COSINE, 0.0, FastMath.scalb(1.0, -24),
199                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
200                                                     TimeInterval.UNLIMITED);
201         this.cusDriver        = new ParameterDriver(LATITUDE_SINE, 0.0, FastMath.scalb(1.0, -24),
202                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
203                                                     TimeInterval.UNLIMITED);
204         this.crcDriver        = new ParameterDriver(RADIUS_COSINE, 0.0, FastMath.scalb(1.0,   0),
205                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
206                                                     TimeInterval.UNLIMITED);
207         this.crsDriver        = new ParameterDriver(RADIUS_SINE, 0.0, FastMath.scalb(1.0,   0),
208                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
209                                                     TimeInterval.UNLIMITED);
210         this.cicDriver        = new ParameterDriver(INCLINATION_COSINE, 0.0, FastMath.scalb(1.0, -24),
211                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
212                                                     TimeInterval.UNLIMITED);
213         this.cisDriver        = new ParameterDriver(INCLINATION_SINE, 0.0, FastMath.scalb(1.0, -24),
214                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
215                                                     TimeInterval.UNLIMITED);
216 
217         // clock drivers
218         this.af0Driver = new ParameterDriver(AF0, 0.0, FastMath.scalb(1.0, -26),
219                                              Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TimeInterval.UNLIMITED);
220         this.af1Driver = new ParameterDriver(AF1, 0.0, FastMath.scalb(1.0, -42),
221                                              Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TimeInterval.UNLIMITED);
222         this.af2Driver = new ParameterDriver(AF2, 0.0, FastMath.scalb(1.0, -58),
223                                              Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TimeInterval.UNLIMITED);
224 
225     }
226 
227     /** Reset the parameters drivers from existing elements.
228      * @param elements elements to use for reset
229      */
230     public void reset(final GNSSOrbitalElements<?> elements) {
231         reset(timeDriver,       elements.getTimeOfEphemeris().getSecondsInWeek());
232         reset(aDotDriver,       elements.getADot());
233         reset(deltaN0Driver,    elements.getDeltaN0());
234         reset(deltaN0DotDriver, elements.getDeltaN0Dot());
235         reset(iDotDriver,       elements.getIDot());
236         reset(domDriver,        elements.getOmegaDot());
237         reset(cucDriver,        elements.getCuc());
238         reset(cusDriver,        elements.getCus());
239         reset(crcDriver,        elements.getCrc());
240         reset(crsDriver,        elements.getCrs());
241         reset(cicDriver,        elements.getCic());
242         reset(cisDriver,        elements.getCis());
243         reset(af0Driver,        elements.getAf0());
244         reset(af1Driver,        elements.getAf1());
245         reset(af2Driver,        elements.getAf2());
246     }
247 
248     /** Reset the parameters drivers from existing elements.
249      * @param elements elements to use for reset
250      */
251     public void reset(final FieldGnssOrbitalElements<?, ?> elements) {
252         reset(timeDriver,       elements.getTimeOfEphemeris().getGnssDate().getSecondsInWeek());
253         reset(aDotDriver,       elements.getADot().getReal());
254         reset(deltaN0Driver,    elements.getDeltaN0().getReal());
255         reset(deltaN0DotDriver, elements.getDeltaN0Dot().getReal());
256         reset(iDotDriver,       elements.getIDot().getReal());
257         reset(domDriver,        elements.getOmegaDot().getReal());
258         reset(cucDriver,        elements.getCuc().getReal());
259         reset(cusDriver,        elements.getCus().getReal());
260         reset(crcDriver,        elements.getCrc().getReal());
261         reset(crsDriver,        elements.getCrs().getReal());
262         reset(cicDriver,        elements.getCic().getReal());
263         reset(cisDriver,        elements.getCis().getReal());
264         reset(af0Driver,        elements.getAf0().getReal());
265         reset(af1Driver,        elements.getAf1().getReal());
266         reset(af2Driver,        elements.getAf2().getReal());
267     }
268 
269     /** Reset one driver.
270      * @param driver driver to reset
271      * @param value new value (also used as reference)
272      */
273     private void reset (final ParameterDriver driver, final double value) {
274         driver.setValue(value);
275         driver.setReferenceValue(value);
276     }
277 
278     /** Get the 15 drivers for the non-Keplerian parameters.
279      * <p>
280      * Only the 15 non-Keplerian parameters (12 evolution parameters and 3 clock parameters)
281      * are listed here:
282      * Time driver at index {@link #TIME_INDEX},
283      * ADot driver at index {@link #A_DOT_INDEX},
284      * DeltaN0 driver at index {@link #DELTA_N0_INDEX},
285      * DeltaN0Dot driver at index {@link #DELTA_N0_DOT_INDEX},
286      * IDot driver at index {@link #I_DOT_INDEX},
287      * OmegaDot driver at index {@link #OMEGA_DOT_INDEX},
288      * Cuc driver at index {@link #CUC_INDEX},
289      * Cus driver at index {@link #CUS_INDEX},
290      * Crc driver at index {@link #CRC_INDEX},
291      * Crs driver at index {@link #CRS_INDEX},
292      * Cic driver at index {@link #CIC_INDEX},
293      * Cis driver at index {@link #CIS_INDEX},
294      * af0 driver at index {@link #AF0_INDEX},
295      * af1 driver at index {@link #AF1_INDEX},
296      * and af2 driver at index {@link #AF2_INDEX}.
297      * </p>
298      * @return 15 drivers for the non-Keplerian parameters
299      */
300     public List<ParameterDriver> getParametersDrivers() {
301 
302         // ensure the parameters are really at the advertised indices
303         final ParameterDriver[] array = new ParameterDriver[SIZE];
304 
305         array[TIME_INDEX]         = timeDriver;
306         array[A_DOT_INDEX]        = aDotDriver;
307         array[DELTA_N0_INDEX]     = deltaN0Driver;
308         array[DELTA_N0_DOT_INDEX] = deltaN0DotDriver;
309         array[I_DOT_INDEX]        = iDotDriver;
310         array[OMEGA_DOT_INDEX]    = domDriver;
311         array[CUC_INDEX]          = cucDriver;
312         array[CUS_INDEX]          = cusDriver;
313         array[CRC_INDEX]          = crcDriver;
314         array[CRS_INDEX]          = crsDriver;
315         array[CIC_INDEX]          = cicDriver;
316         array[CIS_INDEX]          = cisDriver;
317 
318         array[AF0_INDEX]         = af0Driver;
319         array[AF1_INDEX]         = af1Driver;
320         array[AF2_INDEX]         = af2Driver;
321 
322         return Arrays.asList(array);
323 
324     }
325 
326     /** Get driver for reference time of the GNSS orbit as a duration from week start.
327      * @return driver for reference time of the GNSS orbit (s)
328      */
329     public ParameterDriver getTimeDriver() {
330         return timeDriver;
331     }
332 
333     /** Get driver for change rate in semi-major axis.
334      * @return driver for the change rate in semi-major axis
335      */
336     public ParameterDriver getADotDriver() {
337         return aDotDriver;
338     }
339 
340     /** Get driver for the delta of satellite mean motion.
341      * @return driver for the delta of satellite mean motion
342      */
343     public ParameterDriver getDeltaN0Driver() {
344         return deltaN0Driver;
345     }
346 
347     /** Get driver for the change rate in Δn₀.
348      * @return driver for change rate in Δn₀
349      */
350     public ParameterDriver getDeltaN0DotDriver() {
351         return deltaN0DotDriver;
352     }
353 
354     /** Get driver for rate of inclination angle.
355      * @return driver for rate of inclination angle (rad/s)
356      */
357     public ParameterDriver getIDotDriver() {
358         return iDotDriver;
359     }
360 
361     /** Get driver for rate of right ascension.
362      * @return driver for rate of right ascension (rad/s)
363      */
364     public ParameterDriver getOmegaDotDriver() {
365         return domDriver;
366     }
367 
368     /** Get driver for amplitude of the cosine harmonic correction term to the argument of latitude.
369      * @return driver for amplitude of the cosine harmonic correction term to the argument of latitude (rad)
370      */
371     public ParameterDriver getCucDriver() {
372         return cucDriver;
373     }
374 
375     /** Get driver for amplitude of the sine harmonic correction term to the argument of latitude.
376      * @return driver for amplitude of the sine harmonic correction term to the argument of latitude (rad)
377      */
378     public ParameterDriver getCusDriver() {
379         return cusDriver;
380     }
381 
382     /** Get driver for amplitude of the cosine harmonic correction term to the orbit radius.
383      * @return driver for amplitude of the cosine harmonic correction term to the orbit radius (m)
384      */
385     public ParameterDriver getCrcDriver() {
386         return crcDriver;
387     }
388 
389     /** Get driver for amplitude of the sine harmonic correction term to the orbit radius.
390      * @return driver for amplitude of the sine harmonic correction term to the orbit radius (m)
391      */
392     public ParameterDriver getCrsDriver() {
393         return crsDriver;
394     }
395 
396     /** Get driver for amplitude of the cosine harmonic correction term to the angle of inclination.
397      * @return driver for amplitude of the cosine harmonic correction term to the angle of inclination (rad)
398      */
399     public ParameterDriver getCicDriver() {
400         return cicDriver;
401     }
402 
403     /** Get driver for amplitude of the sine harmonic correction term to the angle of inclination.
404      * @return driver for amplitude of the sine harmonic correction term to the angle of inclination (rad)
405      */
406     public ParameterDriver getCisDriver() {
407         return cisDriver;
408     }
409 
410     /** Get driver for SV zero-th order clock correction.
411      * @return driver for SV zero-th order clock correction (s)
412      */
413     public ParameterDriver getAf0Driver() {
414         return af0Driver;
415     }
416 
417     /** Get driver for SV first order clock correction.
418      * @return driver for SV first order clock correction (s/s)
419      */
420     public ParameterDriver getAf1Driver() {
421         return af1Driver;
422     }
423 
424     /** Get driver for SV second order clock correction.
425      * @return driver for SV second order clock correction (s/s²)
426      */
427     public ParameterDriver getAf2Driver() {
428         return af2Driver;
429     }
430 
431     /** Get the non-Keplerian elements as gradient variables or constants, depending on selection status.
432      * @param freeParameters total number of free parameters in the gradient
433      * @return non-Keplerian elements as gradient variables or constants
434      */
435     public Gradient[] toGradients(final int freeParameters) {
436         final Filler filler = new Filler(freeParameters);
437         filler.manage(timeDriver,       TIME_INDEX);
438         filler.manage(aDotDriver,       A_DOT_INDEX);
439         filler.manage(deltaN0Driver,    DELTA_N0_INDEX);
440         filler.manage(deltaN0DotDriver, DELTA_N0_DOT_INDEX);
441         filler.manage(iDotDriver,       I_DOT_INDEX);
442         filler.manage(domDriver,        OMEGA_DOT_INDEX);
443         filler.manage(cucDriver,        CUC_INDEX);
444         filler.manage(cusDriver,        CUS_INDEX);
445         filler.manage(crcDriver,        CRC_INDEX);
446         filler.manage(crsDriver,        CRS_INDEX);
447         filler.manage(cicDriver,        CIC_INDEX);
448         filler.manage(cisDriver,        CIS_INDEX);
449         filler.manage(af0Driver,        AF0_INDEX);
450         filler.manage(af1Driver,        AF1_INDEX);
451         filler.manage(af2Driver,        AF2_INDEX);
452         return filler.gradients;
453     }
454 
455     /** Get the non-Keplerian elements as gradient variables or constants, depending on selection status.
456      * @param <T> type of the field elements
457      * @param field field
458      * @param freeParameters total number of free parameters in the gradient
459      * @return non-Keplerian elements as gradient variables or constants
460      */
461     public <T extends CalculusFieldElement<T>> FieldGradient<T>[] toGradients(final Field<T> field,
462                                                                               final int freeParameters) {
463         final FieldFiller<T> filler = new FieldFiller<>(field, freeParameters);
464         filler.manage(timeDriver,       TIME_INDEX);
465         filler.manage(aDotDriver,       A_DOT_INDEX);
466         filler.manage(deltaN0Driver,    DELTA_N0_INDEX);
467         filler.manage(deltaN0DotDriver, DELTA_N0_DOT_INDEX);
468         filler.manage(iDotDriver,       I_DOT_INDEX);
469         filler.manage(domDriver,        OMEGA_DOT_INDEX);
470         filler.manage(cucDriver,        CUC_INDEX);
471         filler.manage(cusDriver,        CUS_INDEX);
472         filler.manage(crcDriver,        CRC_INDEX);
473         filler.manage(crsDriver,        CRS_INDEX);
474         filler.manage(cicDriver,        CIC_INDEX);
475         filler.manage(cisDriver,        CIS_INDEX);
476         filler.manage(af0Driver,        AF0_INDEX);
477         filler.manage(af1Driver,        AF1_INDEX);
478         filler.manage(af2Driver,        AF2_INDEX);
479         return filler.gradients;
480     }
481 
482     /** Array filler for gradients.
483      * @since 14.0
484      */
485     private static class Filler {
486 
487         /** Total number of free parameters in the gradient. */
488         private final int freeParameters;
489 
490         /** Gradient array. */
491         private final Gradient[] gradients;
492 
493         /** Partial derivative index. */
494         private int derivative;
495 
496         /** Simple constructor.
497           * @param freeParameters total number of free parameters in the gradient
498           */
499         Filler(final int freeParameters) {
500             this.freeParameters = freeParameters;
501             this.gradients      = new Gradient[SIZE];
502             this.derivative     = 6;
503         }
504 
505         /** Manage one driver.
506          * @param driver driver to manage
507          * @param index index of the driver in the array
508          */
509         private void manage(final ParameterDriver driver, final int index) {
510             if (driver.isSelected()) {
511                 // this driver should be managed as a variable
512                 gradients[index] = Gradient.variable(freeParameters, derivative, driver.getValue());
513                 ++derivative;
514             } else {
515                 // this driver should be managed as a constant
516                 gradients[index] = Gradient.constant(freeParameters, driver.getValue());
517             }
518         }
519 
520     }
521 
522     /** Array filler for gradients.
523      * @param <T> field to which elements belong
524      * @since 14.0
525      */
526     private static class FieldFiller<T extends CalculusFieldElement<T>> {
527 
528         /** Field. */
529         private final Field<T> field;
530 
531         /** Total number of free parameters in the gradient. */
532         private final int freeParameters;
533 
534         /** Gradient array. */
535         private final FieldGradient<T>[] gradients;
536 
537         /** Partial derivative index. */
538         private int derivative;
539 
540         /** Simple constructor.
541          * @param field field
542          * @param freeParameters total number of free parameters in the gradient
543          */
544         FieldFiller(final Field<T> field, final int freeParameters) {
545             this.field          = field;
546             this.freeParameters = freeParameters;
547             this.gradients      = MathArrays.buildArray(FieldGradientField.getField(field, freeParameters), SIZE);
548             this.derivative     = 6;
549         }
550 
551         /** Manage one driver.
552          * @param driver driver to manage
553          * @param index index of the driver in the array
554          */
555         private void manage(final ParameterDriver driver, final int index) {
556             if (driver.isSelected()) {
557                 // this driver should be managed as a variable
558                 gradients[index] = FieldGradient.variable(freeParameters, derivative,
559                                                           field.getZero().newInstance(driver.getValue()));
560                 ++derivative;
561             } else {
562                 // this driver should be managed as a constant
563                 gradients[index] = FieldGradient.constant(freeParameters,
564                                                           field.getZero().newInstance(driver.getValue()));
565             }
566         }
567 
568     }
569 
570 }