1   /* Copyright 2025-2026 Hawkeye 360 (HE360)
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  
18  package org.orekit.estimation.measurements;
19  
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.List;
23  
24  import org.hipparchus.Field;
25  import org.hipparchus.analysis.differentiation.Gradient;
26  import org.hipparchus.util.FastMath;
27  import org.orekit.orbits.CartesianOrbit;
28  import org.orekit.orbits.FieldCartesianOrbit;
29  import org.orekit.propagation.SpacecraftState;
30  import org.orekit.time.AbsoluteDate;
31  import org.orekit.time.clocks.QuadraticClockModel;
32  import org.orekit.utils.AbsolutePVCoordinates;
33  import org.orekit.utils.FieldAbsolutePVCoordinates;
34  import org.orekit.utils.FieldPVCoordinatesProvider;
35  import org.orekit.utils.PVCoordinatesProvider;
36  import org.orekit.utils.ParameterDriver;
37  import org.orekit.utils.TimeStampedFieldPVCoordinates;
38  import org.orekit.utils.TimeStampedPVCoordinates;
39  
40  /** Abstract class underlying both observed and observing measurement
41   * objects.  Contains the QuadraticClockModel and the ability to store a
42   * master list of all parameter drivers associated with the object.
43   *
44   * @author Brianna Aubin
45   * @since 14.0
46   */
47  public abstract class AbstractParticipant implements MeasurementParticipant {
48  
49      /** Checkstyle is annoying sometimes. */
50      private static final String CLOCK_STRING = "-clock";
51  
52      /** Clock offset scaling factor.
53       * <p>
54       * We use a power of 2 to avoid numeric noise introduction
55       * in the multiplications/divisions sequences.
56       * </p>
57       */
58      private static final double CLOCK_OFFSET_SCALE = FastMath.scalb(1.0, -10);
59  
60      /** Stores quadratic clock model. */
61      private final QuadraticClockModel quadraticClockModel;
62  
63      /** Stores list of all ParameterDriver values. */
64      private final List<ParameterDriver> parameterDrivers = new ArrayList<>();
65  
66      /** Name of the satellite. */
67      private final String name;
68  
69      /** Simple constructor.
70       * @param name name of MeasurementObject
71       */
72      protected AbstractParticipant(final String name) {
73          this(name, createEmptyQuadraticClock(name));
74      }
75  
76      /** Simple constructor.
77       * @param name name of MeasurementObject
78       * @param quadraticClock clock belonging to MeasurementObject
79       */
80      protected AbstractParticipant(final String name, final QuadraticClockModel quadraticClock) {
81  
82          // Initialize member variables
83          this.name = name;
84          this.quadraticClockModel = quadraticClock;
85  
86          // Add clock parameters
87          parameterDrivers.add(quadraticClockModel.getClockBiasDriver());
88          parameterDrivers.add(quadraticClockModel.getClockDriftDriver());
89          parameterDrivers.add(quadraticClockModel.getClockAccelerationDriver());
90      }
91  
92      /** Get the MeasurementObject name.
93       * @return name for the object
94       */
95      public final String getName() {
96          return name;
97      }
98  
99      /** Creates a quadratic clock with zero displacement.
100      * @param name name of object that is holding the clock
101      * @return new quadratic clock model
102      */
103     protected static QuadraticClockModel createEmptyQuadraticClock(final String name) {
104         return new QuadraticClockModel(new ParameterDriver(name + CLOCK_STRING + BIAS_SUFFIX,
105                                                     0.0, CLOCK_OFFSET_SCALE,
106                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY),
107                                            new ParameterDriver(name + CLOCK_STRING + DRIFT_SUFFIX,
108                                                     0.0, CLOCK_OFFSET_SCALE,
109                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY),
110                                            new ParameterDriver(name + CLOCK_STRING + ACCELERATION_SUFFIX,
111                                                     0.0, CLOCK_OFFSET_SCALE,
112                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
113     }
114 
115     /** Get a quadratic clock model valid at some date.
116      * @return quadratic clock model
117      */
118     public final QuadraticClockModel getQuadraticClockModel() {
119         return quadraticClockModel;
120     }
121 
122     /** Get model parameters.
123      * @return model parameters, will throw an
124      * exception if one PDriver has several values driven. If
125      * it's the case (if at least 1 PDriver of the model has several values
126      * driven) the method {@link org.orekit.utils.ParameterDriversProvider#getParameters(AbsoluteDate)} must be used.
127      */
128     public List<ParameterDriver> getParametersDrivers() {
129         return Collections.unmodifiableList(parameterDrivers);
130     }
131 
132     /**
133      * Add a single parameter.
134      * @param parameterDriver parameter being added to the MeasurementObject
135      */
136     protected final void addParameterDriver(final ParameterDriver parameterDriver) {
137         parameterDrivers.add(parameterDriver);
138     }
139 
140     /**
141      * Create PV provider from position-velocity-acceleration vector and template state.
142      * @param templateState template state
143      * @param pvCoordinates position-velocity-acceleration
144      * @return position-velocity-acceleration provider
145      */
146     public static PVCoordinatesProvider extractPVCoordinatesProvider(final SpacecraftState templateState,
147                                                                      final TimeStampedPVCoordinates pvCoordinates) {
148         if (templateState.isOrbitDefined()) {
149             final CartesianOrbit cartesianOrbit = new CartesianOrbit(pvCoordinates, templateState.getFrame(),
150                     templateState.getOrbit().getMu());
151             return templateState.getOrbit().getType().convertType(cartesianOrbit);
152         } else {
153             return new AbsolutePVCoordinates(templateState.getFrame(), pvCoordinates);
154         }
155     }
156 
157     /**
158      * Create PV provider from position-velocity-acceleration vector and template state.
159      *
160      * @param templateState template state
161      * @param pvCoordinates position-velocity-acceleration
162      * @return position-velocity-acceleration provider
163      */
164     public static FieldPVCoordinatesProvider<Gradient> extractFieldPVCoordinatesProvider(final SpacecraftState templateState,
165                                                                                          final TimeStampedFieldPVCoordinates<Gradient> pvCoordinates) {
166         final Field<Gradient> field = pvCoordinates.getDate().getField();
167         if (templateState.isOrbitDefined()) {
168             final FieldCartesianOrbit<Gradient> cartesianOrbit = new FieldCartesianOrbit<>(pvCoordinates, templateState.getFrame(),
169                     field.getZero().newInstance(templateState.getOrbit().getMu()));
170             return templateState.getOrbit().getType().convertType(cartesianOrbit);
171         } else {
172             return new FieldAbsolutePVCoordinates<>(templateState.getFrame(), pvCoordinates);
173         }
174     }
175 }