/* Copyright 2002-2019 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.iod;

import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.util.FastMath;
import org.orekit.estimation.measurements.PV;
import org.orekit.frames.Frame;
import org.orekit.orbits.KeplerianOrbit;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.PVCoordinates;

/**
 * Gibbs initial orbit determination.
 * An orbit is determined from three position vectors.
 *
 * Reference:
 *  Vallado, D., Fundamentals of Astrodynamics and Applications
 *
 * @author Joris Olympio
 * @since 8.0
 *
 */
public class IodGibbs {

    /** Threshold for checking coplanr vectors. */
    private static final double COPLANAR_THRESHOLD = FastMath.toRadians(5.);

    /** gravitationnal constant. */
    private final double mu;

    /** Creator.
     *
     * @param mu gravitational constant
     */
    public IodGibbs(final double mu) {
        this.mu = mu;
    }

    /** Give an initial orbit estimation, assuming Keplerian motion.
     * All observations should be from the same location.
     *
     * @param frame measure frame
     * @param pv1 PV measure 1 taken in frame
     * @param pv2 PV measure 2 taken in frame
     * @param pv3 PV measure 3 taken in frame
     * @return an initial orbit estimation
     */
    public KeplerianOrbit estimate(final Frame frame, final PV pv1, final PV pv2, final PV pv3) {

        return estimate(frame,
                        pv1.getPosition(), pv1.getDate(),
                        pv2.getPosition(), pv2.getDate(),
                        pv3.getPosition(), pv3.getDate());
    }

    /** Give an initial orbit estimation, assuming Keplerian motion.
     * All observations should be from the same location.
     *
     * @param frame measure frame
     * @param r1 position 1 measured in frame
     * @param date1 date of measure 1
     * @param r2 position 2 measured in frame
     * @param date2 date of measure 2
     * @param r3 position 3 measured in frame
     * @param date3 date of measure 3
     * @return an initial orbit estimation
     */
    public KeplerianOrbit estimate(final Frame frame,
                                   final Vector3D r1, final AbsoluteDate date1,
                                   final Vector3D r2, final AbsoluteDate date2,
                                   final Vector3D r3, final AbsoluteDate date3) {
        // Checks measures are not at the same date
        if (date1.equals(date2) || date1.equals(date3) || date2.equals(date3)) {
            //throw new OrekitException("The measures are not different!");
        }

        // Checks measures are in the same plane
        final double num = r1.normalize().dotProduct(r2.normalize().crossProduct(r3.normalize()));
        final double alpha = FastMath.PI / 2.0 - FastMath.acos(num);
        if (FastMath.abs(alpha) > COPLANAR_THRESHOLD) {
            // throw something
            //throw new OrekitException("Non coplanar points!");
        }

        final Vector3D D = r1.crossProduct(r2).add(r2.crossProduct(r3).add(r3.crossProduct(r1)));

        final Vector3D N = (r2.crossProduct(r3)).scalarMultiply(r1.getNorm())
                .add((r3.crossProduct(r1)).scalarMultiply(r2.getNorm()))
                .add((r1.crossProduct(r2)).scalarMultiply(r3.getNorm()));

        final Vector3D B = D.crossProduct(r2);

        final Vector3D S = r1.scalarMultiply(r2.getNorm() - r3.getNorm())
                .add(r2.scalarMultiply(r3.getNorm() - r1.getNorm())
                     .add(r3.scalarMultiply(r1.getNorm() - r2.getNorm())));

        // middle velocity
        final double vm = FastMath.sqrt(mu / (N.getNorm() * D.getNorm()));
        final Vector3D vlEci = B.scalarMultiply(vm / r2.getNorm()).add(S.scalarMultiply(vm));

        // compile a new middle point with position, velocity
        final PVCoordinates pv = new PVCoordinates(r2, vlEci);
        final AbsoluteDate date = date2;

        // compute the equivalent Keplerian orbit
        return new KeplerianOrbit(pv, frame, date, mu);
    }
}

@@ -4792,23 +4792,247 @@
         
-return 
+KeplerianOrbit orbit = new KeplerianOrbit(pv, frame, date, mu);%0A        %0A        %0A        //define the reverse orbit%0A        final PVCoordinates pv2 = new PVCoordinates(r2, vlEci.scalarMultiply(-1));%0A        KeplerianOrbit orbit2 =
 new Kepl
@@ -5036,32 +5036,33 @@
 eplerianOrbit(pv
+2
 , frame, date, m
@@ -5061,20 +5061,541 @@
  date, mu);%0A
+        %0A        //check which orbit is correct%0A        Vector3D estP3 = orbit.shiftedBy(date3.durationFrom(date2)).%0A                getPVCoordinates().getPosition();%0A        double dist = estP3.subtract(r3).getNorm();%0A        Vector3D estP3_2 = orbit2.shiftedBy(date3.durationFrom(date2)).%0A                getPVCoordinates().getPosition();%0A        double dist2 = estP3_2.subtract(r3).getNorm();%0A        %0A        %0A        if(dist %3C= dist2)%7B%0A            return orbit;%0A        %7D else %7B%0A            return orbit2;%0A        %7D%0A
     %7D%0A%7D%0A

Update IodGibbs.java When there's more than a revolution between first and third observation, sometimes this method returned the corrected orbit but percurred in the wrong sense. So I added a control before the return.