LOSBuilder.java
/* Copyright 2013-2016 CS Systèmes d'Information
* Licensed to CS Systèmes d'Information (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.rugged.los;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
import org.apache.commons.math3.geometry.euclidean.threed.FieldVector3D;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.orekit.rugged.errors.RuggedException;
import org.orekit.rugged.errors.RuggedMessages;
import org.orekit.rugged.utils.ParametricModel;
import org.orekit.time.AbsoluteDate;
/** Builder for lines-of-sight list.
* <p>
* This class implements the <em>builder pattern</em> to create {@link TimeDependentLOS} instances.
* It does so by using a <em>fluent API</em> in order to clarify reading and allow
* later extensions with new configuration parameters.
* </p>
* <p>
* This builder aims at creating lines-of-sight directions which are
* the result of several transforms applied to an initial list of raw
* directions. It therefore allows to take into account the optical
* path due to mirrors and the alignments of sensors frames with respect
* to a spacecraft.
* </p>
* @see TimeDependentLOS
* @see <a href="https://en.wikipedia.org/wiki/Builder_pattern">Builder pattern (wikipedia)</a>
* @see <a href="https://en.wikipedia.org/wiki/Fluent_interface">Fluent interface (wikipedia)</a>
* @author Luc Maisonobe
*/
public class LOSBuilder {
/** Raw fixed ine-of-sights. */
private final List<Vector3D> rawLOS;
/** Transforms to be applied. */
private final List<LOSTransform> transforms;
/** Flag for time-independent only transforms. */
private boolean timeIndependent;
/** Create builder.
* @param rawLOS raw fixed lines-of-sight
*/
public LOSBuilder(final List<Vector3D> rawLOS) {
this.rawLOS = rawLOS;
this.transforms = new ArrayList<LOSTransform>();
this.timeIndependent = true;
}
/** Add a transform to be applied after the already registered transforms.
* @param transform transform to be applied to the lines-of-sight
* @return the builder instance
*/
public LOSBuilder addTransform(final TimeIndependentLOSTransform transform) {
transforms.add(new TransformAdapter(transform));
return this;
}
/** Add a transform to be applied after the already registered transforms.
* @param transform transform to be applied to the lines-of-sight
* @return the builder instance
*/
public LOSBuilder addTransform(final LOSTransform transform) {
transforms.add(transform);
timeIndependent = false;
return this;
}
/** Build a lines-of-sight provider.
* @return lines-of-sight provider
*/
public TimeDependentLOS build() {
if (timeIndependent) {
// fast implementation for time-independent lines-of-sight
return new FixedLOS(rawLOS, transforms);
} else {
// regular implementation, for time-dependent lines-of-sight
return new TransformsSequenceLOS(rawLOS, transforms);
}
}
/** Adapter from time-independent transform to time-dependent transform. */
private static class TransformAdapter implements LOSTransform {
/** Underlying transform. */
private final TimeIndependentLOSTransform transform;
/** Simple constructor.
* @param transform underlying time-independent transform
*/
public TransformAdapter(final TimeIndependentLOSTransform transform) {
this.transform = transform;
}
/** {@inheritDoc} */
@Override
public int getNbEstimatedParameters() {
return transform.getNbEstimatedParameters();
}
/** {@inheritDoc} */
@Override
public void getEstimatedParameters(final double[] parameters, final int start, final int length)
throws RuggedException {
transform.getEstimatedParameters(parameters, start, length);
}
/** {@inheritDoc} */
@Override
public void setEstimatedParameters(final double[] parameters, final int start, final int length)
throws RuggedException {
transform.setEstimatedParameters(parameters, start, length);
}
/** {@inheritDoc} */
@Override
public Vector3D transformLOS(final int i, final Vector3D los, final AbsoluteDate date) {
return transform.transformLOS(i, los);
}
/** {@inheritDoc} */
@Override
public FieldVector3D<DerivativeStructure> transformLOS(final int i, final FieldVector3D<DerivativeStructure> los,
final AbsoluteDate date) {
return transform.transformLOS(i, los);
}
}
/** Implement time-independent LOS by recomputing directions by applying all transforms each time. */
private static class TransformsSequenceLOS implements ParametricModel, TimeDependentLOS {
/** Raw direction. */
private final Vector3D[] raw;
/** Transforms to be applied. */
private final LOSTransform[] transforms;
/** Total number of estimated parameters. */
private final int total;
/** Simple constructor.
* @param raw raw directions
* @param transforms transforms to apply
*/
public TransformsSequenceLOS(final List<Vector3D> raw, final List<LOSTransform> transforms) {
// copy the lists, to ensure immutability of the built object,
// in case addTransform is called again after build
// or the raw LOS list is changed by caller
this.raw = new Vector3D[raw.size()];
for (int i = 0; i < raw.size(); ++i) {
this.raw[i] = raw.get(i);
}
this.transforms = new LOSTransform[transforms.size()];
int n = 0;
for (int i = 0; i < transforms.size(); ++i) {
final LOSTransform transform = transforms.get(i);
this.transforms[i] = transform;
n += transform.getNbEstimatedParameters();
}
this.total = n;
}
/** {@inheritDoc} */
@Override
public int getNbEstimatedParameters() {
return total;
}
/** {@inheritDoc} */
@Override
public void getEstimatedParameters(final double[] parameters, final int start, final int length)
throws RuggedException {
// global check
checkSlice(length);
// retrieve parameters for all transforms
int offset = 0;
for (final ParametricModel model : transforms) {
final int n = model.getNbEstimatedParameters();
model.getEstimatedParameters(parameters, offset, n);
offset += n;
}
}
/** {@inheritDoc} */
@Override
public void setEstimatedParameters(final double[] parameters, final int start, final int length)
throws RuggedException {
// global check
checkSlice(length);
// set parameters for all transforms
int offset = 0;
for (final ParametricModel model : transforms) {
final int n = model.getNbEstimatedParameters();
model.setEstimatedParameters(parameters, offset, n);
offset += n;
}
}
/** Check the number of parameters of an array slice.
* @param length number of elements in the array slice to consider
* @exception RuggedException if the size of the slice does not match
* the {@link #getNbEstimatedParameters() number of estimated parameters}
*/
private void checkSlice(final int length) throws RuggedException {
if (getNbEstimatedParameters() != length) {
throw new RuggedException(RuggedMessages.ESTIMATED_PARAMETERS_NUMBER_MISMATCH,
getNbEstimatedParameters(), length);
}
}
/** {@inheritDoc} */
public int getNbPixels() {
return raw.length;
}
/** {@inheritDoc} */
@Override
public Vector3D getLOS(final int index, final AbsoluteDate date) {
Vector3D los = raw[index];
for (final LOSTransform transform : transforms) {
los = transform.transformLOS(index, los, date);
}
return los.normalize();
}
/** {@inheritDoc} */
@Override
public FieldVector3D<DerivativeStructure> getLOS(final int index, final AbsoluteDate date,
final double[] parameters) {
// non-adjustable LOS do not depend on any parameters
final Vector3D los = getLOS(index, date);
return new FieldVector3D<DerivativeStructure>(new DerivativeStructure(parameters.length, 1, los.getX()),
new DerivativeStructure(parameters.length, 1, los.getY()),
new DerivativeStructure(parameters.length, 1, los.getZ()));
}
}
/** Implement time-independent LOS by computing directions only when parameters are changed. */
private static class FixedLOS extends TransformsSequenceLOS {
/** transformed direction for los. */
private final Vector3D[] transformed;
/** Simple constructor.
* @param raw raw directions
* @param transforms transforms to apply (must be time-independent!)
*/
public FixedLOS(final List<Vector3D> raw, final List<LOSTransform> transforms) {
super(raw, transforms);
transformed = new Vector3D[raw.size()];
}
/** {@inheritDoc} */
@Override
public void setEstimatedParameters(final double[] parameters, final int start, final int length)
throws RuggedException {
// update the transforms
super.setEstimatedParameters(parameters, start, length);
// unset the directions, to ensure they get recomputed if needed
Arrays.fill(transformed, null);
}
/** {@inheritDoc} */
@Override
public Vector3D getLOS(final int index, final AbsoluteDate date) {
if (transformed[index] == null) {
// recompute the transformed los direction only if needed
transformed[index] = super.getLOS(index, date);
}
return transformed[index];
}
}
}