@Test
  public void testSpin() throws OrekitException {

    AbsoluteDate date =
        new AbsoluteDate(
            new DateComponents(1970, 01, 01),
            new TimeComponents(3, 25, 45.6789),
            TimeScalesFactory.getUTC());
    KeplerianOrbit orbit =
        new KeplerianOrbit(
            7178000.0,
            1.e-4,
            FastMath.toRadians(50.),
            FastMath.toRadians(10.),
            FastMath.toRadians(20.),
            FastMath.toRadians(30.),
            PositionAngle.MEAN,
            FramesFactory.getEME2000(),
            date,
            3.986004415e14);

    final AttitudeProvider law =
        new LofOffsetPointing(
            earthSpheric,
            new LofOffset(orbit.getFrame(), LOFType.VVLH, RotationOrder.XYX, 0.1, 0.2, 0.3),
            Vector3D.PLUS_K);

    Propagator propagator = new KeplerianPropagator(orbit, law);

    double h = 0.01;
    SpacecraftState sMinus = propagator.propagate(date.shiftedBy(-h));
    SpacecraftState s0 = propagator.propagate(date);
    SpacecraftState sPlus = propagator.propagate(date.shiftedBy(h));

    // check spin is consistent with attitude evolution
    double errorAngleMinus =
        Rotation.distance(
            sMinus.shiftedBy(h).getAttitude().getRotation(), s0.getAttitude().getRotation());
    double evolutionAngleMinus =
        Rotation.distance(sMinus.getAttitude().getRotation(), s0.getAttitude().getRotation());
    Assert.assertEquals(0.0, errorAngleMinus, 1.0e-6 * evolutionAngleMinus);
    double errorAnglePlus =
        Rotation.distance(
            s0.getAttitude().getRotation(), sPlus.shiftedBy(-h).getAttitude().getRotation());
    double evolutionAnglePlus =
        Rotation.distance(s0.getAttitude().getRotation(), sPlus.getAttitude().getRotation());
    Assert.assertEquals(0.0, errorAnglePlus, 1.0e-6 * evolutionAnglePlus);

    Vector3D spin0 = s0.getAttitude().getSpin();
    Vector3D reference =
        AngularCoordinates.estimateRate(
            sMinus.getAttitude().getRotation(), sPlus.getAttitude().getRotation(), 2 * h);
    Assert.assertTrue(spin0.getNorm() > 1.0e-3);
    Assert.assertEquals(0.0, spin0.subtract(reference).getNorm(), 1.0e-10);
  }
  @Test
  public void testCartesian() throws OrekitException {

    // Propagation of the initial at t + dt
    final double dt = 3200;
    propagator.setOrbitType(OrbitType.CARTESIAN);
    final PVCoordinates finalState =
        propagator.propagate(initDate.shiftedBy(dt)).getPVCoordinates();
    final Vector3D pFin = finalState.getPosition();
    final Vector3D vFin = finalState.getVelocity();

    // Check results
    final PVCoordinates reference = initialState.shiftedBy(dt).getPVCoordinates();
    final Vector3D pRef = reference.getPosition();
    final Vector3D vRef = reference.getVelocity();
    Assert.assertEquals(0, pRef.subtract(pFin).getNorm(), 2e-4);
    Assert.assertEquals(0, vRef.subtract(vFin).getNorm(), 7e-8);

    try {
      propagator.getGeneratedEphemeris();
      Assert.fail("an exception should have been thrown");
    } catch (IllegalStateException ise) {
      // expected
    }
  }
 /**
  * Simple constructor.
  *
  * <p>The {@code applyBefore} parameter is mainly used when the differential effect is associated
  * with a maneuver. In this case, the parameter must be set to {@code false}.
  *
  * @param original original state at reference date
  * @param directEffect direct effect changing the orbit
  * @param applyBefore if true, effect is applied both before and after reference date, if false it
  *     is only applied after reference date
  * @param referenceRadius reference radius of the Earth for the potential model (m)
  * @param mu central attraction coefficient (m³/s²)
  * @param j2 un-normalized zonal coefficient (about +1.08e-3 for Earth)
  * @exception OrekitException if direct effect cannot be applied
  */
 public J2DifferentialEffect(
     final SpacecraftState original,
     final AdapterPropagator.DifferentialEffect directEffect,
     final boolean applyBefore,
     final double referenceRadius,
     final double mu,
     final double j2)
     throws OrekitException {
   this(
       original.getOrbit(),
       directEffect.apply(original.shiftedBy(0.001)).getOrbit().shiftedBy(-0.001),
       applyBefore,
       referenceRadius,
       mu,
       j2);
 }