diff --git a/src/test/java/org/orekit/rugged/TestUtils.java b/src/test/java/org/orekit/rugged/TestUtils.java index e37b7a38e8e757b70c4c2c890ec471a6346bf9f1..d67cb3e20b2a718671c2eeb84a4898c5757b2b6e 100644 --- a/src/test/java/org/orekit/rugged/TestUtils.java +++ b/src/test/java/org/orekit/rugged/TestUtils.java @@ -24,6 +24,8 @@ import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; import org.hipparchus.util.FastMath; import org.junit.Assert; +import static org.junit.Assert.assertEquals; + import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -35,6 +37,7 @@ import org.orekit.attitudes.NadirPointing; import org.orekit.attitudes.YawCompensation; import org.orekit.bodies.BodyShape; import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.data.DataProvidersManager; import org.orekit.errors.OrekitException; @@ -60,6 +63,7 @@ import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.propagation.sampling.OrekitFixedStepHandler; import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials; import org.orekit.propagation.semianalytical.dsst.utilities.NewcombOperators; +import org.orekit.rugged.linesensor.SensorPixel; import org.orekit.rugged.los.LOSBuilder; import org.orekit.rugged.los.TimeDependentLOS; import org.orekit.time.AbsoluteDate; @@ -230,28 +234,7 @@ public class TestUtils { FastMath.PI, PositionAngle.TRUE, eme2000, date, mu); } - - /** Create an orbit at a chosen date for Refining tests - * @param mu Earth gravitational constant - * @param date the chosen date - * @return the orbit - * @throws OrekitException - */ - public Orbit createOrbit(final double mu, final AbsoluteDate date) throws OrekitException { - - final Frame eme2000 = FramesFactory.getEME2000(); - return new CircularOrbit(694000.0 + Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - -4.029194321683225E-4, - 0.0013530362644647786, - FastMath.toRadians(98.2), // Pleiades inclination 98.2 deg - FastMath.toRadians(-86.47 + 180), - FastMath.toRadians(135.9 + 0.3), - PositionAngle.TRUE, - eme2000, - date, - mu); - } - + /** Create the propagator of an orbit. * @return propagator of the orbit * @throws OrekitException @@ -367,5 +350,77 @@ public class TestUtils { propagator.propagate(maxDate); return list; } + + /** + * Asserts that two SensorPixel are equal to within a positive delta for each component. + * For more details see: + * org.junit.assert.assertEquals(String message, double expected, double actual, double delta) + */ + static public void sensorPixelAssertEquals(SensorPixel expected, SensorPixel result, double delta) { + + assertEquals(null,expected.getLineNumber(), result.getLineNumber(), delta); + assertEquals(null,expected.getPixelNumber(), result.getPixelNumber(), delta); + } + + /** + * Tell if two SensorPixel are the same, where each components are equal to within a positive delta. + * For more details see: + * org.junit.assert.assertEquals(String message, double expected, double actual, double delta) + * @param expected expected sensor pixel + * @param result actual sensor pixel + * @param delta delta within two values are consider equal + * @return true if the two SensorPixel are the same, false otherwise + */ + static public Boolean sameSensorPixels(SensorPixel expected, SensorPixel result, double delta) { + + Boolean sameSensorPixel = false; + + // To have same pixel (true) + sameSensorPixel = !(isDifferent(expected.getLineNumber(), result.getLineNumber(), delta) || + isDifferent(expected.getPixelNumber(), result.getPixelNumber(), delta)); + + return sameSensorPixel; + } + + /** + * Tell if two SensorPixel are the same, where each components are equal to within a positive delta. + * For more details see: + * org.junit.assert.assertEquals(String message, double expected, double actual, double delta) + * @param expected expected sensor pixel + * @param result actual sensor pixel + * @param delta delta within two values are consider equal + * @return true if the two SensorPixel are the same, false otherwise + */ + static public Boolean sameGeodeticPoints(GeodeticPoint expected, GeodeticPoint result, double delta) { + + Boolean sameGeodeticPoint = false; + + // To have same GeodeticPoint (true) + sameGeodeticPoint = !(isDifferent(expected.getLatitude(), result.getLatitude(), delta) || + isDifferent(expected.getLongitude(), result.getLongitude(), delta) || + isDifferent(expected.getAltitude(), result.getAltitude(), delta)); + + return sameGeodeticPoint; + } + + + /** Return true if two double values are different within a positive delta. + * @param val1 first value to compare + * @param val2 second value to compare + * @param delta delta within the two values are consider equal + * @return true is different, false if equal within the positive delta + */ + static private boolean isDifferent(double val1, double val2, double delta) { + + if (Double.compare(val1, val2) == 0) { + return false; + } + if ((FastMath.abs(val1 - val2) <= delta)) { + return false; + } + return true; + } + + } diff --git a/src/test/java/org/orekit/rugged/adjustment/AdjustmentContextTest.java b/src/test/java/org/orekit/rugged/adjustment/AdjustmentContextTest.java index 3d45d74577081676fae919421df232f931f7888d..e897ecdf4e08657aa84d51c6770ffd85af56a827 100644 --- a/src/test/java/org/orekit/rugged/adjustment/AdjustmentContextTest.java +++ b/src/test/java/org/orekit/rugged/adjustment/AdjustmentContextTest.java @@ -16,21 +16,26 @@ */ package org.orekit.rugged.adjustment; -import static org.junit.Assert.assertTrue; - import java.lang.reflect.Field; + import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer.Optimum; + import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; + import org.orekit.rugged.adjustment.measurements.Observables; +import org.orekit.rugged.adjustment.measurements.SensorMapping; +import org.orekit.rugged.adjustment.util.InitInterRefiningTest; import org.orekit.rugged.api.Rugged; import org.orekit.rugged.errors.RuggedException; -import org.orekit.rugged.utils.RefiningTest; +import org.orekit.rugged.errors.RuggedMessages; +import org.orekit.rugged.linesensor.SensorPixel; public class AdjustmentContextTest { @@ -38,16 +43,18 @@ public class AdjustmentContextTest { public void setUp() { try { - RefiningTest refiningTest = new RefiningTest(); + // One must set a context for the adjustment ... Here we choose an inter sensors optimization problem + InitInterRefiningTest refiningTest = new InitInterRefiningTest(); refiningTest.initRefiningTest(); ruggedList = refiningTest.getRuggedList(); int lineSampling = 1000; int pixelSampling = 1000; + + double earthConstraintWeight = 0.1; - measurements = refiningTest.generateNoisyPoints(lineSampling, pixelSampling); - numberOfParameters = refiningTest.getParameterToAdjust(); + measurements = refiningTest.generateNoisyPoints(lineSampling, pixelSampling, earthConstraintWeight, false); } catch (RuggedException re) { Assert.fail(re.getLocalizedMessage()); @@ -59,81 +66,126 @@ public class AdjustmentContextTest { AdjustmentContext adjustmentContext = new AdjustmentContext(ruggedList, measurements); - // Check if the default OptimizerId is the expected one + // Check if the default OptimizerId is the expected one (use reflectivity) Field optimizerId = adjustmentContext.getClass().getDeclaredField("optimizerID"); optimizerId.setAccessible(true); OptimizerId defaultOptimizerId = (OptimizerId) optimizerId.get(adjustmentContext); + Assert.assertTrue((defaultOptimizerId == OptimizerId.GAUSS_NEWTON_QR)); - System.out.println(defaultOptimizerId); - assertTrue((defaultOptimizerId == OptimizerId.GAUSS_NEWTON_QR)); + // Check if the change of the default OptimizerId is correct + adjustmentContext.setOptimizer(OptimizerId.GAUSS_NEWTON_LU); + OptimizerId modifiedOptimizerId = (OptimizerId) optimizerId.get(adjustmentContext); + Assert.assertTrue((modifiedOptimizerId == OptimizerId.GAUSS_NEWTON_LU)); -// // Check if the change of the default OptimizerId is correct -// adjustmentContext.setOptimizer(OptimizerId.GAUSS_NEWTON_LU); -// OptimizerId modifiedOptimizerId = (OptimizerId) optimizerId.get(adjustmentContext); -// System.out.println(modifiedOptimizerId); -// assertTrue((modifiedOptimizerId == OptimizerId.GAUSS_NEWTON_LU)); - -// // Check if the change of the default OptimizerId is correct -// adjustmentContext.setOptimizer(OptimizerId.GAUSS_NEWTON_LU); -// OptimizerId modifiedOptimizerId = (OptimizerId) optimizerId.get(adjustmentContext); -// System.out.println(modifiedOptimizerId); -// assertTrue((modifiedOptimizerId == OptimizerId.GAUSS_NEWTON_LU)); + // Check if the change of the default OptimizerId is correct + adjustmentContext.setOptimizer(OptimizerId.LEVENBERG_MARQUADT); + modifiedOptimizerId = (OptimizerId) optimizerId.get(adjustmentContext); + Assert.assertTrue((modifiedOptimizerId == OptimizerId.LEVENBERG_MARQUADT)); - -// OptimizerId.valueOf(OptimizerId.LEVENBERG_MARQUADT.toString()); - } - + } @Test public void testEstimateFreeParameters() throws RuggedException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { AdjustmentContext adjustmentContext = new AdjustmentContext(ruggedList, measurements); - List<String> ruggedNameList = new ArrayList<String>(); - for(Rugged rugged : ruggedList) { - ruggedNameList.add(rugged.getName()); - } - final int maxIterations = 120; - final double convergenceThreshold = 1.e-7; + for (OptimizerId optimizer : OptimizerId.values()) { + + // Set the optimizer + adjustmentContext.setOptimizer(optimizer); - Optimum optimum = adjustmentContext.estimateFreeParameters(ruggedNameList, maxIterations, convergenceThreshold); - - Assert.assertTrue(optimum.getIterations() < 20); - Field optimizerId = adjustmentContext.getClass().getDeclaredField("optimizerID"); - optimizerId.setAccessible(true); - OptimizerId usedOptimizerId = (OptimizerId) optimizerId.get(adjustmentContext); - if (usedOptimizerId == OptimizerId.GAUSS_NEWTON_QR || usedOptimizerId == OptimizerId.GAUSS_NEWTON_LU) { - // For Gauss Newton, the number of evaluations is equal to the number of iterations - Assert.assertTrue(optimum.getEvaluations() == optimum.getIterations()); - } else if (usedOptimizerId == OptimizerId.LEVENBERG_MARQUADT) { - // For Levenberg Marquadt, the number of evaluations is slightly greater than the number of iterations - Assert.assertTrue(optimum.getEvaluations() >= optimum.getIterations()); - } + List<String> ruggedNameList = new ArrayList<String>(); + for(Rugged rugged : ruggedList) { + ruggedNameList.add(rugged.getName()); + } + final int maxIterations = 120; + final double convergenceThreshold = 1.e-7; + + Optimum optimum = adjustmentContext.estimateFreeParameters(ruggedNameList, maxIterations, convergenceThreshold); + + Field optimizerId = adjustmentContext.getClass().getDeclaredField("optimizerID"); + optimizerId.setAccessible(true); + OptimizerId usedOptimizerId = (OptimizerId) optimizerId.get(adjustmentContext); + + if (usedOptimizerId == OptimizerId.GAUSS_NEWTON_QR || usedOptimizerId == OptimizerId.GAUSS_NEWTON_LU) { + // For Gauss Newton, the number of evaluations is equal to the number of iterations + Assert.assertTrue(optimum.getEvaluations() == optimum.getIterations()); + } else if (usedOptimizerId == OptimizerId.LEVENBERG_MARQUADT) { + // For Levenberg Marquadt, the number of evaluations is slightly greater than the number of iterations + Assert.assertTrue(optimum.getEvaluations() >= optimum.getIterations()); + } + + } // loop on OptimizerId + } + + @Test + public void testInvalidRuggedName() { + try { - final double expectedMaxValue = 3.324585e-03; - Assert.assertEquals(expectedMaxValue, optimum.getResiduals().getMaxValue(), 1.0e-6); + AdjustmentContext adjustmentContext = new AdjustmentContext(ruggedList, measurements); - final double expectedRMS = 0.069669; - Assert.assertEquals(expectedRMS, optimum.getRMS(), 1.0e-6); + List<String> ruggedNameList = new ArrayList<String>(); + // the list must not have a null value + Iterator<Rugged> it = ruggedList.iterator(); + while (it.hasNext()) { + ruggedNameList.add(null); + it.next(); + } + final int maxIterations = 1; + final double convergenceThreshold = 1.e-7; - System.out.format("Chi sqaure %3.8e %n", optimum.getChiSquare()); - - assertTrue(numberOfParameters == optimum.getPoint().getDimension()); + adjustmentContext.estimateFreeParameters(ruggedNameList, maxIterations, convergenceThreshold); + Assert.fail("An exception should have been thrown"); + + } catch (RuggedException re) { + Assert.assertEquals(RuggedMessages.INVALID_RUGGED_NAME,re.getSpecifier()); + } + } + + @Test + public void testUnsupportedRefiningContext() { + try { + + AdjustmentContext adjustmentContext = new AdjustmentContext(ruggedList, measurements); + + List<String> ruggedNameList = new ArrayList<String>(); + // Add too many rugged name: the list must have 1 or 2 items + for(Rugged rugged : ruggedList) { + ruggedNameList.add(rugged.getName()); + ruggedNameList.add(rugged.getName()); + ruggedNameList.add(rugged.getName()); + } + final int maxIterations = 1; + final double convergenceThreshold = 1.e-7; + + adjustmentContext.estimateFreeParameters(ruggedNameList, maxIterations, convergenceThreshold); + Assert.fail("An exception should have been thrown"); + + } catch (RuggedException re) { + Assert.assertEquals(RuggedMessages.UNSUPPORTED_REFINING_CONTEXT,re.getSpecifier()); + } + } + + @Test + public void testSensorMapping() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + + String sensorGiven = "lineSensor"; + String ruggedGiven = "Rugged"; + SensorMapping<SensorPixel> simpleMapping = new SensorMapping<SensorPixel>(sensorGiven) ; - System.out.format("residuals %d \n", optimum.getResiduals().getDimension()); -// int measureCount = 0; -// while (measurements.getInterMappings().iterator().hasNext()) { -// measureCount++; -// System.out.println("measure " + measureCount); -// } -// System.out.format(" measurements %d \n", measureCount); - System.out.format("cost %3.8e \n", optimum.getCost()); + Field ruggedField = simpleMapping.getClass().getDeclaredField("ruggedName"); + ruggedField.setAccessible(true); + String ruggedRead = (String) ruggedField.get(simpleMapping); + Field sensorField = simpleMapping.getClass().getDeclaredField("sensorName"); + sensorField.setAccessible(true); + String sensorRead = (String) sensorField.get(simpleMapping); - + Assert.assertTrue(ruggedGiven.equals(ruggedRead)); + Assert.assertTrue(sensorGiven.equals(sensorRead)); } - + @After public void tearDown() { measurements = null; @@ -142,6 +194,5 @@ public class AdjustmentContextTest { private Observables measurements; private List<Rugged> ruggedList; - private int numberOfParameters; } diff --git a/src/test/java/org/orekit/rugged/adjustment/GroundOptimizationProblemBuilderTest.java b/src/test/java/org/orekit/rugged/adjustment/GroundOptimizationProblemBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..00e50fab525e8d7ec374733a73a9089ce0f93c76 --- /dev/null +++ b/src/test/java/org/orekit/rugged/adjustment/GroundOptimizationProblemBuilderTest.java @@ -0,0 +1,204 @@ +package org.orekit.rugged.adjustment; + +import java.lang.reflect.Field; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer.Optimum; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import org.orekit.bodies.GeodeticPoint; +import org.orekit.errors.OrekitException; +import org.orekit.rugged.TestUtils; +import org.orekit.rugged.adjustment.measurements.Observables; +import org.orekit.rugged.adjustment.measurements.SensorToGroundMapping; +import org.orekit.rugged.adjustment.util.InitGroundRefiningTest; +import org.orekit.rugged.adjustment.util.RefiningParametersDriver; +import org.orekit.rugged.api.Rugged; +import org.orekit.rugged.errors.RuggedException; +import org.orekit.rugged.errors.RuggedMessages; +import org.orekit.rugged.linesensor.LineSensor; +import org.orekit.rugged.linesensor.SensorPixel; + +public class GroundOptimizationProblemBuilderTest { + + @Before + public void setUp() { + + try { + refiningTest = new InitGroundRefiningTest(); + refiningTest.initGroundRefiningTest(); + + rugged = refiningTest.getRugged(); + // get the only line sensor + LineSensor sensor = rugged.getLineSensors().iterator().next(); + sensorName = sensor.getName(); + + measurements = refiningTest.generateNoisyPoints(lineSampling, pixelSampling, false); + numberOfParameters = refiningTest.getParameterToAdjust(); + + } catch (RuggedException re) { + Assert.fail(re.getLocalizedMessage()); + } + } + + @Test + public void testEstimateFreeParameters() throws RuggedException { + + AdjustmentContext adjustmentContext = new AdjustmentContext(Collections.singletonList(rugged), measurements); + final int maxIterations = 50; + final double convergenceThreshold = 1.e-11; + Optimum optimum = adjustmentContext.estimateFreeParameters(Collections.singletonList(rugged.getName()), maxIterations, convergenceThreshold); + + Assert.assertTrue(optimum.getIterations() < maxIterations); + + // The default optimizer is a Gauss Newton. + // For Gauss Newton, the number of evaluations is equal to the number of iterations + Assert.assertTrue(optimum.getEvaluations() == optimum.getIterations()); + + final double expectedMaxValue = 39200.0; + Assert.assertEquals(expectedMaxValue, optimum.getResiduals().getMaxValue(), 1.0e-6); + + final double expectedRMS = 5067.112098; + Assert.assertEquals(expectedRMS, optimum.getRMS(), 1.0e-6); + + final double expectedCost = 286639.1460351976; + Assert.assertEquals(expectedCost, optimum.getCost(), 1.0e-6); + + Assert.assertTrue(numberOfParameters == optimum.getPoint().getDimension()); + + final int sensorToGroundMappingSize = 1600; + Collection<SensorToGroundMapping> ssm = measurements.getGroundMappings(); + Iterator<SensorToGroundMapping> it = ssm.iterator(); + while (it.hasNext()) { + SensorToGroundMapping ssmit = it.next(); + Assert.assertTrue(sensorToGroundMappingSize == ssmit.getMapping().size()); + } + Assert.assertTrue(sensorToGroundMappingSize*2 == optimum.getResiduals().getDimension()); + } + + @Test + public void testNoParametersSelected() throws RuggedException, OrekitException { + try { + RefiningParametersDriver.unselectRoll(rugged, sensorName); + RefiningParametersDriver.unselectPitch(rugged, sensorName); + + AdjustmentContext adjustmentContext = new AdjustmentContext(Collections.singletonList(rugged), measurements); + final int maxIterations = 50; + final double convergenceThreshold = 1.e-11; + + adjustmentContext.estimateFreeParameters(Collections.singletonList(rugged.getName()), maxIterations, convergenceThreshold); + Assert.fail("An exception should have been thrown"); + + } catch (RuggedException re) { + Assert.assertEquals(RuggedMessages.NO_PARAMETERS_SELECTED,re.getSpecifier()); + } + } + + @Test + public void testNoReferenceMapping() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + + try { + final int maxIterations = 50; + final double convergenceThreshold = 1.e-11; + + final List<LineSensor> selectedSensors = new ArrayList<LineSensor>(); + selectedSensors.addAll(rugged.getLineSensors()); + + final GroundOptimizationProblemBuilder groundOptimizationProblem = + new GroundOptimizationProblemBuilder(selectedSensors, measurements, rugged); + + Field sensorToGroundMapping = groundOptimizationProblem.getClass().getDeclaredField("sensorToGroundMappings"); + sensorToGroundMapping.setAccessible(true); + sensorToGroundMapping.set(groundOptimizationProblem,new ArrayList<SensorToGroundMapping>()); + + groundOptimizationProblem.build(maxIterations, convergenceThreshold); + Assert.fail("An exception should have been thrown"); + + } catch (RuggedException re) { + Assert.assertEquals(RuggedMessages.NO_REFERENCE_MAPPINGS,re.getSpecifier()); + } + } + + @Test + public void testDefaultRuggedName() throws RuggedException { + + // Get the measurements as computed in other tests + Collection<SensorToGroundMapping> sensorToGroundMapping = measurements.getGroundMappings(); + int nbModels = measurements.getNbModels(); + + // Recompute the measurements in another way ... but that must be the same after all + Observables measurementsNoName = refiningTest.generateNoisyPoints(lineSampling, pixelSampling, true); + Collection<SensorToGroundMapping> sensorToGroundMappingNoName = measurementsNoName.getGroundMappings(); + int nbModelsWithWeight = measurementsNoName.getNbModels(); + + // Compare the two collections of measurements + Assert.assertEquals(nbModels, nbModelsWithWeight); + + Assert.assertEquals(sensorToGroundMapping.size(), sensorToGroundMappingNoName.size()); + + // There is only one item + SensorToGroundMapping arraySensorToGroundMapping = (SensorToGroundMapping) sensorToGroundMapping.toArray()[0]; + SensorToGroundMapping arraySensorToGroundMappingNoName = (SensorToGroundMapping) sensorToGroundMappingNoName.toArray()[0]; + + + // Check if the two set are the same + Set<Entry<SensorPixel, GeodeticPoint>> mapping = arraySensorToGroundMapping.getMapping(); + Set<Entry<SensorPixel, GeodeticPoint>> mappingNoName = arraySensorToGroundMappingNoName.getMapping(); + + Iterator<Entry<SensorPixel, GeodeticPoint>> itMapping = mapping.iterator(); + while(itMapping.hasNext()) { + Entry<SensorPixel, GeodeticPoint> current = itMapping.next(); + SensorPixel key = current.getKey(); + GeodeticPoint value = current.getValue(); + + // Will search in mappingNoName if we can find the (key,value) found in mapping + Boolean found = false; + Iterator<Entry<SensorPixel, GeodeticPoint>> itMappingNoName = mappingNoName.iterator(); + while(itMappingNoName.hasNext()) { + Entry<SensorPixel, GeodeticPoint> currentNoName = itMappingNoName.next(); + SensorPixel keyNoName = currentNoName.getKey(); + GeodeticPoint valueNoName = currentNoName.getValue(); + + // Comparison of each SensorPixel (for the key part and the value part) + if (TestUtils.sameSensorPixels(key, keyNoName, 1.e-3) && + TestUtils.sameGeodeticPoints(value, valueNoName, 1.e-3)) { + // we found a match ... + found = true; + } + } // end iteration on mappingNoName + + if (!found) { // the current (key,value) of the mapping was not found in the mappingNoName + Assert.assertTrue(found); + } + } // end on iteration on mapping + + Assert.assertEquals(arraySensorToGroundMapping.getRuggedName(),arraySensorToGroundMappingNoName.getRuggedName()); + Assert.assertEquals(arraySensorToGroundMapping.getSensorName(),arraySensorToGroundMappingNoName.getSensorName());; + } + + + @After + public void tearDown() { + measurements = null; + } + + private InitGroundRefiningTest refiningTest; + private Observables measurements; + private Rugged rugged; + private String sensorName; + private int numberOfParameters; + + private final int lineSampling = 1000; + private final int pixelSampling = 1000; +} diff --git a/src/test/java/org/orekit/rugged/adjustment/InterSensorOptimizationProblemBuilderTest.java b/src/test/java/org/orekit/rugged/adjustment/InterSensorOptimizationProblemBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7b0b19bbbc918c85de11b293c74b741e51a265eb --- /dev/null +++ b/src/test/java/org/orekit/rugged/adjustment/InterSensorOptimizationProblemBuilderTest.java @@ -0,0 +1,351 @@ +/* Copyright 2013-2018 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.adjustment; + +import java.lang.reflect.Field; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer.Optimum; +import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import org.orekit.rugged.TestUtils; +import org.orekit.rugged.adjustment.measurements.Observables; +import org.orekit.rugged.adjustment.measurements.SensorToSensorMapping; +import org.orekit.rugged.adjustment.util.InitInterRefiningTest; +import org.orekit.rugged.api.Rugged; +import org.orekit.rugged.errors.RuggedException; +import org.orekit.rugged.errors.RuggedExceptionWrapper; +import org.orekit.rugged.errors.RuggedMessages; +import org.orekit.rugged.linesensor.LineSensor; +import org.orekit.rugged.linesensor.SensorPixel; + +public class InterSensorOptimizationProblemBuilderTest { + + @Before + public void setUp() { + + try { + refiningTest = new InitInterRefiningTest(); + refiningTest.initRefiningTest(); + + ruggedList = refiningTest.getRuggedList(); + + earthConstraintWeight = 0.1; + + measurements = refiningTest.generateNoisyPoints(lineSampling, pixelSampling, earthConstraintWeight, false); + numberOfParameters = refiningTest.getParameterToAdjust(); + + } catch (RuggedException re) { + Assert.fail(re.getLocalizedMessage()); + } + } + + @Test + public void testEstimateFreeParameters() throws RuggedException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + + AdjustmentContext adjustmentContext = new AdjustmentContext(ruggedList, measurements); + + List<String> ruggedNameList = new ArrayList<String>(); + for(Rugged rugged : ruggedList) { + ruggedNameList.add(rugged.getName()); + } + final int maxIterations = 100; + final double convergenceThreshold = 1.e-7; + + Optimum optimum = adjustmentContext.estimateFreeParameters(ruggedNameList, maxIterations, convergenceThreshold); + + Assert.assertTrue(optimum.getIterations() < maxIterations); + + // The default optimizer is a Gauss Newton. + // For Gauss Newton, the number of evaluations is equal to the number of iterations + Assert.assertTrue(optimum.getEvaluations() == optimum.getIterations()); + + final double expectedMaxValue = 1.924769e-03; + Assert.assertEquals(expectedMaxValue, optimum.getResiduals().getMaxValue(), 1.0e-6); + + final double expectedRMS = 0.069302; + Assert.assertEquals(expectedRMS, optimum.getRMS(), 1.0e-6); + + final double expectedCost = 3.597082e+00; + Assert.assertEquals(expectedCost, optimum.getCost(), 1.0e-6); + + Assert.assertTrue(numberOfParameters == optimum.getPoint().getDimension()); + + final int sensorToSensorMappingSize = 1347; + Collection<SensorToSensorMapping> ssm = measurements.getInterMappings(); + Iterator<SensorToSensorMapping> it = ssm.iterator(); + while (it.hasNext()) { + SensorToSensorMapping ssmit = it.next(); + Assert.assertTrue(sensorToSensorMappingSize == ssmit.getMapping().size()); + } + Assert.assertTrue(sensorToSensorMappingSize*2 == optimum.getResiduals().getDimension()); + + } + + @Test + public void testEarthConstraintPostponed() throws RuggedException { + + // Get the measurements as computed in other tests + Collection<SensorToSensorMapping> sensorToSensorMapping = measurements.getInterMappings(); + int nbModels = measurements.getNbModels(); + + // Recompute the measurements in another way ... but that must be the same after all + Observables measurementsPostponed = refiningTest.generateNoisyPoints(lineSampling, pixelSampling, earthConstraintWeight, true); + Collection<SensorToSensorMapping> sensorToSensorMappingPostponed = measurementsPostponed.getInterMappings(); + int nbModelsPostponed = measurementsPostponed.getNbModels(); + + // Compare the two collections of measurements + Assert.assertEquals(nbModels, nbModelsPostponed); + + Assert.assertEquals(sensorToSensorMapping.size(), sensorToSensorMappingPostponed.size()); + + // There is only one item + SensorToSensorMapping arraySensorToSensorMapping = (SensorToSensorMapping) sensorToSensorMapping.toArray()[0]; + SensorToSensorMapping arraySensorToSensorMappingPostponed = (SensorToSensorMapping) sensorToSensorMappingPostponed.toArray()[0]; + + List<Double> listBody = arraySensorToSensorMapping.getBodyDistances(); + + // Convert List<Double> to double[] + double[] arrayBody = listBody.stream().mapToDouble(Double::doubleValue).toArray(); //via method reference + // Explanations: + // get the Stream<Double> from the list + // map each double instance to its primitive value, resulting in a DoubleStream + // call toArray() to get the array. + // Other method: double[] arrayBody = listBody.stream().mapToDouble(d -> d).toArray(); //identity function, Java unboxes automatically to get the double value + + List<Double> listBodyPostponed = arraySensorToSensorMappingPostponed.getBodyDistances(); + double[] arrayBodyPostponed = listBodyPostponed.stream().mapToDouble(Double::doubleValue).toArray(); + + Assert.assertEquals(listBody.size(), listBodyPostponed.size()); + Assert.assertArrayEquals(arrayBody, arrayBodyPostponed, 1.e-3); + + List<Double> listLos = arraySensorToSensorMapping.getLosDistances(); + double[] arrayLos = listLos.stream().mapToDouble(Double::doubleValue).toArray(); + List<Double> listLosPostponed = arraySensorToSensorMappingPostponed.getLosDistances(); + double[] arrayLosPostponed = listLosPostponed.stream().mapToDouble(Double::doubleValue).toArray(); + + Assert.assertEquals(listLos.size(), listLosPostponed.size()); + Assert.assertArrayEquals(arrayLos, arrayLosPostponed, 1.e-6); + + // Check if the two set are the same + Set<Entry<SensorPixel, SensorPixel>> mapping = arraySensorToSensorMapping.getMapping(); + Set<Entry<SensorPixel, SensorPixel>> mappingPostponed = arraySensorToSensorMappingPostponed.getMapping(); + + Iterator<Entry<SensorPixel, SensorPixel>> itMapping = mapping.iterator(); + while(itMapping.hasNext()) { + Entry<SensorPixel, SensorPixel> current = itMapping.next(); + SensorPixel key = current.getKey(); + SensorPixel value = current.getValue(); + + // Will search in mappingPostponed if we can find the (key,value) found in mapping + Boolean found = false; + Iterator<Entry<SensorPixel, SensorPixel>> itMappingPost = mappingPostponed.iterator(); + while(itMappingPost.hasNext()) { + Entry<SensorPixel, SensorPixel> currentPost = itMappingPost.next(); + SensorPixel keyPost = currentPost.getKey(); + SensorPixel valuePost = currentPost.getValue(); + + // Comparison of each SensorPixel (for the key part and the value part) + if (TestUtils.sameSensorPixels(key, keyPost, 1.e-3) && + TestUtils.sameSensorPixels(value, valuePost, 1.e-3)) { + // we found a match ... + found = true; + } + } // end iteration on mappingPostponed + + if (!found) { // the current (key,value) of the mapping was not found in the mappingPostponed + Assert.assertTrue(found); + } + } // end on iteration on mapping + + Assert.assertEquals(arraySensorToSensorMapping.getRuggedNameA(),arraySensorToSensorMappingPostponed.getRuggedNameA()); + Assert.assertEquals(arraySensorToSensorMapping.getRuggedNameB(),arraySensorToSensorMappingPostponed.getRuggedNameB());; + Assert.assertEquals(arraySensorToSensorMapping.getSensorNameA(),arraySensorToSensorMappingPostponed.getSensorNameA());; + Assert.assertEquals(arraySensorToSensorMapping.getSensorNameB(),arraySensorToSensorMappingPostponed.getSensorNameB()); + } + + @Test + public void testDefaultRuggedNames() throws RuggedException { + + // In that case there are no body distance set at construction + + // Generate intermapping with simple constructor of SensorToSensorMapping with Earth Constraint Weight given at construction + Observables measurementsWithWeight = refiningTest.generateSimpleInterMapping(lineSampling, pixelSampling, earthConstraintWeight, false); + Collection<SensorToSensorMapping> sensorToSensorMappingWithWeight = measurementsWithWeight.getInterMappings(); + int nbModelsWithWeight = measurementsWithWeight.getNbModels(); + + // Generate intermapping with simple constructor of SensorToSensorMapping with Earth Constraint Weight given after construction + Observables measurementsWithoutWeight = refiningTest.generateSimpleInterMapping(lineSampling, pixelSampling, earthConstraintWeight, true); + Collection<SensorToSensorMapping> sensorToSensorMappingPostponed = measurementsWithoutWeight.getInterMappings(); + int nbModelsPostponed = measurementsWithoutWeight.getNbModels(); + + // Compare the two collections of measurements + Assert.assertEquals(nbModelsWithWeight, nbModelsPostponed); + + Assert.assertEquals(sensorToSensorMappingWithWeight.size(), sensorToSensorMappingPostponed.size()); + + // There is only one item + SensorToSensorMapping arraySensorToSensorMappingWithWeight = (SensorToSensorMapping) sensorToSensorMappingWithWeight.toArray()[0]; + SensorToSensorMapping arraySensorToSensorMappingPostponed = (SensorToSensorMapping) sensorToSensorMappingPostponed.toArray()[0]; + + + List<Double> listLosWithWeight = arraySensorToSensorMappingWithWeight.getLosDistances(); + double[] arrayLosWithWeight = listLosWithWeight.stream().mapToDouble(Double::doubleValue).toArray(); + List<Double> listLosPostponed = arraySensorToSensorMappingPostponed.getLosDistances(); + double[] arrayLosPostponed = listLosPostponed.stream().mapToDouble(Double::doubleValue).toArray(); + + Assert.assertEquals(listLosWithWeight.size(), listLosPostponed.size()); + Assert.assertArrayEquals(arrayLosWithWeight, arrayLosPostponed, 1.e-6); + + // Check if the two set are the same + Set<Entry<SensorPixel, SensorPixel>> mappingWithWeight = arraySensorToSensorMappingWithWeight.getMapping(); + Set<Entry<SensorPixel, SensorPixel>> mappingPostponed = arraySensorToSensorMappingPostponed.getMapping(); + + Iterator<Entry<SensorPixel, SensorPixel>> itMapping = mappingWithWeight.iterator(); + while(itMapping.hasNext()) { + Entry<SensorPixel, SensorPixel> current = itMapping.next(); + SensorPixel key = current.getKey(); + SensorPixel value = current.getValue(); + + // Will search in mappingPostponed if we can find the (key,value) found in mapping + Boolean found = false; + Iterator<Entry<SensorPixel, SensorPixel>> itMappingPost = mappingPostponed.iterator(); + while(itMappingPost.hasNext()) { + Entry<SensorPixel, SensorPixel> currentPost = itMappingPost.next(); + SensorPixel keyPost = currentPost.getKey(); + SensorPixel valuePost = currentPost.getValue(); + + // Comparison of each SensorPixel (for the key part and the value part) + if (TestUtils.sameSensorPixels(key, keyPost, 1.e-3) && + TestUtils.sameSensorPixels(value, valuePost, 1.e-3)) { + // we found a match ... + found = true; + } + } // end iteration on mappingPostponed + + if (!found) { // the current (key,value) of the mapping was not found in the mappingPostponed + Assert.assertTrue(found); + } + } // end on iteration on mapping + + Assert.assertEquals(arraySensorToSensorMappingWithWeight.getRuggedNameA(),arraySensorToSensorMappingPostponed.getRuggedNameA()); + Assert.assertEquals(arraySensorToSensorMappingWithWeight.getRuggedNameB(),arraySensorToSensorMappingPostponed.getRuggedNameB());; + Assert.assertEquals(arraySensorToSensorMappingWithWeight.getSensorNameA(),arraySensorToSensorMappingPostponed.getSensorNameA());; + Assert.assertEquals(arraySensorToSensorMappingWithWeight.getSensorNameB(),arraySensorToSensorMappingPostponed.getSensorNameB()); + } + + @Test + public void testNoReferenceMapping() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + + try { + final int maxIterations = 120; + final double convergenceThreshold = 1.e-7; + + final List<LineSensor> selectedSensors = new ArrayList<LineSensor>(); + for (Rugged rugged : ruggedList) { + selectedSensors.addAll(rugged.getLineSensors()); + } + final InterSensorsOptimizationProblemBuilder interSensorsOptimizationProblem = + new InterSensorsOptimizationProblemBuilder(selectedSensors, measurements, ruggedList); + + Field sensorToSensorMapping = interSensorsOptimizationProblem.getClass().getDeclaredField("sensorToSensorMappings"); + sensorToSensorMapping.setAccessible(true); + sensorToSensorMapping.set(interSensorsOptimizationProblem,new ArrayList<SensorToSensorMapping>()); + + interSensorsOptimizationProblem.build(maxIterations, convergenceThreshold); + Assert.fail("An exception should have been thrown"); + + } catch (RuggedException re) { + Assert.assertEquals(RuggedMessages.NO_REFERENCE_MAPPINGS,re.getSpecifier()); + } + } + + @Test + public void testInvalidRuggedNames() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, RuggedException { + + final int maxIterations = 120; + final double convergenceThreshold = 1.e-7; + + final List<LineSensor> selectedSensors = new ArrayList<LineSensor>(); + for (Rugged rugged : ruggedList) { + selectedSensors.addAll(rugged.getLineSensors()); + } + final InterSensorsOptimizationProblemBuilder interSensorsOptimizationProblem = + new InterSensorsOptimizationProblemBuilder(selectedSensors, measurements, ruggedList); + + Field ruggedMapField = interSensorsOptimizationProblem.getClass().getDeclaredField("ruggedMap"); + ruggedMapField.setAccessible(true); + LinkedHashMap<String,Rugged> ruggedMap = (LinkedHashMap<String,Rugged>) ruggedMapField.get(interSensorsOptimizationProblem); + + // Set first RuggedB to null to get the right exception ... + try { + ruggedMap.put("RuggedB",null); + + ruggedMapField.set(interSensorsOptimizationProblem,ruggedMap); + + final LeastSquareAdjuster adjuster = new LeastSquareAdjuster(OptimizerId.GAUSS_NEWTON_QR); + LeastSquaresProblem theProblem = interSensorsOptimizationProblem.build(maxIterations, convergenceThreshold); + adjuster.optimize(theProblem); + Assert.fail("An exception should have been thrown"); + + } catch (RuggedExceptionWrapper re) { + Assert.assertEquals(RuggedMessages.INVALID_RUGGED_NAME,re.getException().getSpecifier()); + } + + // Then set RuggedA to null to get the right exception ... + try { + ruggedMap.put("RuggedA",null); + + ruggedMapField.set(interSensorsOptimizationProblem,ruggedMap); + + final LeastSquareAdjuster adjuster = new LeastSquareAdjuster(OptimizerId.GAUSS_NEWTON_QR); + LeastSquaresProblem theProblem = interSensorsOptimizationProblem.build(maxIterations, convergenceThreshold); + adjuster.optimize(theProblem); + Assert.fail("An exception should have been thrown"); + + } catch (RuggedExceptionWrapper re) { + Assert.assertEquals(RuggedMessages.INVALID_RUGGED_NAME,re.getException().getSpecifier()); + } + } + + + @After + public void tearDown() { + measurements = null; + ruggedList = null; + } + + private InitInterRefiningTest refiningTest; + private Observables measurements; + private List<Rugged> ruggedList; + private int numberOfParameters; + private double earthConstraintWeight; + + private final int lineSampling = 1000; + private final int pixelSampling = 1000; +} diff --git a/src/test/java/org/orekit/rugged/adjustment/LeastSquareAdjusterTest.java b/src/test/java/org/orekit/rugged/adjustment/LeastSquareAdjusterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f2cc09745719868452727f75e799d8e8d7eda07b --- /dev/null +++ b/src/test/java/org/orekit/rugged/adjustment/LeastSquareAdjusterTest.java @@ -0,0 +1,27 @@ +package org.orekit.rugged.adjustment; + +import org.junit.Assert; + +import java.lang.reflect.Field; + +import org.junit.Test; + +public class LeastSquareAdjusterTest { + + @Test + public void testLeastSquareAdjuster() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + + final LeastSquareAdjuster adjusterWithOptimizer = new LeastSquareAdjuster(OptimizerId.GAUSS_NEWTON_QR); + final LeastSquareAdjuster adjusterWithDefaultOptimizer = new LeastSquareAdjuster(); + + Field optimizerIdWithOptimizer = adjusterWithOptimizer.getClass().getDeclaredField("optimizerID"); + optimizerIdWithOptimizer.setAccessible(true); + OptimizerId getOptimizerIdWithOptimizer = (OptimizerId) optimizerIdWithOptimizer.get(adjusterWithOptimizer); + + Field optimizerIdWithDefaultOptimizer = adjusterWithDefaultOptimizer.getClass().getDeclaredField("optimizerID"); + optimizerIdWithDefaultOptimizer.setAccessible(true); + OptimizerId getOptimizerIdWithDefaultOptimizer = (OptimizerId) optimizerIdWithDefaultOptimizer.get(adjusterWithDefaultOptimizer); + + Assert.assertTrue(getOptimizerIdWithDefaultOptimizer == getOptimizerIdWithOptimizer); + } +} diff --git a/src/test/java/org/orekit/rugged/adjustment/util/InitGroundRefiningTest.java b/src/test/java/org/orekit/rugged/adjustment/util/InitGroundRefiningTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4cdb8be5b29db937cb7fb7b9b247a13338c6ab65 --- /dev/null +++ b/src/test/java/org/orekit/rugged/adjustment/util/InitGroundRefiningTest.java @@ -0,0 +1,260 @@ +package org.orekit.rugged.adjustment.util; + +import java.io.File; +import java.net.URISyntaxException; +import java.util.List; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.random.GaussianRandomGenerator; +import org.hipparchus.random.UncorrelatedRandomVectorGenerator; +import org.hipparchus.random.Well19937a; +import org.hipparchus.util.FastMath; +import org.junit.Assert; +import org.orekit.bodies.BodyShape; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.data.DataProvidersManager; +import org.orekit.data.DirectoryCrawler; +import org.orekit.errors.OrekitException; +import org.orekit.orbits.Orbit; +import org.orekit.rugged.TestUtils; +import org.orekit.rugged.adjustment.measurements.Observables; +import org.orekit.rugged.adjustment.measurements.SensorToGroundMapping; +import org.orekit.rugged.api.AlgorithmId; +import org.orekit.rugged.api.BodyRotatingFrameId; +import org.orekit.rugged.api.EllipsoidId; +import org.orekit.rugged.api.InertialFrameId; +import org.orekit.rugged.api.Rugged; +import org.orekit.rugged.api.RuggedBuilder; +import org.orekit.rugged.errors.RuggedException; +import org.orekit.rugged.linesensor.LineSensor; +import org.orekit.rugged.linesensor.SensorPixel; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.AngularDerivativesFilter; +import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.Constants; +import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + + +/** Initialization for ground refining (Ground Control Points GCP study) Junit tests. + * @author Guylaine Prat + */ +public class InitGroundRefiningTest { + + /** Pleiades viewing model */ + private PleiadesViewingModel pleiadesViewingModel; + + /** Line sensor */ + private LineSensor lineSensor; + + /** Rugged's instance */ + private Rugged rugged; + + /** Number of parameters to adjust */ + private int parameterToAdjust; + + // Part of the name of parameter drivers + static final String rollSuffix = "_roll"; + static final String pitchSuffix = "_pitch"; + static final String factorName = "factor"; + + // Default values for disruption to apply to roll (deg), pitch (deg) and factor + static final double defaultRollDisruption = 0.004; + static final double defaultPitchDisruption = 0.0008; + static final double defaultFactorDisruption = 1.000000001; + + /** + * Initialize ground refining tests with default values for disruptions on sensors characteristics + * @throws RuggedException + */ + public void initGroundRefiningTest() throws RuggedException { + + initGroundRefiningTest(defaultRollDisruption, defaultPitchDisruption, defaultFactorDisruption); + } + + /** Initialize ground refining tests with disruption on sensors characteristics + * @param rollDisruption disruption to apply to roll angle for sensor (deg) + * @param pitchDisruption disruption to apply to pitch angle for sensor (deg) + * @param factorDisruption disruption to apply to homothety factor for sensor + * @throws RuggedException + */ + public void initGroundRefiningTest(double rollDisruption, double pitchDisruption, double factorDisruption) throws RuggedException { + try { + + String path = getClass().getClassLoader().getResource("orekit-data").toURI().getPath(); + DataProvidersManager.getInstance().addProvider(new DirectoryCrawler(new File(path))); + + // Initialize refining context + // --------------------------- + final String sensorName = "line"; + final double incidenceAngle = -5.0; + final String date = "2016-01-01T11:59:50.0"; + this.pleiadesViewingModel = new PleiadesViewingModel(sensorName, incidenceAngle, date); + + + PleiadesOrbitModel orbitmodel = new PleiadesOrbitModel(); + + // Sensor's definition: creation of Pleiades viewing model + // -------------------------------------------------------- + + // Create Pleiades Viewing Model + final AbsoluteDate minDate = pleiadesViewingModel.getMinDate(); + final AbsoluteDate maxDate = pleiadesViewingModel.getMaxDate(); + final AbsoluteDate refDate = pleiadesViewingModel.getDatationReference(); + this.lineSensor = pleiadesViewingModel.getLineSensor(); + + // ----Satellite position, velocity and attitude: create orbit model + BodyShape earth = TestUtils.createEarth(); + Orbit orbit = orbitmodel.createOrbit(Constants.EIGEN5C_EARTH_MU, refDate); + + // ----If no LOF Transform Attitude Provider is Nadir Pointing Yaw Compensation + final double [] rollPoly = {0.0,0.0,0.0}; + final double[] pitchPoly = {0.025,0.0}; + final double[] yawPoly = {0.0,0.0,0.0}; + orbitmodel.setLOFTransform(rollPoly, pitchPoly, yawPoly, minDate); + + // ----Satellite attitude + List<TimeStampedAngularCoordinates> satelliteQList = orbitmodel.orbitToQ(orbit, earth, minDate.shiftedBy(-0.0), maxDate.shiftedBy(+0.0), 0.25); + final int nbQPoints = 2; + + // ----Position and velocities + List<TimeStampedPVCoordinates> satellitePVListA = orbitmodel.orbitToPV(orbit, earth, minDate.shiftedBy(-0.0), maxDate.shiftedBy(+0.0), 0.25); + final int nbPVPoints = 8; + + // Rugged initialization + // --------------------- + RuggedBuilder ruggedBuilder = new RuggedBuilder(); + + ruggedBuilder.addLineSensor(lineSensor); + ruggedBuilder.setAlgorithm(AlgorithmId.IGNORE_DEM_USE_ELLIPSOID); + ruggedBuilder.setEllipsoid(EllipsoidId.WGS84, BodyRotatingFrameId.ITRF); + ruggedBuilder.setTimeSpan(minDate,maxDate, 0.001, 5.0); + ruggedBuilder.setTrajectory(InertialFrameId.EME2000, satellitePVListA, nbPVPoints, + CartesianDerivativesFilter.USE_PV, satelliteQList, + nbQPoints, AngularDerivativesFilter.USE_R); + ruggedBuilder.setLightTimeCorrection(false); + ruggedBuilder.setAberrationOfLightCorrection(false); + + ruggedBuilder.setName("Rugged"); + + this.rugged = ruggedBuilder.build(); + + + // Select parameters to adjust + // --------------------------- + RefiningParametersDriver.setSelectedRoll(rugged, sensorName); + RefiningParametersDriver.setSelectedPitch(rugged, sensorName); + + this.parameterToAdjust = 2; + + // Initialize disruptions: + // ----------------------- + // introduce rotations around instrument axes (roll and pitch angles, scale factor) + + // apply disruptions on physical model + RefiningParametersDriver.applyDisruptionsRoll(rugged, sensorName, FastMath.toRadians(rollDisruption)); + RefiningParametersDriver.applyDisruptionsPitch(rugged, sensorName, FastMath.toRadians(pitchDisruption)); + RefiningParametersDriver.applyDisruptionsFactor(rugged, sensorName, factorDisruption); + + + } catch (OrekitException oe) { + Assert.fail(oe.getLocalizedMessage()); + } catch (URISyntaxException use) { + Assert.fail(use.getLocalizedMessage()); + } + } + + /** + * Get the Rugged instance + * @return rugged instance + */ + public Rugged getRugged(){ + + return this.rugged; + } + + /** Generate noisy measurements (sensor to ground mapping) + * @param lineSampling line sampling + * @param pixelSampling pixel sampling + * @param flag to tell if the Rugged name used is the default one (true) or not (false) + * @throws RuggedException + */ + public Observables generateNoisyPoints(final int lineSampling, final int pixelSampling, boolean defaultRuggedName) throws RuggedException { + + // Generate reference mapping + SensorToGroundMapping groundMapping; + if (!defaultRuggedName) { + groundMapping = new SensorToGroundMapping(rugged.getName(), lineSensor.getName()); + } else { + // The rugged name used in this test is the same as the dafult one in SensorToGroundMapping + groundMapping = new SensorToGroundMapping(lineSensor.getName()); + } + + // Observable which contains sensor to ground mapping (one model) + Observables observable = new Observables(1); + + // Estimate latitude and longitude errors (rad) + final int pixelMiddle= lineSensor.getNbPixels() / 2; + final int lineMiddle = (int) FastMath.floor(pixelMiddle); + + final AbsoluteDate dateMiddle = lineSensor.getDate(lineMiddle); + final GeodeticPoint gp_pix0 = rugged.directLocation(dateMiddle, lineSensor.getPosition(), lineSensor.getLOS(dateMiddle, pixelMiddle)); + + final AbsoluteDate date1 = lineSensor.getDate(lineMiddle + 1); + final GeodeticPoint gp_pix1 = rugged.directLocation(date1, lineSensor.getPosition(), lineSensor.getLOS(date1, pixelMiddle + 1)); + + final double latitudeErr = FastMath.abs(gp_pix0.getLatitude() - gp_pix1.getLatitude()); + final double longitudeErr = FastMath.abs(gp_pix0.getLongitude() - gp_pix1.getLongitude()); + + // Generation noisy measurements + // distribution: gaussian(0), vector dimension: 3 (for latitude, longitude and altitude) + // Mean latitude, longitude and altitude + final double[] mean = {5.0,5.0,5.0}; + // Standard deviation latitude, longitude and altitude + final double[] std = {0.1,0.1,0.1}; + + final double latErrorMean = mean[0] * latitudeErr; + final double lonErrorMean = mean[1] * longitudeErr; + final double latErrorStd = std[0] * latitudeErr; + final double lonErrorStd = std[1] * longitudeErr; + + // Gaussian random generator + // Build a null mean random uncorrelated vector generator with standard deviation corresponding to the estimated error on ground + final double meanGenerator[] = {latErrorMean, lonErrorMean, mean[2]}; + final double stdGenerator[] = {latErrorStd, lonErrorStd, std[2]}; + + // seed has been fixed for tests purpose + final GaussianRandomGenerator rng = new GaussianRandomGenerator(new Well19937a(0xefac03d9be4d24b9l)); + final UncorrelatedRandomVectorGenerator rvg = new UncorrelatedRandomVectorGenerator(meanGenerator, stdGenerator, rng); + + for (double line = 0; line < pleiadesViewingModel.getDimension(); line += lineSampling) { + + final AbsoluteDate date = lineSensor.getDate(line); + for (int pixel = 0; pixel < lineSensor.getNbPixels(); pixel += pixelSampling) { + + // Components of generated vector follow (independent) Gaussian distribution + final Vector3D vecRandom = new Vector3D(rvg.nextVector()); + + final GeodeticPoint gp2 = rugged.directLocation(date, lineSensor.getPosition(), + lineSensor.getLOS(date, pixel)); + + final GeodeticPoint gpNoisy = new GeodeticPoint(gp2.getLatitude() + vecRandom.getX(), + gp2.getLongitude() + vecRandom.getY(), + gp2.getAltitude() + vecRandom.getZ()); + + groundMapping.addMapping(new SensorPixel(line, pixel), gpNoisy); + } + } + + observable.addGroundMapping(groundMapping); + return observable; + } + + /** Get the number of parameters to adjust + * @return number of parameters to adjust + */ + public int getParameterToAdjust() { + return parameterToAdjust; + } + +} diff --git a/src/test/java/org/orekit/rugged/utils/RefiningTest.java b/src/test/java/org/orekit/rugged/adjustment/util/InitInterRefiningTest.java similarity index 50% rename from src/test/java/org/orekit/rugged/utils/RefiningTest.java rename to src/test/java/org/orekit/rugged/adjustment/util/InitInterRefiningTest.java index 01269840918f7bf72ab8565ba9c56984aaf6e110..68bb5c96323d3e174ddcdc3561d96ee1b1ad4e09 100644 --- a/src/test/java/org/orekit/rugged/utils/RefiningTest.java +++ b/src/test/java/org/orekit/rugged/adjustment/util/InitInterRefiningTest.java @@ -1,4 +1,4 @@ -package org.orekit.rugged.utils; +package org.orekit.rugged.adjustment.util; import java.io.File; import java.lang.reflect.InvocationTargetException; @@ -7,55 +7,20 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.junit.After; -import org.junit.Assert; - import org.hipparchus.analysis.differentiation.DerivativeStructure; -import org.hipparchus.geometry.euclidean.threed.Rotation; -import org.hipparchus.geometry.euclidean.threed.RotationConvention; -import org.hipparchus.geometry.euclidean.threed.RotationOrder; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; import org.hipparchus.random.GaussianRandomGenerator; import org.hipparchus.random.UncorrelatedRandomVectorGenerator; import org.hipparchus.random.Well19937a; import org.hipparchus.util.FastMath; - -import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.LofOffset; -import org.orekit.attitudes.NadirPointing; -import org.orekit.attitudes.TabulatedLofOffset; -import org.orekit.attitudes.YawCompensation; +import org.junit.After; +import org.junit.Assert; import org.orekit.bodies.BodyShape; -import org.orekit.bodies.CelestialBodyFactory; import org.orekit.bodies.GeodeticPoint; import org.orekit.data.DataProvidersManager; import org.orekit.data.DirectoryCrawler; import org.orekit.errors.OrekitException; -import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel; -import org.orekit.forces.gravity.ThirdBodyAttraction; -import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider; -import org.orekit.frames.Frame; -import org.orekit.frames.FramesFactory; -import org.orekit.frames.LOFType; -import org.orekit.orbits.CircularOrbit; import org.orekit.orbits.Orbit; -import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngle; -import org.orekit.propagation.Propagator; -import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.analytical.KeplerianPropagator; -import org.orekit.propagation.numerical.NumericalPropagator; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.TimeScale; -import org.orekit.time.TimeScalesFactory; -import org.orekit.utils.AngularDerivativesFilter; -import org.orekit.utils.CartesianDerivativesFilter; -import org.orekit.utils.Constants; -import org.orekit.utils.ParameterDriver; -import org.orekit.utils.TimeStampedAngularCoordinates; -import org.orekit.utils.TimeStampedPVCoordinates; - import org.orekit.rugged.TestUtils; import org.orekit.rugged.adjustment.InterSensorsOptimizationProblemBuilder; import org.orekit.rugged.adjustment.measurements.Observables; @@ -67,20 +32,24 @@ import org.orekit.rugged.api.InertialFrameId; import org.orekit.rugged.api.Rugged; import org.orekit.rugged.api.RuggedBuilder; import org.orekit.rugged.errors.RuggedException; -import org.orekit.rugged.linesensor.LineDatation; import org.orekit.rugged.linesensor.LineSensor; -import org.orekit.rugged.linesensor.LinearLineDatation; import org.orekit.rugged.linesensor.SensorPixel; -import org.orekit.rugged.los.FixedRotation; -import org.orekit.rugged.los.FixedZHomothety; -import org.orekit.rugged.los.LOSBuilder; -import org.orekit.rugged.los.TimeDependentLOS; +import org.orekit.rugged.utils.DSGenerator; +import org.orekit.rugged.utils.SpacecraftToObservedBody; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.AngularDerivativesFilter; +import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.Constants; +import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; -/** Initialization for refining Junit tests. + + +/** Initialization for inter sensor refining Junit tests. * @author Guylaine Prat */ -public class RefiningTest { +public class InitInterRefiningTest { /** Pleiades viewing model A */ private PleiadesViewingModel pleiadesViewingModelA; @@ -112,7 +81,8 @@ public class RefiningTest { static final double defaultRollDisruptionA = 0.004; static final double defaultPitchDisruptionA = 0.0008; static final double defaultFactorDisruptionA = 1.000000001; - static final double defaultPitchDisruptionB = -0.0008; + static final double defaultRollDisruptionB = -0.004; + static final double defaultPitchDisruptionB = 0.0008; /** * Initialize refining tests with default values for disruptions on sensors characteristics @@ -120,17 +90,20 @@ public class RefiningTest { */ public void initRefiningTest() throws RuggedException { - initRefiningTest(defaultRollDisruptionA, defaultPitchDisruptionA, defaultFactorDisruptionA, defaultPitchDisruptionB); + initRefiningTest(defaultRollDisruptionA, defaultPitchDisruptionA, defaultFactorDisruptionA, + defaultRollDisruptionB, defaultPitchDisruptionB); } /** Initialize refining tests with disruption on sensors characteristics * @param rollDisruptionA disruption to apply to roll angle for sensor A (deg) * @param pitchDisruptionA disruption to apply to pitch angle for sensor A (deg) * @param factorDisruptionA disruption to apply to homothety factor for sensor A + * @param rollDisruptionB disruption to apply to roll angle for sensor B (deg) * @param pitchDisruptionB disruption to apply to pitch angle for sensor B (deg) * @throws RuggedException */ - public void initRefiningTest(double rollDisruptionA, double pitchDisruptionA, double factorDisruptionA, double pitchDisruptionB) throws RuggedException { + public void initRefiningTest(double rollDisruptionA, double pitchDisruptionA, double factorDisruptionA, + double rollDisruptionB, double pitchDisruptionB) throws RuggedException { try { String path = getClass().getClassLoader().getResource("orekit-data").toURI().getPath(); @@ -148,8 +121,8 @@ public class RefiningTest { final String dateB = "2016-01-01T12:02:50.0"; this.pleiadesViewingModelB = new PleiadesViewingModel(sensorNameB, incidenceAngleB, dateB); - OrbitModel orbitmodelA = new OrbitModel(); - OrbitModel orbitmodelB = new OrbitModel(); + PleiadesOrbitModel orbitmodelA = new PleiadesOrbitModel(); + PleiadesOrbitModel orbitmodelB = new PleiadesOrbitModel(); // Sensors's definition: creation of 2 Pleiades viewing models // ----------------------------------------------------------- @@ -234,11 +207,11 @@ public class RefiningTest { // Select parameters to adjust // --------------------------- - setSelectedRoll(ruggedA, sensorNameA); - setSelectedPitch(ruggedA, sensorNameA); + RefiningParametersDriver.setSelectedRoll(ruggedA, sensorNameA); + RefiningParametersDriver.setSelectedPitch(ruggedA, sensorNameA); - setSelectedRoll(ruggedB, sensorNameB); - setSelectedPitch(ruggedB, sensorNameB); + RefiningParametersDriver.setSelectedRoll(ruggedB, sensorNameB); + RefiningParametersDriver.setSelectedPitch(ruggedB, sensorNameB); this.parameterToAdjust = 4; @@ -247,12 +220,13 @@ public class RefiningTest { // introduce rotations around instrument axes (roll and pitch angles, scale factor) // apply disruptions on physical model (acquisition A) - applyDisruptionsRoll(ruggedA, sensorNameA, FastMath.toRadians(rollDisruptionA)); - applyDisruptionsPitch(ruggedA, sensorNameA, FastMath.toRadians(pitchDisruptionA)); - applyDisruptionsFactor(ruggedA, sensorNameA, factorDisruptionA); + RefiningParametersDriver.applyDisruptionsRoll(ruggedA, sensorNameA, FastMath.toRadians(rollDisruptionA)); + RefiningParametersDriver.applyDisruptionsPitch(ruggedA, sensorNameA, FastMath.toRadians(pitchDisruptionA)); + RefiningParametersDriver.applyDisruptionsFactor(ruggedA, sensorNameA, factorDisruptionA); // apply disruptions on physical model (acquisition B) - applyDisruptionsPitch(ruggedB, sensorNameB, FastMath.toRadians(pitchDisruptionB)); + RefiningParametersDriver.applyDisruptionsRoll(ruggedB, sensorNameB, FastMath.toRadians(rollDisruptionB)); + RefiningParametersDriver.applyDisruptionsPitch(ruggedB, sensorNameB, FastMath.toRadians(pitchDisruptionB)); } catch (OrekitException oe) { @@ -272,89 +246,6 @@ public class RefiningTest { return ruggedList; } - /** Apply disruptions on acquisition for roll angle - * @param rugged Rugged instance - * @param sensorName line sensor name - * @param rollValue rotation on roll value - */ - private void applyDisruptionsRoll(final Rugged rugged, final String sensorName, final double rollValue) - throws OrekitException, RuggedException { - - rugged. - getLineSensor(sensorName). - getParametersDrivers(). - filter(driver -> driver.getName().equals(sensorName + rollSuffix)). - findFirst().get().setValue(rollValue); - } - - /** Apply disruptions on acquisition for pitch angle - * @param rugged Rugged instance - * @param sensorName line sensor name - * @param pitchValue rotation on pitch value - */ - private void applyDisruptionsPitch(final Rugged rugged, final String sensorName, final double pitchValue) - throws OrekitException, RuggedException { - - rugged. - getLineSensor(sensorName). - getParametersDrivers(). - filter(driver -> driver.getName().equals(sensorName + pitchSuffix)). - findFirst().get().setValue(pitchValue); - } - - /** Apply disruptions on acquisition for scale factor - * @param rugged Rugged instance - * @param sensorName line sensor name - * @param factorValue scale factor - */ - private void applyDisruptionsFactor(final Rugged rugged, final String sensorName, final double factorValue) - throws OrekitException, RuggedException { - - rugged. - getLineSensor(sensorName). - getParametersDrivers(). - filter(driver -> driver.getName().equals(factorName)). - findFirst().get().setValue(factorValue); - } - - /** Select roll angle to adjust - * @param rugged Rugged instance - * @param sensorName line sensor name - * @throws OrekitException, RuggedException - */ - private void setSelectedRoll(final Rugged rugged, final String sensorName) throws OrekitException, RuggedException { - - ParameterDriver rollDriver = - rugged.getLineSensor(sensorName).getParametersDrivers(). - filter(driver -> driver.getName().equals(sensorName + rollSuffix)).findFirst().get(); - rollDriver.setSelected(true); - } - - /** Select pitch angle to adjust - * @param rugged Rugged instance - * @param sensorName line sensor name - * @throws OrekitException, RuggedException - */ - private void setSelectedPitch(final Rugged rugged, final String sensorName) throws OrekitException, RuggedException { - - ParameterDriver pitchDriver = - rugged.getLineSensor(sensorName).getParametersDrivers(). - filter(driver -> driver.getName().equals(sensorName + pitchSuffix)).findFirst().get(); - pitchDriver.setSelected(true); - } - - /** Select scale factor to adjust - * @param rugged Rugged instance - * @param sensorName line sensor name - * @throws OrekitException, RuggedException - */ - private void setSelectedFactor(final Rugged rugged, final String sensorName) throws OrekitException, RuggedException { - - ParameterDriver factorDriver = - rugged.getLineSensor(sensorName).getParametersDrivers(). - filter(driver -> driver.getName().equals(factorName)).findFirst().get(); - factorDriver.setSelected(true); - } /** Compute the distances between LOS of two real pixels (one from sensor A and one from sensor B) * @param realPixelA real pixel from sensor A @@ -434,29 +325,33 @@ public class RefiningTest { /** Generate noisy measurements (sensor to sensor mapping) * @param lineSampling line sampling * @param pixelSampling pixel sampling + * @param earthConstraintWeight the earth constrint weight + * @param earthConstraintPostponed flag to tell if the earth constraint weight is set at construction (false) or after (true) - For JUnit coverage purpose * @throws RuggedException */ - public Observables generateNoisyPoints(final int lineSampling, final int pixelSampling) throws RuggedException { + public Observables generateNoisyPoints(final int lineSampling, final int pixelSampling, final double earthConstraintWeight, final boolean earthConstraintPostponed) throws RuggedException { // Outliers control final double outlierValue = 1.e+2; - // Earth constraint weight - final double earthConstraintWeight = 0.1; - - // Number of measurements - int measurementCount = 0; - // Generate measurements with constraints on Earth distance and outliers control // Generate reference mapping, with Earth distance constraints. // Weighting will be applied as follow : // (1-earthConstraintWeight) for losDistance weighting // earthConstraintWeight for earthDistance weighting - SensorToSensorMapping interMapping = new SensorToSensorMapping(lineSensorA.getName(), ruggedA.getName(), - lineSensorB.getName(), ruggedB.getName(), - earthConstraintWeight); - + SensorToSensorMapping interMapping; + if (! earthConstraintPostponed) { + interMapping = new SensorToSensorMapping(lineSensorA.getName(), ruggedA.getName(), + lineSensorB.getName(), ruggedB.getName(), + earthConstraintWeight); + } else { // used for JUnit coverage purpose + interMapping = new SensorToSensorMapping(lineSensorA.getName(), ruggedA.getName(), + lineSensorB.getName(), ruggedB.getName()); + // set the earthConstraintWeight after construction + interMapping.setBodyConstraintWeight(earthConstraintWeight); + } + // Observables which contains sensor to sensor mapping Observables observables = new Observables(2); @@ -526,9 +421,6 @@ public class RefiningTest { interMapping.addMapping(realPixelA, realPixelB, losDistance, earthDistance); - // Increment the number of measurements - measurementCount++; - } // end test if geoDistance < outlierValue } // end test if sensorPixelB != null @@ -540,377 +432,124 @@ public class RefiningTest { } - /** Compute a geodetic distance in meters between two geodetic points. - * @param geoPoint1 first geodetic point (rad) - * @param geoPoint2 second geodetic point (rad) - * @return distance in meters - */ - private static double computeDistanceInMeter(final GeodeticPoint geoPoint1, final GeodeticPoint geoPoint2) { - - // get vectors on unit sphere from angular coordinates - final Vector3D p1 = new Vector3D(geoPoint1.getLatitude(), geoPoint1.getLongitude()); - final Vector3D p2 = new Vector3D(geoPoint2.getLatitude(), geoPoint2.getLongitude()); - return Constants.WGS84_EARTH_EQUATORIAL_RADIUS * Vector3D.angle(p1, p2); - } - - - @After - public void tearDown() { - } - - /** Get the number of parameters to adjust - * @return number of parameters to adjust - */ - public int getParameterToAdjust() { - return parameterToAdjust; - } -} - - - -/** - * Class to compute orbit for refining tests - */ -class OrbitModel { - - /** Flag to change Attitude Law (by default Nadir Pointing Yaw Compensation). */ - private boolean userDefinedLOFTransform; - - /** User defined Roll law. */ - private double[] lofTransformRollPoly; - - /** User defined Pitch law. */ - private double[] lofTransformPitchPoly; - - /** User defined Yaw law. */ - private double[] lofTransformYawPoly; - - /** Reference date. */ - private AbsoluteDate refDate; - - - - /** Default constructor. + /** Generate simple noisy measurements (sensor to sensor mapping) + * @param lineSampling line sampling + * @param pixelSampling pixel sampling + * @param earthConstraintWeight the earth constrint weight + * @param earthConstraintPostponed flag to tell if the earth constraint weight is set at construction (false) or after (true) - For JUnit coverage purpose + * @throws RuggedException */ - public OrbitModel() { - userDefinedLOFTransform = false; - } + public Observables generateSimpleInterMapping(final int lineSampling, final int pixelSampling, final double earthConstraintWeight, final boolean earthConstraintPostponed) throws RuggedException { - /** Create a circular orbit. - */ - public Orbit createOrbit(final double mu, final AbsoluteDate date) throws OrekitException { + // Outliers control + final double outlierValue = 1.e+2; - // the following orbital parameters have been computed using - // Orekit tutorial about phasing, using the following configuration: - // - // orbit.date = 2012-01-01T00:00:00.000 - // phasing.orbits.number = 143 - // phasing.days.number = 10 - // sun.synchronous.reference.latitude = 0 - // sun.synchronous.reference.ascending = false - // sun.synchronous.mean.solar.time = 10:30:00 - // gravity.field.degree = 12 - // gravity.field.order = 12 - - final Frame eme2000 = FramesFactory.getEME2000(); - return new CircularOrbit(694000.0 + Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - -4.029194321683225E-4, - 0.0013530362644647786, - FastMath.toRadians(98.2), // Pleiades inclination 98.2 deg - FastMath.toRadians(-86.47 + 180), - FastMath.toRadians(135.9 + 0.3), - PositionAngle.TRUE, - eme2000, - date, - mu); - } - - /** Set the Local Orbital Frame characteristics. - */ - public void setLOFTransform(final double[] rollPoly, final double[] pitchPoly, - final double[] yawPoly, final AbsoluteDate date) { - - this.userDefinedLOFTransform = true; - lofTransformRollPoly = rollPoly.clone(); - lofTransformPitchPoly = pitchPoly.clone(); - lofTransformYawPoly = yawPoly.clone(); - this.refDate = date; - } - - /** Recompute the polynom coefficients with shift. - */ - private double getPoly(final double[] poly, final double shift) { + // Generate measurements with constraints on Earth distance and outliers control - double val = 0.0; - for (int coef = 0; coef < poly.length; coef++) { - val = val + poly[coef] * FastMath.pow(shift, coef); + // Generate reference mapping, with Earth distance constraints. + // Weighting will be applied as follow : + // (1-earthConstraintWeight) for losDistance weighting + // earthConstraintWeight for earthDistance weighting + SensorToSensorMapping interMapping; + if (! earthConstraintPostponed) { + interMapping = new SensorToSensorMapping(lineSensorA.getName(), lineSensorB.getName(), earthConstraintWeight); + } else { // used for JUnit coverage purpose + interMapping = new SensorToSensorMapping(lineSensorA.getName(), lineSensorB.getName()); + // set the earthConstraintWeight after construction + interMapping.setBodyConstraintWeight(earthConstraintWeight); } - return val; - } - /** Get the offset. - */ - private Rotation getOffset(final BodyShape earth, final Orbit orbit, final double shift) - throws OrekitException { + // Observables which contains sensor to sensor mapping + Observables observables = new Observables(2); - final LOFType type = LOFType.VVLH; - final double roll = getPoly(lofTransformRollPoly, shift); - final double pitch = getPoly(lofTransformPitchPoly, shift); - final double yaw = getPoly(lofTransformYawPoly, shift); - - final LofOffset law = new LofOffset(orbit.getFrame(), type, RotationOrder.XYZ, - roll, pitch, yaw); - final Rotation offsetAtt = law. - getAttitude(orbit, orbit.getDate().shiftedBy(shift), orbit.getFrame()). - getRotation(); - final NadirPointing nadirPointing = new NadirPointing(orbit.getFrame(), earth); - final Rotation nadirAtt = nadirPointing. - getAttitude(orbit, orbit.getDate().getDate().shiftedBy(shift), orbit.getFrame()). - getRotation(); - final Rotation offsetProper = offsetAtt.compose(nadirAtt.revert(), RotationConvention.VECTOR_OPERATOR); - - return offsetProper; - } - - /** Create the attitude provider. - */ - public AttitudeProvider createAttitudeProvider(final BodyShape earth, final Orbit orbit) - throws OrekitException { - - if (userDefinedLOFTransform) { - final LOFType type = LOFType.VVLH; - - final List<TimeStampedAngularCoordinates> list = new ArrayList<TimeStampedAngularCoordinates>(); - - for (double shift = -10.0; shift < +10.0; shift += 1e-2) { - list.add(new TimeStampedAngularCoordinates(refDate.shiftedBy(shift), - getOffset(earth, orbit, shift), - Vector3D.ZERO, - Vector3D.ZERO)); - } - - final TabulatedLofOffset tabulated = new TabulatedLofOffset(orbit.getFrame(), type, list, - 2, AngularDerivativesFilter.USE_R); + // Generation noisy measurements + // distribution: gaussian(0), vector dimension: 2 + final double meanA[] = { 5.0, 5.0 }; + final double stdA[] = { 0.1, 0.1 }; + final double meanB[] = { 0.0, 0.0 }; + final double stdB[] = { 0.1, 0.1 }; - return tabulated; - } else { - return new YawCompensation(orbit.getFrame(), new NadirPointing(orbit.getFrame(), earth)); - } - } + // Seed has been fixed for tests purpose + final GaussianRandomGenerator rngA = new GaussianRandomGenerator(new Well19937a(0xefac03d9be4d24b9l)); + final UncorrelatedRandomVectorGenerator rvgA = new UncorrelatedRandomVectorGenerator(meanA, stdA, rngA); - /** Create the orbit propagator. - */ - public Propagator createPropagator(final BodyShape earth, - final NormalizedSphericalHarmonicsProvider gravityField, - final Orbit orbit) - throws OrekitException { - - final AttitudeProvider attitudeProvider = createAttitudeProvider(earth, orbit); - - final SpacecraftState state = - new SpacecraftState(orbit, - attitudeProvider.getAttitude(orbit, orbit.getDate(), orbit.getFrame()), 1180.0); - - // numerical model for improving orbit - final OrbitType type = OrbitType.CIRCULAR; - final double[][] tolerances = NumericalPropagator.tolerances(0.1, orbit, type); - final DormandPrince853Integrator integrator = - new DormandPrince853Integrator(1.0e-4 * orbit.getKeplerianPeriod(), - 1.0e-1 * orbit.getKeplerianPeriod(), - tolerances[0], - tolerances[1]); - integrator.setInitialStepSize(1.0e-2 * orbit.getKeplerianPeriod()); - - final NumericalPropagator numericalPropagator = new NumericalPropagator(integrator); - numericalPropagator.addForceModel(new HolmesFeatherstoneAttractionModel(earth.getBodyFrame(), gravityField)); - numericalPropagator .addForceModel(new ThirdBodyAttraction(CelestialBodyFactory.getSun())); - numericalPropagator .addForceModel(new ThirdBodyAttraction(CelestialBodyFactory.getMoon())); - numericalPropagator.setOrbitType(type); - numericalPropagator.setInitialState(state); - numericalPropagator.setAttitudeProvider(attitudeProvider); - - return numericalPropagator; - } + // Seed has been fixed for tests purpose + final GaussianRandomGenerator rngB = new GaussianRandomGenerator(new Well19937a(0xdf1c03d9be0b34b9l)); + final UncorrelatedRandomVectorGenerator rvgB = new UncorrelatedRandomVectorGenerator(meanB, stdB, rngB); - /** Generate the orbit. - */ - public List<TimeStampedPVCoordinates> orbitToPV(final Orbit orbit, final BodyShape earth, - final AbsoluteDate minDate, final AbsoluteDate maxDate, - final double step) - throws OrekitException { - final Propagator propagator = new KeplerianPropagator(orbit); - - propagator.setAttitudeProvider(createAttitudeProvider(earth, orbit)); - - propagator.propagate(minDate); - final List<TimeStampedPVCoordinates> list = new ArrayList<TimeStampedPVCoordinates>(); - propagator.setMasterMode(step, - (currentState, isLast) -> - list.add(new TimeStampedPVCoordinates(currentState.getDate(), - currentState.getPVCoordinates().getPosition(), - currentState.getPVCoordinates().getVelocity(), - Vector3D.ZERO))); - propagator.propagate(maxDate); - - return list; - } - - /** Generate the attitude. - */ - public List<TimeStampedAngularCoordinates> orbitToQ(final Orbit orbit, final BodyShape earth, - final AbsoluteDate minDate, final AbsoluteDate maxDate, - final double step) - throws OrekitException { + // Search the sensor pixel seeing point + final int minLine = 0; + final int maxLine = pleiadesViewingModelB.getDimension() - 1; - final Propagator propagator = new KeplerianPropagator(orbit); - propagator.setAttitudeProvider(createAttitudeProvider(earth, orbit)); - propagator.propagate(minDate); - final List<TimeStampedAngularCoordinates> list = new ArrayList<>(); - propagator.setMasterMode(step, - (currentState, isLast) -> - list.add(new TimeStampedAngularCoordinates(currentState.getDate(), - currentState.getAttitude().getRotation(), - Vector3D.ZERO, - Vector3D.ZERO))); - propagator.propagate(maxDate); - - return list; - } -} // end class OrbitModel - - -/** - * Pleiades viewing model class definition. - */ -class PleiadesViewingModel { - - /** Pleiades parameters. */ - private static final double FOV = 1.65; // 20km - alt 694km - private static final int DIMENSION = 40000; - private static final double LINE_PERIOD = 1.e-4; - - private double incidenceAngle; - private LineSensor lineSensor; - private String referenceDate; - private String sensorName; - - - /** Simple constructor. - * <p> - * initialize PleiadesViewingModel with - * sensorName="line", incidenceAngle = 0.0, date = "2016-01-01T12:00:00.0" - * </p> - */ - public PleiadesViewingModel(final String sensorName) throws RuggedException, OrekitException { - - this(sensorName, 0.0, "2016-01-01T12:00:00.0"); - } - - /** PleiadesViewingModel constructor. - * @param sensorName sensor name - * @param incidenceAngle incidence angle - * @param referenceDate reference date - */ - public PleiadesViewingModel(final String sensorName, final double incidenceAngle, final String referenceDate) - throws RuggedException, OrekitException { - - this.sensorName = sensorName; - this.referenceDate = referenceDate; - this.incidenceAngle = incidenceAngle; - this.createLineSensor(); - } - - /** Create raw fixed Line Of sight - */ - public LOSBuilder rawLOS(final Vector3D center, final Vector3D normal, final double halfAperture, final int n) { - - final List<Vector3D> list = new ArrayList<Vector3D>(n); - for (int i = 0; i < n; ++i) { - final double alpha = (halfAperture * (2 * i + 1 - n)) / (n - 1); - list.add(new Rotation(normal, alpha, RotationConvention.VECTOR_OPERATOR).applyTo(center)); - } - - return new LOSBuilder(list); - } - - /** Build a LOS provider - */ - public TimeDependentLOS buildLOS() { - - final LOSBuilder losBuilder = rawLOS(new Rotation(Vector3D.PLUS_I, - FastMath.toRadians(incidenceAngle), - RotationConvention.VECTOR_OPERATOR).applyTo(Vector3D.PLUS_K), - Vector3D.PLUS_I, FastMath.toRadians(FOV / 2), DIMENSION); - - losBuilder.addTransform(new FixedRotation(sensorName + RefiningTest.rollSuffix, Vector3D.MINUS_I, 0.00)); - losBuilder.addTransform(new FixedRotation(sensorName + RefiningTest.pitchSuffix, Vector3D.MINUS_J, 0.00)); + final String sensorNameB = lineSensorB.getName(); - // factor is a common parameters shared between all Pleiades models - losBuilder.addTransform(new FixedZHomothety(RefiningTest.factorName, 1.0)); + for (double line = 0; line < pleiadesViewingModelA.getDimension(); line += lineSampling) { - return losBuilder.build(); - } + final AbsoluteDate dateA = lineSensorA.getDate(line); + + for (double pixelA = 0; pixelA < lineSensorA.getNbPixels(); pixelA += pixelSampling) { + + final GeodeticPoint gpA = ruggedA.directLocation(dateA, lineSensorA.getPosition(), + lineSensorA.getLOS(dateA, pixelA)); + final SensorPixel sensorPixelB = ruggedB.inverseLocation(sensorNameB, gpA, minLine, maxLine); + // We need to test if the sensor pixel is found in the prescribed lines + // otherwise the sensor pixel is null + if (sensorPixelB != null) { + + final AbsoluteDate dateB = lineSensorB.getDate(sensorPixelB.getLineNumber()); + final double pixelB = sensorPixelB.getPixelNumber(); - /** Get the reference date. - */ - public AbsoluteDate getDatationReference() throws OrekitException { + final GeodeticPoint gpB = ruggedB.directLocation(dateB, lineSensorB.getPosition(), + lineSensorB.getLOS(dateB, pixelB)); + final double geoDistance = computeDistanceInMeter(gpA, gpB); + + // Create the inter mapping if distance is below outlier value + if (geoDistance < outlierValue) { - // We use Orekit for handling time and dates, and Rugged for defining the datation model: - final TimeScale utc = TimeScalesFactory.getUTC(); + final double[] vecRandomA = rvgA.nextVector(); + final double[] vecRandomB = rvgB.nextVector(); - return new AbsoluteDate(referenceDate, utc); - } + final SensorPixel realPixelA = new SensorPixel(line + vecRandomA[0], pixelA + vecRandomA[1]); + final SensorPixel realPixelB = new SensorPixel(sensorPixelB.getLineNumber() + vecRandomB[0], + sensorPixelB.getPixelNumber() + vecRandomB[1]); + // dummy value for JUnit test purpose + final Double losDistance = FastMath.abs(vecRandomA[0]); - /** Get the min date. - */ - public AbsoluteDate getMinDate() throws RuggedException { - return lineSensor.getDate(0); - } + interMapping.addMapping(realPixelA, realPixelB, losDistance); - /** Get the max date. - */ - public AbsoluteDate getMaxDate() throws RuggedException { - return lineSensor.getDate(DIMENSION); - } + } // end test if geoDistance < outlierValue + } // end test if sensorPixelB != null + + } // end loop on pixel of sensorA + } // end loop on line of sensorA - /** Get the line sensor. - */ - public LineSensor getLineSensor() { - return lineSensor; + observables.addInterMapping(interMapping); + return observables; } - - /** Get the sensor name. + + /** Compute a geodetic distance in meters between two geodetic points. + * @param geoPoint1 first geodetic point (rad) + * @param geoPoint2 second geodetic point (rad) + * @return distance in meters */ - public String getSensorName() { - return sensorName; - } + private static double computeDistanceInMeter(final GeodeticPoint geoPoint1, final GeodeticPoint geoPoint2) { - /** Get the number of lines of the sensor. - * @return the number of lines of the sensor - */ - public int getDimension() { - return DIMENSION; + // get vectors on unit sphere from angular coordinates + final Vector3D p1 = new Vector3D(geoPoint1.getLatitude(), geoPoint1.getLongitude()); + final Vector3D p2 = new Vector3D(geoPoint2.getLatitude(), geoPoint2.getLongitude()); + return Constants.WGS84_EARTH_EQUATORIAL_RADIUS * Vector3D.angle(p1, p2); } - /** Create the line sensor. + /** Get the number of parameters to adjust + * @return number of parameters to adjust */ - private void createLineSensor() throws RuggedException, OrekitException { - - // Offset of the MSI from center of mass of satellite - // one line sensor - // los: swath in the (YZ) plane, looking at 50° roll, 2.6" per pixel - final Vector3D msiOffset = new Vector3D(0, 0, 0); - - final TimeDependentLOS lineOfSight = buildLOS(); - - final double rate = 1. / LINE_PERIOD; - // linear datation model: at reference time we get the middle line, and the rate is one line every 1.5ms - - final LineDatation lineDatation = new LinearLineDatation(getDatationReference(), DIMENSION / 2, rate); - - lineSensor = new LineSensor(sensorName, lineDatation, msiOffset, lineOfSight); + public int getParameterToAdjust() { + return parameterToAdjust; } -} // end class PleiadesViewingModel + @After + public void tearDown() { + } +} diff --git a/src/test/java/org/orekit/rugged/adjustment/util/PleiadesOrbitModel.java b/src/test/java/org/orekit/rugged/adjustment/util/PleiadesOrbitModel.java new file mode 100644 index 0000000000000000000000000000000000000000..bd7ef95ece8207c352b86271f0ca17c713a43020 --- /dev/null +++ b/src/test/java/org/orekit/rugged/adjustment/util/PleiadesOrbitModel.java @@ -0,0 +1,205 @@ +package org.orekit.rugged.adjustment.util; + +import java.util.ArrayList; +import java.util.List; + +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.RotationOrder; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.LofOffset; +import org.orekit.attitudes.NadirPointing; +import org.orekit.attitudes.TabulatedLofOffset; +import org.orekit.attitudes.YawCompensation; +import org.orekit.bodies.BodyShape; +import org.orekit.errors.OrekitException; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.frames.LOFType; +import org.orekit.orbits.CircularOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngle; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.AngularDerivativesFilter; +import org.orekit.utils.Constants; +import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** + * Class to compute Pleiades orbit for refining Junit tests. + */ +public class PleiadesOrbitModel { + + /** Flag to change Attitude Law (by default Nadir Pointing Yaw Compensation). */ + private boolean userDefinedLOFTransform; + + /** User defined Roll law. */ + private double[] lofTransformRollPoly; + + /** User defined Pitch law. */ + private double[] lofTransformPitchPoly; + + /** User defined Yaw law. */ + private double[] lofTransformYawPoly; + + /** Reference date. */ + private AbsoluteDate refDate; + + /** Default constructor. + */ + public PleiadesOrbitModel() { + userDefinedLOFTransform = false; + } + + /** Create a circular orbit. + */ + public Orbit createOrbit(final double mu, final AbsoluteDate date) throws OrekitException { + + // the following orbital parameters have been computed using + // Orekit tutorial about phasing, using the following configuration: + // + // orbit.date = 2012-01-01T00:00:00.000 + // phasing.orbits.number = 143 + // phasing.days.number = 10 + // sun.synchronous.reference.latitude = 0 + // sun.synchronous.reference.ascending = false + // sun.synchronous.mean.solar.time = 10:30:00 + // gravity.field.degree = 12 + // gravity.field.order = 12 + + final Frame eme2000 = FramesFactory.getEME2000(); + return new CircularOrbit(694000.0 + Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + -4.029194321683225E-4, + 0.0013530362644647786, + FastMath.toRadians(98.2), // Pleiades inclination 98.2 deg + FastMath.toRadians(-86.47 + 180), + FastMath.toRadians(135.9 + 0.3), + PositionAngle.TRUE, + eme2000, + date, + mu); + } + + /** Set the Local Orbital Frame characteristics. + */ + public void setLOFTransform(final double[] rollPoly, final double[] pitchPoly, + final double[] yawPoly, final AbsoluteDate date) { + + this.userDefinedLOFTransform = true; + lofTransformRollPoly = rollPoly.clone(); + lofTransformPitchPoly = pitchPoly.clone(); + lofTransformYawPoly = yawPoly.clone(); + this.refDate = date; + } + + /** Recompute the polynom coefficients with shift. + */ + private double getPoly(final double[] poly, final double shift) { + + double val = 0.0; + for (int coef = 0; coef < poly.length; coef++) { + val = val + poly[coef] * FastMath.pow(shift, coef); + } + return val; + } + + /** Get the offset. + */ + private Rotation getOffset(final BodyShape earth, final Orbit orbit, final double shift) + throws OrekitException { + + final LOFType type = LOFType.VVLH; + final double roll = getPoly(lofTransformRollPoly, shift); + final double pitch = getPoly(lofTransformPitchPoly, shift); + final double yaw = getPoly(lofTransformYawPoly, shift); + + final LofOffset law = new LofOffset(orbit.getFrame(), type, RotationOrder.XYZ, + roll, pitch, yaw); + final Rotation offsetAtt = law. + getAttitude(orbit, orbit.getDate().shiftedBy(shift), orbit.getFrame()). + getRotation(); + final NadirPointing nadirPointing = new NadirPointing(orbit.getFrame(), earth); + final Rotation nadirAtt = nadirPointing. + getAttitude(orbit, orbit.getDate().getDate().shiftedBy(shift), orbit.getFrame()). + getRotation(); + final Rotation offsetProper = offsetAtt.compose(nadirAtt.revert(), RotationConvention.VECTOR_OPERATOR); + + return offsetProper; + } + + /** Create the attitude provider. + */ + public AttitudeProvider createAttitudeProvider(final BodyShape earth, final Orbit orbit) + throws OrekitException { + + if (userDefinedLOFTransform) { + final LOFType type = LOFType.VVLH; + + final List<TimeStampedAngularCoordinates> list = new ArrayList<TimeStampedAngularCoordinates>(); + + for (double shift = -10.0; shift < +10.0; shift += 1e-2) { + list.add(new TimeStampedAngularCoordinates(refDate.shiftedBy(shift), + getOffset(earth, orbit, shift), + Vector3D.ZERO, + Vector3D.ZERO)); + } + + final TabulatedLofOffset tabulated = new TabulatedLofOffset(orbit.getFrame(), type, list, + 2, AngularDerivativesFilter.USE_R); + + return tabulated; + } else { + return new YawCompensation(orbit.getFrame(), new NadirPointing(orbit.getFrame(), earth)); + } + } + + /** Generate the orbit. + */ + public List<TimeStampedPVCoordinates> orbitToPV(final Orbit orbit, final BodyShape earth, + final AbsoluteDate minDate, final AbsoluteDate maxDate, + final double step) + throws OrekitException { + + final Propagator propagator = new KeplerianPropagator(orbit); + + propagator.setAttitudeProvider(createAttitudeProvider(earth, orbit)); + + propagator.propagate(minDate); + final List<TimeStampedPVCoordinates> list = new ArrayList<TimeStampedPVCoordinates>(); + propagator.setMasterMode(step, + (currentState, isLast) -> + list.add(new TimeStampedPVCoordinates(currentState.getDate(), + currentState.getPVCoordinates().getPosition(), + currentState.getPVCoordinates().getVelocity(), + Vector3D.ZERO))); + propagator.propagate(maxDate); + + return list; + } + + /** Generate the attitude. + */ + public List<TimeStampedAngularCoordinates> orbitToQ(final Orbit orbit, final BodyShape earth, + final AbsoluteDate minDate, final AbsoluteDate maxDate, + final double step) + throws OrekitException { + + final Propagator propagator = new KeplerianPropagator(orbit); + propagator.setAttitudeProvider(createAttitudeProvider(earth, orbit)); + propagator.propagate(minDate); + final List<TimeStampedAngularCoordinates> list = new ArrayList<>(); + propagator.setMasterMode(step, + (currentState, isLast) -> + list.add(new TimeStampedAngularCoordinates(currentState.getDate(), + currentState.getAttitude().getRotation(), + Vector3D.ZERO, + Vector3D.ZERO))); + propagator.propagate(maxDate); + + return list; + } +} diff --git a/src/test/java/org/orekit/rugged/adjustment/util/PleiadesViewingModel.java b/src/test/java/org/orekit/rugged/adjustment/util/PleiadesViewingModel.java new file mode 100644 index 0000000000000000000000000000000000000000..244258a3ba9d3e2ad322f255080c84e5e807b90f --- /dev/null +++ b/src/test/java/org/orekit/rugged/adjustment/util/PleiadesViewingModel.java @@ -0,0 +1,145 @@ +package org.orekit.rugged.adjustment.util; + +import java.util.ArrayList; +import java.util.List; + +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.orekit.rugged.errors.RuggedException; +import org.orekit.rugged.linesensor.LineDatation; +import org.orekit.rugged.linesensor.LineSensor; +import org.orekit.rugged.linesensor.LinearLineDatation; +import org.orekit.rugged.los.FixedRotation; +import org.orekit.rugged.los.FixedZHomothety; +import org.orekit.rugged.los.LOSBuilder; +import org.orekit.rugged.los.TimeDependentLOS; + +import org.orekit.errors.OrekitException; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScalesFactory; + +/** + * Pleiades viewing model for refining Junit tests. + */ +public class PleiadesViewingModel { + + /** Pleiades parameters. */ + private static final double FOV = 1.65; // 20km - alt 694km + private static final int DIMENSION = 40000; + private static final double LINE_PERIOD = 1.e-4; + + private double incidenceAngle; + private LineSensor lineSensor; + private String referenceDate; + private String sensorName; + + /** PleiadesViewingModel constructor. + * @param sensorName sensor name + * @param incidenceAngle incidence angle + * @param referenceDate reference date + */ + public PleiadesViewingModel(final String sensorName, final double incidenceAngle, final String referenceDate) + throws RuggedException, OrekitException { + + this.sensorName = sensorName; + this.referenceDate = referenceDate; + this.incidenceAngle = incidenceAngle; + this.createLineSensor(); + } + + /** Create raw fixed Line Of sight + */ + public LOSBuilder rawLOS(final Vector3D center, final Vector3D normal, final double halfAperture, final int n) { + + final List<Vector3D> list = new ArrayList<Vector3D>(n); + for (int i = 0; i < n; ++i) { + final double alpha = (halfAperture * (2 * i + 1 - n)) / (n - 1); + list.add(new Rotation(normal, alpha, RotationConvention.VECTOR_OPERATOR).applyTo(center)); + } + + return new LOSBuilder(list); + } + + /** Build a LOS provider + */ + public TimeDependentLOS buildLOS() { + + final LOSBuilder losBuilder = rawLOS(new Rotation(Vector3D.PLUS_I, + FastMath.toRadians(incidenceAngle), + RotationConvention.VECTOR_OPERATOR).applyTo(Vector3D.PLUS_K), + Vector3D.PLUS_I, FastMath.toRadians(FOV / 2), DIMENSION); + + losBuilder.addTransform(new FixedRotation(sensorName + InitInterRefiningTest.rollSuffix, Vector3D.MINUS_I, 0.00)); + losBuilder.addTransform(new FixedRotation(sensorName + InitInterRefiningTest.pitchSuffix, Vector3D.MINUS_J, 0.00)); + + // factor is a common parameters shared between all Pleiades models + losBuilder.addTransform(new FixedZHomothety(InitInterRefiningTest.factorName, 1.0)); + + return losBuilder.build(); + } + + /** Get the reference date. + */ + public AbsoluteDate getDatationReference() throws OrekitException { + + // We use Orekit for handling time and dates, and Rugged for defining the datation model: + final TimeScale utc = TimeScalesFactory.getUTC(); + + return new AbsoluteDate(referenceDate, utc); + } + + /** Get the min date. + */ + public AbsoluteDate getMinDate() throws RuggedException { + return lineSensor.getDate(0); + } + + /** Get the max date. + */ + public AbsoluteDate getMaxDate() throws RuggedException { + return lineSensor.getDate(DIMENSION); + } + + /** Get the line sensor. + */ + public LineSensor getLineSensor() { + return lineSensor; + } + + /** Get the sensor name. + */ + public String getSensorName() { + return sensorName; + } + + /** Get the number of lines of the sensor. + * @return the number of lines of the sensor + */ + public int getDimension() { + return DIMENSION; + } + + /** Create the line sensor. + */ + private void createLineSensor() throws RuggedException, OrekitException { + + // Offset of the MSI from center of mass of satellite + // one line sensor + // los: swath in the (YZ) plane, looking at 50° roll, 2.6" per pixel + final Vector3D msiOffset = new Vector3D(0, 0, 0); + + final TimeDependentLOS lineOfSight = buildLOS(); + + final double rate = 1. / LINE_PERIOD; + // linear datation model: at reference time we get the middle line, and the rate is one line every 1.5ms + + final LineDatation lineDatation = new LinearLineDatation(getDatationReference(), DIMENSION / 2, rate); + + lineSensor = new LineSensor(sensorName, lineDatation, msiOffset, lineOfSight); + } + +} + diff --git a/src/test/java/org/orekit/rugged/adjustment/util/RefiningParametersDriver.java b/src/test/java/org/orekit/rugged/adjustment/util/RefiningParametersDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..dae8fb87e9c5cb323aff626bccfadc0a72a56678 --- /dev/null +++ b/src/test/java/org/orekit/rugged/adjustment/util/RefiningParametersDriver.java @@ -0,0 +1,142 @@ +package org.orekit.rugged.adjustment.util; + +import org.orekit.errors.OrekitException; +import org.orekit.rugged.api.Rugged; +import org.orekit.rugged.errors.RuggedException; +import org.orekit.utils.ParameterDriver; + + +/** Apply disruptions or select/unselect parameter to adjust for Refining JUnit tests. + * @author Guylaine Prat + */ +public class RefiningParametersDriver { + + // Part of the name of parameter drivers + static final String rollSuffix = "_roll"; + static final String pitchSuffix = "_pitch"; + static final String factorName = "factor"; + + /** Apply disruptions on acquisition for roll angle + * @param rugged Rugged instance + * @param sensorName line sensor name + * @param rollValue rotation on roll value + */ + public static void applyDisruptionsRoll(final Rugged rugged, final String sensorName, final double rollValue) + throws OrekitException, RuggedException { + + rugged. + getLineSensor(sensorName). + getParametersDrivers(). + filter(driver -> driver.getName().equals(sensorName + rollSuffix)). + findFirst().get().setValue(rollValue); + } + + /** Apply disruptions on acquisition for pitch angle + * @param rugged Rugged instance + * @param sensorName line sensor name + * @param pitchValue rotation on pitch value + */ + public static void applyDisruptionsPitch(final Rugged rugged, final String sensorName, final double pitchValue) + throws OrekitException, RuggedException { + + rugged. + getLineSensor(sensorName). + getParametersDrivers(). + filter(driver -> driver.getName().equals(sensorName + pitchSuffix)). + findFirst().get().setValue(pitchValue); + } + + /** Apply disruptions on acquisition for scale factor + * @param rugged Rugged instance + * @param sensorName line sensor name + * @param factorValue scale factor + */ + public static void applyDisruptionsFactor(final Rugged rugged, final String sensorName, final double factorValue) + throws OrekitException, RuggedException { + + rugged. + getLineSensor(sensorName). + getParametersDrivers(). + filter(driver -> driver.getName().equals(factorName)). + findFirst().get().setValue(factorValue); + } + + /** Select roll angle to adjust + * @param rugged Rugged instance + * @param sensorName line sensor name + * @throws OrekitException, RuggedException + */ + public static void setSelectedRoll(final Rugged rugged, final String sensorName) throws OrekitException, RuggedException { + + ParameterDriver rollDriver = + rugged.getLineSensor(sensorName).getParametersDrivers(). + filter(driver -> driver.getName().equals(sensorName + rollSuffix)).findFirst().get(); + rollDriver.setSelected(true); + } + + /** Select pitch angle to adjust + * @param rugged Rugged instance + * @param sensorName line sensor name + * @throws OrekitException, RuggedException + */ + public static void setSelectedPitch(final Rugged rugged, final String sensorName) throws OrekitException, RuggedException { + + ParameterDriver pitchDriver = + rugged.getLineSensor(sensorName).getParametersDrivers(). + filter(driver -> driver.getName().equals(sensorName + pitchSuffix)).findFirst().get(); + pitchDriver.setSelected(true); + } + + /** Select scale factor to adjust + * @param rugged Rugged instance + * @param sensorName line sensor name + * @throws OrekitException, RuggedException + */ + public static void setSelectedFactor(final Rugged rugged, final String sensorName) throws OrekitException, RuggedException { + + ParameterDriver factorDriver = + rugged.getLineSensor(sensorName).getParametersDrivers(). + filter(driver -> driver.getName().equals(factorName)).findFirst().get(); + factorDriver.setSelected(true); + } + + /** Unselect roll angle to adjust (for test coverage purpose) + * @param rugged Rugged instance + * @param sensorName line sensor name + * @throws OrekitException, RuggedException + */ + public static void unselectRoll(final Rugged rugged, final String sensorName) throws OrekitException, RuggedException { + + ParameterDriver rollDriver = + rugged.getLineSensor(sensorName).getParametersDrivers(). + filter(driver -> driver.getName().equals(sensorName + rollSuffix)).findFirst().get(); + rollDriver.setSelected(false); + } + + /** Unselect pitch angle to adjust (for test coverage purpose) + * @param rugged Rugged instance + * @param sensorName line sensor name + * @throws OrekitException, RuggedException + */ + public static void unselectPitch(final Rugged rugged, final String sensorName) throws OrekitException, RuggedException { + + ParameterDriver pitchDriver = + rugged.getLineSensor(sensorName).getParametersDrivers(). + filter(driver -> driver.getName().equals(sensorName + pitchSuffix)).findFirst().get(); + pitchDriver.setSelected(false); + } + + /** Unselect factor angle to adjust (for test coverage purpose) + * @param rugged Rugged instance + * @param sensorName line sensor name + * @throws OrekitException, RuggedException + */ + public static void unselectFactor(final Rugged rugged, final String sensorName) throws OrekitException, RuggedException { + + ParameterDriver factorDriver = + rugged.getLineSensor(sensorName).getParametersDrivers(). + filter(driver -> driver.getName().equals(factorName)).findFirst().get(); + factorDriver.setSelected(false); + } + +} diff --git a/src/test/java/org/orekit/rugged/api/RuggedTest.java b/src/test/java/org/orekit/rugged/api/RuggedTest.java index 930c8659fe8a69d22e9aebbe3d47f6941ec71e97..67ded92a05850a76115328ad2a8165da3d736dda 100644 --- a/src/test/java/org/orekit/rugged/api/RuggedTest.java +++ b/src/test/java/org/orekit/rugged/api/RuggedTest.java @@ -73,8 +73,8 @@ import org.orekit.rugged.raster.RandomLandscapeUpdater; import org.orekit.rugged.raster.TileUpdater; import org.orekit.rugged.raster.VolcanicConeElevationUpdater; import org.orekit.rugged.adjustment.measurements.Observables; +import org.orekit.rugged.adjustment.util.InitInterRefiningTest; import org.orekit.rugged.utils.DSGenerator; -import org.orekit.rugged.utils.RefiningTest; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; @@ -1376,7 +1376,7 @@ public class RuggedTest { @Test public void testDistanceBetweenLOS() throws RuggedException { - RefiningTest refiningTest = new RefiningTest(); + InitInterRefiningTest refiningTest = new InitInterRefiningTest(); refiningTest.initRefiningTest(); final SensorPixel realPixelA = new SensorPixel(2005.015883575199, 18004.968656395424); @@ -1384,8 +1384,8 @@ public class RuggedTest { double[] distancesBetweenLOS = refiningTest.computeDistancesBetweenLOS(realPixelA, realPixelB); - double expectedDistanceBetweenLOS = 1.43205763; - double expectedDistanceToTheGround = 6367488.049257; + double expectedDistanceBetweenLOS = 3.88800245; + double expectedDistanceToTheGround = 6368020.559109; Assert.assertEquals(expectedDistanceBetweenLOS, distancesBetweenLOS[0], 1.e-8); Assert.assertEquals(expectedDistanceToTheGround, distancesBetweenLOS[1], 1.e-5); @@ -1394,21 +1394,21 @@ public class RuggedTest { @Test public void testDistanceBetweenLOSDerivatives() throws RuggedException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - RefiningTest refiningTest = new RefiningTest(); + InitInterRefiningTest refiningTest = new InitInterRefiningTest(); refiningTest.initRefiningTest(); final SensorPixel realPixelA = new SensorPixel(2005.015883575199, 18004.968656395424); final SensorPixel realPixelB = new SensorPixel(4798.487736488162, 13952.2195710654); // Expected distances between LOS and to the ground - double expectedDistanceBetweenLOS = 1.43205763; - double expectedDistanceToTheGround = 6367488.049257; + double expectedDistanceBetweenLOS = 3.88800245; + double expectedDistanceToTheGround = 6368020.559109; // Expected derivatives for // minimum distance between LOS - double[] expectedDminDerivatives = {1.43205763, 153938.24840674, 679398.19955421, -191388.31413817, -669127.13904528} ; + double[] expectedDminDerivatives = {3.88800245, -153874.01319097, -678866.03112033, 191294.06938169, 668600.16715270} ; // minimum distance to the ground - double[] expectedDcentralBodyDerivatives = {6367488.04924976, 7018753.80043417, -1578385.34826076, -6850071.440488495, 1958372.41322515}; + double[] expectedDcentralBodyDerivatives = {6368020.55910153, 7007767.46926062, -1577060.82402054, -6839286.39593802, 1956452.66636262}; DerivativeStructure[] distancesBetweenLOSwithDS = refiningTest.computeDistancesBetweenLOSDerivatives(realPixelA, realPixelB, expectedDistanceBetweenLOS, expectedDistanceToTheGround); diff --git a/src/test/java/org/orekit/rugged/intersection/ConstantElevationAlgorithmTest.java b/src/test/java/org/orekit/rugged/intersection/ConstantElevationAlgorithmTest.java index 976d44f739829029c83a152396a00158e7733b42..f810f6be94f05c44f9b659dfe49ac201d82e9fb0 100644 --- a/src/test/java/org/orekit/rugged/intersection/ConstantElevationAlgorithmTest.java +++ b/src/test/java/org/orekit/rugged/intersection/ConstantElevationAlgorithmTest.java @@ -94,6 +94,9 @@ public class ConstantElevationAlgorithmTest { 2 * FastMath.PI)); Assert.assertEquals(2 * FastMath.PI + gpConst.getLongitude(), shifted.getLongitude(), 1.0e-6); + // Simple test for test coverage purpose + double elevation0 = ignore.getElevation(gpRef.getLatitude(), gpConst.getLatitude()); + Assert.assertEquals(elevation0, 0.0, 1.e-15); } @Before diff --git a/src/test/java/org/orekit/rugged/utils/EnumeratesTest.java b/src/test/java/org/orekit/rugged/utils/EnumeratesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d74d19c5156372405f3cd212da9c45ae51b1eeb2 --- /dev/null +++ b/src/test/java/org/orekit/rugged/utils/EnumeratesTest.java @@ -0,0 +1,41 @@ +package org.orekit.rugged.utils; + +import org.junit.Test; +import org.orekit.rugged.adjustment.OptimizerId; +import org.orekit.rugged.api.BodyRotatingFrameId; +import org.orekit.rugged.api.EllipsoidId; +import org.orekit.rugged.api.InertialFrameId; + +/** Tests to obtain 100% of coverage for enum class with jacoco tool. + * Even though one use each enumerate of an enum class, + * there is no way to obtain a full 100% coverage of the class + * unless ... to perform a simple test like + * TheEnumClass.valueOf(TheEnumClass.OneEnumValue..toString()); + * @author Guylaine Prat + */ +public class EnumeratesTest { + + @Test + public void testEnumOptimizerId() { + // Test to have 100% coverage of enum class + OptimizerId.valueOf(OptimizerId.LEVENBERG_MARQUADT.toString()); + } + + @Test + public void testBodyRotatingFrameId() { + // Test to have 100% coverage of enum class + BodyRotatingFrameId.valueOf(BodyRotatingFrameId.ITRF.toString()); + } + + @Test + public void testEllipsoidId() { + // Test to have 100% coverage of enum class + EllipsoidId.valueOf(EllipsoidId.IERS2003.toString()); + } + + @Test + public void testInertialFrameId() { + // Test to have 100% coverage of enum class + InertialFrameId.valueOf(InertialFrameId.GCRF.toString()); + } +} diff --git a/src/test/java/org/orekit/rugged/utils/RefiningTiePointsMetrics.java b/src/test/java/org/orekit/rugged/utils/RefiningTiePointsMetrics.java deleted file mode 100644 index 5e3f3038f5da30437d0190e83400a3cd0c568579..0000000000000000000000000000000000000000 --- a/src/test/java/org/orekit/rugged/utils/RefiningTiePointsMetrics.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.orekit.rugged.utils; - -/** - * Contains results from tie metrics computation - */ -public class RefiningTiePointsMetrics { - - /** Maximum residual distance. */ - private double resMax; - /** Mean residual distance. */ - private double resMean; - /** LOS distance max. */ - private double losDistanceMax; - /** LOS distance mean. */ - private double losDistanceMean; - /** Earth distance max. */ - private double earthDistanceMax; - /** Earth distance mean.*/ - private double earthDistanceMean; - - public RefiningTiePointsMetrics() { - - this.resMax = 0.0; - this.resMean = 0.0; - this.losDistanceMax = 0.0; - this.losDistanceMean = 0.0; - this.earthDistanceMax = 0.0; - this.earthDistanceMean = 0.0; - } - - public double getMaxResidual() { - return resMax; - } - - public void setMaxResidual(double resMax) { - this.resMax = resMax; - } - - public double getMeanResidual() { - return resMean; - } - - public void setMeanResidual(double resMean) { - this.resMean = resMean; - } - - public double getLosMaxDistance() { - return losDistanceMax; - } - - public void setLosMaxDistance(double losDistanceMax) { - this.losDistanceMax = losDistanceMax; - } - - public double getLosMeanDistance() { - return losDistanceMean; - } - - public void setLosMeanDistance(double losDistanceMean) { - this.losDistanceMean = losDistanceMean; - } - - public double getEarthMaxDistance() { - return earthDistanceMax; - } - - public void setEarthMaxDistance(double earthDistanceMax) { - this.earthDistanceMax = earthDistanceMax; - } - - public double getEarthMeanDistance() { - return earthDistanceMean; - } - - public void setEarthMeanDistance(double earthDistanceMean) { - this.earthDistanceMean = earthDistanceMean; - } -}