1 /* Copyright 2013-2019 CS Systèmes d'Information
2 * Licensed to CS Systèmes d'Information (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.los;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.List;
22 import java.util.stream.Stream;
23
24 import org.hipparchus.analysis.differentiation.DerivativeStructure;
25 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
26 import org.hipparchus.geometry.euclidean.threed.Vector3D;
27 import org.orekit.rugged.utils.DSGenerator;
28 import org.orekit.time.AbsoluteDate;
29 import org.orekit.utils.ParameterDriver;
30 import org.orekit.utils.ParameterObserver;
31
32 /** Builder for lines-of-sight list.
33 * <p>
34 * This class implements the <em>builder pattern</em> to create {@link TimeDependentLOS} instances.
35 * It does so by using a <em>fluent API</em> in order to clarify reading and allow
36 * later extensions with new configuration parameters.
37 * </p>
38 * <p>
39 * This builder aims at creating lines-of-sight directions which are
40 * the result of several transforms applied to an initial list of raw
41 * directions. It therefore allows to take into account the optical
42 * path due to mirrors and the alignments of sensors frames with respect
43 * to a spacecraft.
44 * </p>
45 * @see TimeDependentLOS
46 * @see <a href="https://en.wikipedia.org/wiki/Builder_pattern">Builder pattern (wikipedia)</a>
47 * @see <a href="https://en.wikipedia.org/wiki/Fluent_interface">Fluent interface (wikipedia)</a>
48 * @author Luc Maisonobe
49 */
50 public class LOSBuilder {
51
52 /** Raw fixed line-of-sights. */
53 private final List<Vector3D> rawLOS;
54
55 /** Transforms to be applied. */
56 private final List<LOSTransform> transforms;
57
58 /** Flag for time-independent only transforms. */
59 private boolean timeIndependent;
60
61 /** Create builder.
62 * @param rawLOS raw fixed lines-of-sight
63 */
64 public LOSBuilder(final List<Vector3D> rawLOS) {
65 this.rawLOS = rawLOS;
66 this.transforms = new ArrayList<LOSTransform>();
67 this.timeIndependent = true;
68 }
69
70 /** Add a transform to be applied after the already registered transforms.
71 * @param transform transform to be applied to the lines-of-sight
72 * @return the builder instance
73 */
74 public LOSBuilder addTransform(final TimeIndependentLOSTransform transform) {
75 transforms.add(new TransformAdapter(transform));
76 return this;
77 }
78
79 /** Add a transform to be applied after the already registered transforms.
80 * @param transform transform to be applied to the lines-of-sight
81 * @return the builder instance
82 */
83 public LOSBuilder addTransform(final LOSTransform transform) {
84 transforms.add(transform);
85 timeIndependent = false;
86 return this;
87 }
88
89 /** Build a lines-of-sight provider.
90 * @return lines-of-sight provider
91 */
92 public TimeDependentLOS build() {
93
94 if (timeIndependent) {
95 // fast implementation for time-independent lines-of-sight
96 return new FixedLOS(rawLOS, transforms);
97 } else {
98 // regular implementation, for time-dependent lines-of-sight
99 return new TransformsSequenceLOS(rawLOS, transforms);
100 }
101
102 }
103
104 /** Adapter from time-independent transform to time-dependent transform. */
105 private static class TransformAdapter implements LOSTransform {
106
107 /** Underlying transform. */
108 private final TimeIndependentLOSTransform transform;
109
110 /** Simple constructor.
111 * @param transform underlying time-independent transform
112 */
113 TransformAdapter(final TimeIndependentLOSTransform transform) {
114 this.transform = transform;
115 }
116
117 /** {@inheritDoc} */
118 @Override
119 public Vector3D transformLOS(final int i, final Vector3D los, final AbsoluteDate date) {
120 return transform.transformLOS(i, los);
121 }
122
123 /** {@inheritDoc} */
124 @Override
125 public FieldVector3D<DerivativeStructure> transformLOS(final int i, final FieldVector3D<DerivativeStructure> los,
126 final AbsoluteDate date, final DSGenerator generator) {
127 return transform.transformLOS(i, los, generator);
128 }
129
130 /** {@inheritDoc} */
131 @Override
132 public Stream<ParameterDriver> getParametersDrivers() {
133 return transform.getParametersDrivers();
134 }
135
136 }
137
138 /** Implement time-independent LOS by recomputing directions by applying all transforms each time. */
139 private static class TransformsSequenceLOS implements TimeDependentLOS {
140
141 /** Raw direction. */
142 private final Vector3D[] raw;
143
144 /** Transforms to be applied. */
145 private final List<LOSTransform> transforms;
146
147 /** Simple constructor.
148 * @param raw raw directions
149 * @param transforms transforms to apply
150 */
151 TransformsSequenceLOS(final List<Vector3D> raw, final List<LOSTransform> transforms) {
152
153 // copy the lists, to ensure immutability of the built object,
154 // in case addTransform is called again after build
155 // or the raw LOS list is changed by caller
156 this.raw = new Vector3D[raw.size()];
157 for (int i = 0; i < raw.size(); ++i) {
158 this.raw[i] = raw.get(i);
159 }
160
161 this.transforms = new ArrayList<LOSTransform>(transforms);
162
163 }
164
165 /** {@inheritDoc} */
166 public int getNbPixels() {
167 return raw.length;
168 }
169
170 /** {@inheritDoc} */
171 @Override
172 public Vector3D getLOS(final int index, final AbsoluteDate date) {
173 Vector3D los = raw[index];
174 for (final LOSTransform transform : transforms) {
175 los = transform.transformLOS(index, los, date);
176 }
177 return los.normalize();
178 }
179
180 /** {@inheritDoc} */
181 @Override
182 public FieldVector3D<DerivativeStructure> getLOSDerivatives(final int index, final AbsoluteDate date,
183 final DSGenerator generator) {
184
185 // the raw line of sights are considered to be constant
186 FieldVector3D<DerivativeStructure> los =
187 new FieldVector3D<DerivativeStructure>(generator.constant(raw[index].getX()),
188 generator.constant(raw[index].getY()),
189 generator.constant(raw[index].getZ()));
190
191 // apply the transforms, which depend on parameters and hence may introduce non-zero derivatives
192 for (final LOSTransform transform : transforms) {
193 los = transform.transformLOS(index, los, date, generator);
194 }
195
196 return los.normalize();
197
198 }
199
200 @Override
201 public Stream<ParameterDriver> getParametersDrivers() {
202 Stream<ParameterDriver> drivers = Stream.<ParameterDriver>empty();
203 for (final LOSTransform transform : transforms) {
204 drivers = Stream.concat(drivers, transform.getParametersDrivers());
205 }
206 return drivers;
207 }
208
209 }
210
211 /** Implement time-independent LOS by computing directions only when parameters are changed. */
212 private static class FixedLOS extends TransformsSequenceLOS {
213
214 /** transformed direction for los. */
215 private final Vector3D[] transformed;
216
217 /** Simple constructor.
218 * @param raw raw directions
219 * @param transforms transforms to apply (must be time-independent!)
220 */
221 FixedLOS(final List<Vector3D> raw, final List<LOSTransform> transforms) {
222
223 super(raw, transforms);
224 transformed = new Vector3D[raw.size()];
225
226 // we will reset the transforms to null when parameters are changed
227 final ParameterObserver resettingObserver = new ParameterObserver() {
228 /** {@inheritDoc} */
229 @Override
230 public void valueChanged(final double previousValue, final ParameterDriver driver) {
231 Arrays.fill(transformed, null);
232 }
233 };
234 getParametersDrivers().forEach(driver -> {
235 driver.addObserver(resettingObserver);
236 });
237 }
238
239 /** {@inheritDoc} */
240 @Override
241 public Vector3D getLOS(final int index, final AbsoluteDate date) {
242 if (transformed[index] == null) {
243 // recompute the transformed los direction only if needed
244 transformed[index] = super.getLOS(index, date);
245 }
246 return transformed[index];
247 }
248
249 }
250
251 }