Commit c9259ae0 authored by Luc Maisonobe's avatar Luc Maisonobe
Browse files

Added on-board antenna phase center effect on range measurements.

parent 91fee5fe
/* Copyright 2002-2017 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.estimation.measurements.modifiers;
import java.util.Collections;
import java.util.List;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.estimation.measurements.EstimatedMeasurement;
import org.orekit.estimation.measurements.EstimationModifier;
import org.orekit.estimation.measurements.Range;
import org.orekit.propagation.SpacecraftState;
import org.orekit.utils.ParameterDriver;
import org.orekit.utils.TimeStampedPVCoordinates;
/** On-board antenna offset effect on range measurements.
* @author Luc Maisonobe
* @since 9.0
*/
public class OnBoardAntennaRangeModifier implements EstimationModifier<Range> {
/** Position of the Antenna Phase Center in satellite frame. */
private final Vector3D antennaPhaseCenter;
/** Simple constructor.
* @param antennaPhaseCenter position of the Antenna Phase Center in satellite frame
*/
public OnBoardAntennaRangeModifier(final Vector3D antennaPhaseCenter) {
this.antennaPhaseCenter = antennaPhaseCenter;
}
/** {@inheritDoc} */
@Override
public List<ParameterDriver> getParametersDrivers() {
return Collections.emptyList();
}
/** {@inheritDoc} */
@Override
public void modify(final EstimatedMeasurement<Range> estimated) {
// the participants are ground station at emission, spacecraft, ground station at reception
final TimeStampedPVCoordinates[] participants = estimated.getParticipants();
final Vector3D pEmission = participants[0].getPosition();
final Vector3D pSpaceraft = participants[1].getPosition();
final Vector3D pReception = participants[2].getPosition();
// compute the geometrical value of the range directly from participants positions.
// Note that this may be different from the value returned by estimated.getEstimatedValue(),
// because other modifiers may already have been taken into account
final double rangeUsingSpacecraftCenter =
0.5 * (Vector3D.distance(pEmission, pSpaceraft) + Vector3D.distance(pSpaceraft, pReception));
// compute the geometrical value of the range replacing
// the spacecraft position with antenna phase center position
// position of the antenna phase center in inertial frame
final int index = estimated.getObservedMeasurement().getPropagatorsIndices().get(0);
final SpacecraftState state = estimated.getStates()[index];
final Vector3D pAPC = state.toTransform().getInverse().transformPosition(antennaPhaseCenter);
final double rangeUsingAntennaPhaseCenter =
0.5 * (Vector3D.distance(pEmission, pAPC) + Vector3D.distance(pAPC, pReception));
// get the estimated value before this modifier is applied
final double[] value = estimated.getEstimatedValue();
// modify the value
value[0] += rangeUsingAntennaPhaseCenter - rangeUsingSpacecraftCenter;
estimated.setEstimatedValue(value);
}
}
......@@ -21,6 +21,9 @@
</properties>
<body>
<release version="9.0" date="TBD" description="TBD">
<action dev="luc" type="add">
Added on-board antenna phase center effect on range measurements.
</action>
<action dev="luc" type="update">
Moved Bias and OutlierFilter classes together with the other estimation modifiers.
</action>
......
......@@ -25,6 +25,7 @@ import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem.Ev
import org.hipparchus.optim.nonlinear.vector.leastsquares.LevenbergMarquardtOptimizer;
import org.junit.Assert;
import org.junit.Test;
import org.orekit.attitudes.LofOffset;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.estimation.Context;
......@@ -33,8 +34,11 @@ import org.orekit.estimation.measurements.EstimationsProvider;
import org.orekit.estimation.measurements.InterSatellitesRangeMeasurementCreator;
import org.orekit.estimation.measurements.ObservedMeasurement;
import org.orekit.estimation.measurements.PVMeasurementCreator;
import org.orekit.estimation.measurements.Range;
import org.orekit.estimation.measurements.RangeMeasurementCreator;
import org.orekit.estimation.measurements.RangeRateMeasurementCreator;
import org.orekit.estimation.measurements.modifiers.OnBoardAntennaRangeModifier;
import org.orekit.frames.LOFType;
import org.orekit.orbits.CartesianOrbit;
import org.orekit.orbits.KeplerianOrbit;
import org.orekit.orbits.Orbit;
......@@ -178,6 +182,102 @@ public class BatchLSEstimatorTest {
}
@Test
public void testKeplerRangeWithOnBoardAntennaOffset() throws OrekitException {
Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
final NumericalPropagatorBuilder propagatorBuilder =
context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
1.0e-6, 60.0, 1.0);
propagatorBuilder.setAttitudeProvider(new LofOffset(propagatorBuilder.getFrame(), LOFType.LVLH));
final Vector3D antennaPhaseCenter = new Vector3D(-1.2, 2.3, -0.7);
// create perfect range measurements with antenna offset
final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit,
propagatorBuilder);
final List<ObservedMeasurement<?>> measurements =
EstimationTestUtils.createMeasurements(propagator,
new RangeMeasurementCreator(context, antennaPhaseCenter),
1.0, 3.0, 300.0);
// create orbit estimator
final BatchLSEstimator estimator = new BatchLSEstimator(new LevenbergMarquardtOptimizer(),
propagatorBuilder);
final OnBoardAntennaRangeModifier obaModifier = new OnBoardAntennaRangeModifier(antennaPhaseCenter);
for (final ObservedMeasurement<?> range : measurements) {
((Range) range).addModifier(obaModifier);
estimator.addMeasurement(range);
}
estimator.setParametersConvergenceThreshold(1.0e-2);
estimator.setMaxIterations(10);
estimator.setMaxEvaluations(20);
estimator.setObserver(new BatchLSObserver() {
int lastIter = 0;
int lastEval = 0;
/** {@inheritDoc} */
@Override
public void evaluationPerformed(int iterationsCount, int evaluationscount,
Orbit[] orbits,
ParameterDriversList estimatedOrbitalParameters,
ParameterDriversList estimatedPropagatorParameters,
ParameterDriversList estimatedMeasurementsParameters,
EstimationsProvider evaluationsProvider, Evaluation lspEvaluation)
throws OrekitException {
if (iterationsCount == lastIter) {
Assert.assertEquals(lastEval + 1, evaluationscount);
} else {
Assert.assertEquals(lastIter + 1, iterationsCount);
}
lastIter = iterationsCount;
lastEval = evaluationscount;
Assert.assertEquals(measurements.size(), evaluationsProvider.getNumber());
try {
evaluationsProvider.getEstimatedMeasurement(-1);
Assert.fail("an exception should have been thrown");
} catch (OrekitException oe) {
Assert.assertEquals(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, oe.getSpecifier());
}
try {
evaluationsProvider.getEstimatedMeasurement(measurements.size());
Assert.fail("an exception should have been thrown");
} catch (OrekitException oe) {
Assert.assertEquals(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE, oe.getSpecifier());
}
AbsoluteDate previous = AbsoluteDate.PAST_INFINITY;
for (int i = 0; i < evaluationsProvider.getNumber(); ++i) {
AbsoluteDate current = evaluationsProvider.getEstimatedMeasurement(i).getDate();
Assert.assertTrue(current.compareTo(previous) >= 0);
previous = current;
}
}
});
ParameterDriver aDriver = estimator.getOrbitalParametersDrivers(true).getDrivers().get(0);
Assert.assertEquals("a", aDriver.getName());
aDriver.setValue(aDriver.getValue() + 1.2);
aDriver.setReferenceDate(AbsoluteDate.GALILEO_EPOCH);
EstimationTestUtils.checkFit(context, estimator, 2, 3,
0.0, 2.0e-5,
0.0, 5.2e-5,
0.0, 2.6e-5,
0.0, 1.1e-8);
// after the call to estimate, the parameters lacking a user-specified reference date
// got a default one
for (final ParameterDriver driver : estimator.getOrbitalParametersDrivers(true).getDrivers()) {
if ("a".equals(driver.getName())) {
// user-specified reference date
Assert.assertEquals(0, driver.getReferenceDate().durationFrom(AbsoluteDate.GALILEO_EPOCH), 1.0e-15);
} else {
// default reference date
Assert.assertEquals(0, driver.getReferenceDate().durationFrom(propagatorBuilder.getInitialOrbitDate()), 1.0e-15);
}
}
}
@Test
public void testMultiSat() throws OrekitException {
......
......@@ -34,9 +34,15 @@ import org.orekit.utils.Constants;
public class RangeMeasurementCreator extends MeasurementCreator {
private final Context context;
private final Vector3D antennaPhaseCenter;
public RangeMeasurementCreator(final Context context) {
this.context = context;
this(context, Vector3D.ZERO);
}
public RangeMeasurementCreator(final Context context, final Vector3D antennaPhaseCenter) {
this.context = context;
this.antennaPhaseCenter = antennaPhaseCenter;
}
public void handleStep(final SpacecraftState currentState, final boolean isLast)
......@@ -45,7 +51,7 @@ public class RangeMeasurementCreator extends MeasurementCreator {
for (final GroundStation station : context.stations) {
final AbsoluteDate date = currentState.getDate();
final Frame inertial = currentState.getFrame();
final Vector3D position = currentState.getPVCoordinates().getPosition();
final Vector3D position = currentState.toTransform().getInverse().transformPosition(antennaPhaseCenter);
final TopocentricFrame topo = station.getBaseFrame();
if (topo.getElevation(position, inertial, date) > FastMath.toRadians(30.0)) {
......
/* Copyright 2002-2017 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.estimation.measurements.modifiers;
import java.util.List;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.util.FastMath;
import org.junit.Assert;
import org.junit.Test;
import org.orekit.attitudes.LofOffset;
import org.orekit.errors.OrekitException;
import org.orekit.estimation.Context;
import org.orekit.estimation.EstimationTestUtils;
import org.orekit.estimation.measurements.EstimatedMeasurement;
import org.orekit.estimation.measurements.ObservedMeasurement;
import org.orekit.estimation.measurements.Range;
import org.orekit.estimation.measurements.RangeMeasurementCreator;
import org.orekit.frames.LOFType;
import org.orekit.orbits.OrbitType;
import org.orekit.orbits.PositionAngle;
import org.orekit.propagation.Propagator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.conversion.NumericalPropagatorBuilder;
import org.orekit.utils.Constants;
public class OnBoardAntenneRangeModifierTest {
@Test
public void testPreliminary() throws OrekitException {
// this test does not check OnBoardAntenneRangeModifier at all,
// it just checks RangeMeasurementCreator behaves as necessary for the other test
// the *real* test is testEffect below
Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
final NumericalPropagatorBuilder propagatorBuilder =
context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
1.0e-6, 60.0, 0.001);
propagatorBuilder.setAttitudeProvider(new LofOffset(propagatorBuilder.getFrame(), LOFType.LVLH));
// create perfect range measurements without antenna offset
final Propagator p1 = EstimationTestUtils.createPropagator(context.initialOrbit,
propagatorBuilder);
final List<ObservedMeasurement<?>> spacecraftCenteredMeasurements =
EstimationTestUtils.createMeasurements(p1,
new RangeMeasurementCreator(context, Vector3D.ZERO),
1.0, 3.0, 300.0);
// create perfect range measurements with antenna offset
final double xOffset = -2.5;
final Propagator p2 = EstimationTestUtils.createPropagator(context.initialOrbit,
propagatorBuilder);
final List<ObservedMeasurement<?>> antennaCenteredMeasurements =
EstimationTestUtils.createMeasurements(p2,
new RangeMeasurementCreator(context, new Vector3D(xOffset, 0, 0)),
1.0, 3.0, 300.0);
for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) {
Range sr = (Range) spacecraftCenteredMeasurements.get(i);
Range ar = (Range) antennaCenteredMeasurements.get(i);
double alphaMax = FastMath.asin(Constants.WGS84_EARTH_EQUATORIAL_RADIUS / sr.getObservedValue()[0]);
Assert.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 1.0e-8);
Assert.assertTrue(ar.getObservedValue()[0] - sr.getObservedValue()[0] >= xOffset);
Assert.assertTrue(ar.getObservedValue()[0] - sr.getObservedValue()[0] <= xOffset * FastMath.cos(alphaMax));
}
}
@Test
public void testEffect() throws OrekitException {
Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides");
final NumericalPropagatorBuilder propagatorBuilder =
context.createBuilder(OrbitType.KEPLERIAN, PositionAngle.TRUE, true,
1.0e-6, 60.0, 0.001);
propagatorBuilder.setAttitudeProvider(new LofOffset(propagatorBuilder.getFrame(), LOFType.LVLH));
// create perfect range measurements without antenna offset
final Propagator p1 = EstimationTestUtils.createPropagator(context.initialOrbit,
propagatorBuilder);
final List<ObservedMeasurement<?>> spacecraftCenteredMeasurements =
EstimationTestUtils.createMeasurements(p1,
new RangeMeasurementCreator(context, Vector3D.ZERO),
1.0, 3.0, 300.0);
// create perfect range measurements with antenna offset
final Vector3D apc = new Vector3D(-2.5, 0, 0);
final Propagator p2 = EstimationTestUtils.createPropagator(context.initialOrbit,
propagatorBuilder);
final List<ObservedMeasurement<?>> antennaCenteredMeasurements =
EstimationTestUtils.createMeasurements(p2,
new RangeMeasurementCreator(context, apc),
1.0, 3.0, 300.0);
final Propagator p3 = EstimationTestUtils.createPropagator(context.initialOrbit,
propagatorBuilder);
OnBoardAntennaRangeModifier modifier = new OnBoardAntennaRangeModifier(apc);
for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) {
Range sr = (Range) spacecraftCenteredMeasurements.get(i);
sr.addModifier(modifier);
EstimatedMeasurement<Range> estimated = sr.estimate(0, 0, new SpacecraftState[] { p3.propagate(sr.getDate()) });
Range ar = (Range) antennaCenteredMeasurements.get(i);
Assert.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 1.0e-8);
Assert.assertEquals(ar.getObservedValue()[0], estimated.getEstimatedValue()[0], 2.0e-7);
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment