Skip to content
Snippets Groups Projects
Commit 23281eac authored by Luc Maisonobe's avatar Luc Maisonobe
Browse files

Merge branch 'ThomasP/orekit-issue-702' into develop

parents eee4db78 541e3dd4
No related branches found
No related tags found
No related merge requests found
......@@ -21,6 +21,9 @@
</properties>
<body>
<release version="11.0" date="TBD" description="TBD">
<action dev="thomas" type="fix" issue="702">
Added possibility to take in account several bodies while computing SRP perturbation.
</action>
<action dev="bryan" type="update" issue="793">
Updated SP3File visibility to public.
</action>
......
......@@ -16,12 +16,15 @@
*/
package org.orekit.forces.radiation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.hipparchus.CalculusFieldElement;
import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.util.FastMath;
import org.hipparchus.util.MathArrays;
import org.hipparchus.util.Precision;
import org.orekit.frames.Frame;
import org.orekit.propagation.FieldSpacecraftState;
......@@ -109,7 +112,7 @@ public class SolarRadiationPressure extends AbstractRadiationForceModel {
final double r2 = sunSatVector.getNormSq();
// compute flux
final double ratio = getLightingRatio(position, frame, date);
final double ratio = getTotalLightingRatio(position, frame, date);
final double rawP = ratio * kRef / r2;
final Vector3D flux = new Vector3D(rawP / FastMath.sqrt(r2), sunSatVector);
......@@ -130,7 +133,7 @@ public class SolarRadiationPressure extends AbstractRadiationForceModel {
final T r2 = sunSatVector.getNormSq();
// compute flux
final T ratio = getLightingRatio(position, frame, date);
final T ratio = getTotalLightingRatio(position, frame, date);
final T rawP = ratio.divide(r2).multiply(kRef);
final FieldVector3D<T> flux = new FieldVector3D<>(rawP.divide(r2.sqrt()), sunSatVector);
......@@ -140,6 +143,7 @@ public class SolarRadiationPressure extends AbstractRadiationForceModel {
}
/** Get the lighting ratio ([0-1]).
* Considers only central body as occulting body.
* @param position the satellite's position in the selected frame.
* @param frame in which is defined the position
* @param date the date
......@@ -201,7 +205,163 @@ public class SolarRadiationPressure extends AbstractRadiationForceModel {
}
/** Get eclipse ratio between to bodies seen from a specific object.
* Ratio is in [0-1].
* @param position the satellite's position in the selected frame
* @param occultingPosition the position of the occulting object
* @param occultingRadius the mean radius of the occulting object
* @param occultedPosition the position of the occulted object
* @param occultedRadius the mean radius of the occulted object
* @return eclipse ratio
*/
public double getGeneralEclipseRatio(final Vector3D position,
final Vector3D occultingPosition,
final double occultingRadius,
final Vector3D occultedPosition,
final double occultedRadius) {
// Compute useful angles
final double[] angle = getGeneralEclipseAngles(position, occultingPosition, occultingRadius, occultedPosition, occultedRadius);
// Sat-Occulted/ Sat-Occulting angle
final double occultedSatOcculting = angle[0];
// Occulting apparent radius
final double alphaOcculting = angle[1];
// Occulted apparent radius
final double alphaOcculted = angle[2];
double result = 1.0;
// Is the satellite in complete umbra ?
if (occultedSatOcculting - alphaOcculting + alphaOcculted <= ANGULAR_MARGIN) {
result = 0.0;
} else if (occultedSatOcculting - alphaOcculting - alphaOcculted < -ANGULAR_MARGIN) {
// Compute an eclipse ratio in penumbra
final double sEA2 = occultedSatOcculting * occultedSatOcculting;
final double oo2sEA = 1.0 / (2. * occultedSatOcculting);
final double aS2 = alphaOcculted * alphaOcculted;
final double aE2 = alphaOcculting * alphaOcculting;
final double aE2maS2 = aE2 - aS2;
final double alpha1 = (sEA2 - aE2maS2) * oo2sEA;
final double alpha2 = (sEA2 + aE2maS2) * oo2sEA;
// Protection against numerical inaccuracy at boundaries
final double almost0 = Precision.SAFE_MIN;
final double almost1 = FastMath.nextDown(1.0);
final double a1oaS = FastMath.min(almost1, FastMath.max(-almost1, alpha1 / alphaOcculted));
final double aS2ma12 = FastMath.max(almost0, aS2 - alpha1 * alpha1);
final double a2oaE = FastMath.min(almost1, FastMath.max(-almost1, alpha2 / alphaOcculting));
final double aE2ma22 = FastMath.max(almost0, aE2 - alpha2 * alpha2);
final double P1 = aS2 * FastMath.acos(a1oaS) - alpha1 * FastMath.sqrt(aS2ma12);
final double P2 = aE2 * FastMath.acos(a2oaE) - alpha2 * FastMath.sqrt(aE2ma22);
result = 1. - (P1 + P2) / (FastMath.PI * aS2);
}
return result;
}
/** Get the total lighting ratio ([0-1]).
* This method considers every occulting bodies.
* @param position the satellite's position in the selected frame.
* @param frame in which is defined the position
* @param date the date
* @return lighting ratio
*/
public double getTotalLightingRatio(final Vector3D position, final Frame frame, final AbsoluteDate date) {
double result = 0.0;
final Map<ExtendedPVCoordinatesProvider, Double> otherOccultingBodies = getOtherOccultingBodies();
final Vector3D sunPosition = sun.getPVCoordinates(date, frame).getPosition();
final int n = otherOccultingBodies.size() + 1;
if (n > 1) {
final Vector3D[] occultingBodyPositions = new Vector3D[n];
final double[] occultingBodyRadiuses = new double[n];
// Central body
occultingBodyPositions[0] = new Vector3D(0.0, 0.0, 0.0);
occultingBodyRadiuses[0] = getEquatorialRadius();
// Other occulting bodies
int k = 1;
for (ExtendedPVCoordinatesProvider provider: otherOccultingBodies.keySet()) {
occultingBodyPositions[k] = provider.getPVCoordinates(date, frame).getPosition();
occultingBodyRadiuses[k] = otherOccultingBodies.get(provider);
++k;
}
for (int i = 0; i < n; ++i) {
// Lighting ratio computations
final double eclipseRatioI = getGeneralEclipseRatio(position,
occultingBodyPositions[i],
occultingBodyRadiuses[i],
sunPosition,
Constants.SUN_RADIUS);
// First body totaly occults Sun, full eclipse is occuring.
if (eclipseRatioI == 0.0) {
return 0.0;
}
result += eclipseRatioI;
// Mutual occulting body eclipse ratio computations between first and secondary bodies
for (int j = i + 1; j < n; ++j) {
final double eclipseRatioJ = getGeneralEclipseRatio(position,
occultingBodyPositions[j],
occultingBodyRadiuses[j],
sunPosition,
Constants.SUN_RADIUS);
final double eclipseRatioIJ = getGeneralEclipseRatio(position,
occultingBodyPositions[i],
occultingBodyRadiuses[i],
occultingBodyPositions[j],
occultingBodyRadiuses[j]);
final double alphaJ = getGeneralEclipseAngles(position,
occultingBodyPositions[i],
occultingBodyRadiuses[i],
occultingBodyPositions[j],
occultingBodyRadiuses[j])[2];
final double alphaSun = getEclipseAngles(sunPosition, position)[2];
final double alphaJSq = alphaJ * alphaJ;
final double alphaSunSq = alphaSun * alphaSun;
final double mutualEclipseCorrection = (1 - eclipseRatioIJ) * alphaJSq / alphaSunSq;
// Secondary body totaly occults Sun, no more computations are required, full eclipse is occuring.
if (eclipseRatioJ == 0.0 ) {
return 0.0;
}
// Secondary body partially occults Sun
else if (eclipseRatioJ != 1) {
result -= mutualEclipseCorrection;
}
}
}
// Final term
result -= n - 1;
} else {
// only central body is considered
result = getLightingRatio(position, frame, date);
}
return result;
}
/** Get the lighting ratio ([0-1]).
* Considers only central body as occulting body.
* @param position the satellite's position in the selected frame.
* @param frame in which is defined the position
* @param date the date
......@@ -266,6 +426,166 @@ public class SolarRadiationPressure extends AbstractRadiationForceModel {
return result;
}
/** Get eclipse ratio between to bodies seen from a specific object.
* Ratio is in [0-1].
* @param position the satellite's position in the selected frame
* @param occultingPosition the position of the occulting object
* @param occultingRadius the mean radius of the occulting object
* @param occultedPosition the position of the occulted object
* @param occultedRadius the mean radius of the occulted object
* @param <T> extends RealFieldElement
* @return eclipse ratio
*/
public <T extends CalculusFieldElement<T>> T getGeneralEclipseRatio(final FieldVector3D<T> position,
final FieldVector3D<T> occultingPosition,
final T occultingRadius,
final FieldVector3D<T> occultedPosition,
final T occultedRadius) {
final T one = occultingRadius.getField().getOne();
// Compute useful angles
final T[] angle = getGeneralEclipseAngles(position, occultingPosition, occultingRadius, occultedPosition, occultedRadius);
// Sat-Occulted/ Sat-Occulting angle
final T occultedSatOcculting = angle[0];
// Occulting apparent radius
final T alphaOcculting = angle[1];
// Occulted apparent radius
final T alphaOcculted = angle[2];
T result = one;
// Is the satellite in complete umbra ?
if (occultedSatOcculting.getReal() - alphaOcculting.getReal() + alphaOcculted.getReal() <= ANGULAR_MARGIN) {
result = occultingRadius.getField().getZero();
} else if (occultedSatOcculting.getReal() - alphaOcculting.getReal() - alphaOcculted.getReal() < -ANGULAR_MARGIN) {
// Compute a lighting ratio in penumbra
final T sEA2 = occultedSatOcculting.multiply(occultedSatOcculting);
final T oo2sEA = occultedSatOcculting.multiply(2).reciprocal();
final T aS2 = alphaOcculted.multiply(alphaOcculted);
final T aE2 = alphaOcculting.multiply(alphaOcculting);
final T aE2maS2 = aE2.subtract(aS2);
final T alpha1 = sEA2.subtract(aE2maS2).multiply(oo2sEA);
final T alpha2 = sEA2.add(aE2maS2).multiply(oo2sEA);
// Protection against numerical inaccuracy at boundaries
final double almost0 = Precision.SAFE_MIN;
final double almost1 = FastMath.nextDown(1.0);
final T a1oaS = min(almost1, max(-almost1, alpha1.divide(alphaOcculted)));
final T aS2ma12 = max(almost0, aS2.subtract(alpha1.multiply(alpha1)));
final T a2oaE = min(almost1, max(-almost1, alpha2.divide(alphaOcculting)));
final T aE2ma22 = max(almost0, aE2.subtract(alpha2.multiply(alpha2)));
final T P1 = aS2.multiply(a1oaS.acos()).subtract(alpha1.multiply(aS2ma12.sqrt()));
final T P2 = aE2.multiply(a2oaE.acos()).subtract(alpha2.multiply(aE2ma22.sqrt()));
result = one.subtract(P1.add(P2).divide(aS2.multiply(FastMath.PI)));
}
return result;
}
/** Get the total lighting ratio ([0-1]).
* This method considers every occulting bodies.
* @param position the satellite's position in the selected frame.
* @param frame in which is defined the position
* @param date the date
* @param <T> extends RealFieldElement
* @return lighting rati
*/
public <T extends CalculusFieldElement<T>> T getTotalLightingRatio(final FieldVector3D<T> position, final Frame frame, final FieldAbsoluteDate<T> date) {
final T zero = position.getAlpha().getField().getZero();
T result = zero;
final Map<ExtendedPVCoordinatesProvider, Double> otherOccultingBodies = getOtherOccultingBodies();
final FieldVector3D<T> sunPosition = sun.getPVCoordinates(date, frame).getPosition();
final int n = otherOccultingBodies.size() + 1;
if (n > 1) {
final List<FieldVector3D<T>> occultingBodyPositions = new ArrayList<FieldVector3D<T>>(n);
final T[] occultingBodyRadiuses = MathArrays.buildArray(zero.getField(), n);
// Central body
occultingBodyPositions.add(0, new FieldVector3D<>(zero, zero, zero));
occultingBodyRadiuses[0] = zero.add(getEquatorialRadius());
// Other occulting bodies
int k = 1;
for (ExtendedPVCoordinatesProvider provider: otherOccultingBodies.keySet()) {
occultingBodyPositions.add(k, provider.getPVCoordinates(date, frame).getPosition());
occultingBodyRadiuses[k] = zero.add(otherOccultingBodies.get(provider));
++k;
}
for (int i = 0; i < n; ++i) {
// Lighting ratio computations
final T eclipseRatioI = getGeneralEclipseRatio(position,
occultingBodyPositions.get(i),
occultingBodyRadiuses[i],
sunPosition,
zero.add(Constants.SUN_RADIUS));
// First body totaly occults Sun, full eclipse is occuring.
if (eclipseRatioI.getReal() == 0.0) {
return zero;
}
result = result.add(eclipseRatioI);
// Mutual occulting body eclipse ratio computations between first and secondary bodies
for (int j = i + 1; j < n; ++j) {
final T eclipseRatioJ = getGeneralEclipseRatio(position,
occultingBodyPositions.get(i),
occultingBodyRadiuses[j],
sunPosition,
zero.add(Constants.SUN_RADIUS));
final T eclipseRatioIJ = getGeneralEclipseRatio(position,
occultingBodyPositions.get(i),
occultingBodyRadiuses[i],
occultingBodyPositions.get(j),
occultingBodyRadiuses[j]);
final T alphaJ = getGeneralEclipseAngles(position,
occultingBodyPositions.get(i),
occultingBodyRadiuses[i],
occultingBodyPositions.get(j),
occultingBodyRadiuses[j])[2];
final T alphaSun = getEclipseAngles(sunPosition, position)[2];
final T alphaJSq = alphaJ.multiply(alphaJ);
final T alphaSunSq = alphaSun.multiply(alphaSun);
final T mutualEclipseCorrection = eclipseRatioIJ.negate().add(1).multiply(alphaJSq).divide(alphaSunSq);
// Secondary body totaly occults Sun, no more computations are required, full eclipse is occuring.
if (eclipseRatioJ.getReal() == 0.0 ) {
return zero;
}
// Secondary body partially occults Sun
else if (eclipseRatioJ.getReal() != 1) {
result = result.subtract(mutualEclipseCorrection);
}
}
}
// Final term
result = result.subtract(n - 1);
} else {
// only central body is considered
result = getLightingRatio(position, frame, date);
}
return result;
}
/** {@inheritDoc} */
@Override
public List<ParameterDriver> getParametersDrivers() {
......@@ -279,7 +599,7 @@ public class SolarRadiationPressure extends AbstractRadiationForceModel {
* @return min value
*/
private <T extends CalculusFieldElement<T>> T min(final double d, final T f) {
return (f.getReal() > d) ? f.getField().getZero().add(d) : f;
return (f.getReal() > d) ? f.getField().getZero().newInstance(d) : f;
}
/** Compute max of two values, one double and one field element.
......@@ -289,7 +609,7 @@ public class SolarRadiationPressure extends AbstractRadiationForceModel {
* @return max value
*/
private <T extends CalculusFieldElement<T>> T max(final double d, final T f) {
return (f.getReal() <= d) ? f.getField().getZero().add(d) : f;
return (f.getReal() <= d) ? f.getField().getZero().newInstance(d) : f;
}
}
......@@ -17,6 +17,7 @@
package org.orekit.forces.radiation;
import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
......@@ -41,6 +42,9 @@ import org.orekit.Utils;
import org.orekit.attitudes.LofOffset;
import org.orekit.bodies.CelestialBodyFactory;
import org.orekit.bodies.OneAxisEllipsoid;
import org.orekit.data.DataContext;
import org.orekit.data.DataProvidersManager;
import org.orekit.data.DirectoryCrawler;
import org.orekit.errors.OrekitException;
import org.orekit.forces.AbstractLegacyForceModelTest;
import org.orekit.forces.BoxAndSolarArraySpacecraft;
......@@ -175,7 +179,7 @@ public class SolarRadiationPressureTest extends AbstractLegacyForceModelTest {
ExtendedPVCoordinatesProvider sun = CelestialBodyFactory.getSun();
SolarRadiationPressure srp =
new SolarRadiationPressure(sun, Constants.SUN_RADIUS,
(RadiationSensitive) new IsotropicRadiationClassicalConvention(50.0, 0.5, 0.5));
new IsotropicRadiationClassicalConvention(50.0, 0.5, 0.5));
Assert.assertFalse(srp.dependsOnPositionOnly());
Vector3D position = orbit.getPVCoordinates().getPosition();
......@@ -206,7 +210,7 @@ public class SolarRadiationPressureTest extends AbstractLegacyForceModelTest {
FramesFactory.getITRF(IERSConventions.IERS_2010, true));
SolarRadiationPressure SRP =
new SolarRadiationPressure(sun, earth.getEquatorialRadius(),
(RadiationSensitive) new IsotropicRadiationCNES95Convention(50.0, 0.5, 0.5));
new IsotropicRadiationCNES95Convention(50.0, 0.5, 0.5));
double period = 2*FastMath.PI*FastMath.sqrt(orbit.getA()*orbit.getA()*orbit.getA()/orbit.getMu());
Assert.assertEquals(86164, period, 1);
......@@ -603,6 +607,7 @@ public class SolarRadiationPressureTest extends AbstractLegacyForceModelTest {
private static class SolarStepHandler implements OrekitFixedStepHandler {
@Override
public void handleStep(SpacecraftState currentState, boolean isLast) {
final double dex = currentState.getEquinoctialEx() - 0.01071166;
final double dey = currentState.getEquinoctialEy() - 0.00654848;
......@@ -656,7 +661,7 @@ public class SolarRadiationPressureTest extends AbstractLegacyForceModelTest {
AdaptiveStepsizeFieldIntegrator<DerivativeStructure> integrator =
new DormandPrince853FieldIntegrator<>(field, 0.001, 200, tolerance[0], tolerance[1]);
integrator.setInitialStepSize(60);
AdaptiveStepsizeIntegrator RIntegrator =
new DormandPrince853Integrator(0.001, 200, tolerance[0], tolerance[1]);
RIntegrator.setInitialStepSize(60);
......@@ -682,7 +687,7 @@ public class SolarRadiationPressureTest extends AbstractLegacyForceModelTest {
FNP.addForceModel(forceModel);
NP.addForceModel(forceModel);
// Do the test
checkRealFieldPropagation(FKO, PositionAngle.MEAN, 1000., NP, FNP,
1.0e-30, 5.0e-10, 3.0e-11, 3.0e-10,
......@@ -761,6 +766,129 @@ public class SolarRadiationPressureTest extends AbstractLegacyForceModelTest {
Assert.assertFalse(FastMath.abs(finPVC_DS.toPVCoordinates().getPosition().getZ() - finPVC_R.getPosition().getZ()) < FastMath.abs(finPVC_R.getPosition().getZ()) * 1e-11);
}
/** Testing if eclipses due to Moon are considered.
* Earth is artificially reduced to a single point to only consider Moon effect.
* Reference values are presented in "A Study of Solar Radiation Pressure acting on GPS Satellites"
* written by Laurent Olivier Froideval in 2009.
* Modifications of the step handler and time span able to print lighting ratios other a year and get a reference like graph.
*/
@Test
public void testMoonEclipse() {
// Configure Orekit
final File home = new File(System.getProperty("user.home"));
final File orekitData = new File(home, "orekit-data");
final DataProvidersManager manager = DataContext.getDefault().getDataProvidersManager();
manager.addProvider(new DirectoryCrawler(orekitData));
final ExtendedPVCoordinatesProvider sun = CelestialBodyFactory.getSun();
final ExtendedPVCoordinatesProvider moon = CelestialBodyFactory.getMoon();
final Frame GCRF = FramesFactory.getGCRF();
final AbsoluteDate date = new AbsoluteDate(2007, 01, 01, 6, 0, 0, TimeScalesFactory.getGPS());
final double dt = 3600 * 24 * 180;
final AbsoluteDate target = date.shiftedBy(dt);
//PV coordinates from sp3 file esa14081.sp3 PRN 03
final PVCoordinates refPV = new PVCoordinates(new Vector3D(14986728.76145754, 14849687.258579938, -16523319.786690142),
new Vector3D(-3152.6722260146, 1005.6757515113, -1946.9273038773));
final Orbit orbit = new CartesianOrbit(refPV, GCRF, date, Constants.EIGEN5C_EARTH_MU);
final SpacecraftState initialState = new SpacecraftState(orbit);
// Create SRP perturbation with Moon and Earth
SolarRadiationPressure srp =
new SolarRadiationPressure(sun, Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS,
new IsotropicRadiationClassicalConvention(50.0, 0.5, 0.5));
srp.addOccultingBody(moon, Constants.MOON_EQUATORIAL_RADIUS);
// creation of the propagator
double[] absTolerance = {
0.1, 1.0e-9, 1.0e-9, 1.0e-5, 1.0e-5, 1.0e-5, 0.001
};
double[] relTolerance = {
1.0e-4, 1.0e-4, 1.0e-4, 1.0e-6, 1.0e-6, 1.0e-6, 1.0e-7
};
AdaptiveStepsizeIntegrator integrator =
new DormandPrince853Integrator(600, 1000000, absTolerance, relTolerance);
final NumericalPropagator propagator = new NumericalPropagator(integrator);
propagator.setInitialState(initialState);
propagator.addForceModel(srp);
propagator.setMasterMode(600, new MoonEclipseStepHandler(moon, sun, srp));
propagator.propagate(target);
}
/** Specialized step handler.
* <p>This class extends the step handler in order to print on the output stream at the given step.<p>
* @author Thomas Paulet
*/
private static class MoonEclipseStepHandler implements OrekitFixedStepHandler {
final ExtendedPVCoordinatesProvider moon;
final ExtendedPVCoordinatesProvider sun;
final SolarRadiationPressure srp;
/** Simple constructor.
*/
MoonEclipseStepHandler(final ExtendedPVCoordinatesProvider moon, final ExtendedPVCoordinatesProvider sun,
final SolarRadiationPressure srp) {
this.moon = moon;
this.sun = sun;
this.srp = srp;
}
/** {@inheritDoc} */
@Override
public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) {
}
/** {@inheritDoc} */
@Override
public void handleStep(final SpacecraftState currentState, final boolean isLast) {
final AbsoluteDate date = currentState.getDate();
final Frame frame = currentState.getFrame();
final Vector3D moonPos = moon.getPVCoordinates(date, frame).getPosition();
final Vector3D sunPos = sun.getPVCoordinates(date, frame).getPosition();
final Vector3D statePos = currentState.getPVCoordinates().getPosition();
// Moon umbra and penumbra conditions
final double[] moonAngles = srp.getGeneralEclipseAngles(statePos, moonPos, Constants.MOON_EQUATORIAL_RADIUS,
sunPos, Constants.SUN_RADIUS);
final double moonUmbra = moonAngles[0] - moonAngles[1] + moonAngles[2];
final boolean isInMoonUmbra = (moonUmbra < 1.0e-10);
final double moonPenumbra = moonAngles[0] - moonAngles[1] - moonAngles[2];
final boolean isInMoonPenumbra = (moonPenumbra < -1.0e-10);
// Earth umbra and penumbra conditions
final double[] earthAngles = srp.getEclipseAngles(sunPos, statePos);
final double earthUmbra = earthAngles[0] - earthAngles[1] + earthAngles[2];
final boolean isInEarthUmbra = (earthUmbra < 1.0e-10);
final double earthPenumbra = earthAngles[0] - earthAngles[1] - earthAngles[2];
final boolean isInEarthPenumbra = (earthPenumbra < -1.0e-10);
// Compute lighting ration
final double lightingRatio = srp.getTotalLightingRatio(statePos, frame, date);
// Check behaviour
if (isInMoonUmbra || isInEarthUmbra) {
Assert.assertEquals(0.0, lightingRatio, 1e-8);
}
else if (isInMoonPenumbra || isInEarthPenumbra) {
Assert.assertEquals(true, (lightingRatio < 1.0));
Assert.assertEquals(true, (lightingRatio > 0.0));
}
else {
Assert.assertEquals(1.0, lightingRatio, 1e-8);
}
}
}
@Before
public void setUp() {
Utils.setDataRoot("regular-data");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment