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 }