public string ApproachPrediction(string moonName)
    {
        // @TODO: Too much C&P!
        // Step 1: Determine the patched conic xfer
        OrbitData shipOrbit = new OrbitData();
        NBody     shipNbody = spaceshipCtrl.GetNBody();

        shipOrbit.SetOrbit(shipNbody, centralMass);
        OrbitData moonOrbit = new OrbitData();
        NBody     moonNbody = GetTargetByName(moonName);

        moonOrbit.SetOrbit(moonNbody, centralMass);
        //Make a copy of the universe state and evolve forward to find min distance to
        // moon.
        GravityEngine ge = GravityEngine.Instance();
        GravityState  gs = ge.GetGravityStateCopy();
        // run a simulation and find the closest approach (Expensive!)
        LunarCourseCorrection lcc = new LunarCourseCorrection(shipNbody, moonNbody);

        // want to be within 10% of Earth-Moon distance, before start checking
        courseCorrectionData = new LunarCourseCorrection.CorrectionData();
        courseCorrectionData.gravityState     = gs;
        courseCorrectionData.approachDistance = 0.1f * moonOrbit.a;;
        courseCorrectionData.correction       = 0;
        courseCorrectionData.maxPhysTime      = time_to_moon_phys;
        lcc.ClosestApproachAsync(courseCorrectionData, MoonPreviewCompleted);
        return("Calculation started...\n");
    }
    public string MoonPreview(string moonName, float lambda1, bool async)
    {
        // Step 1: Determine the patched conic xfer
        OrbitData shipOrbit = new OrbitData();
        NBody     shipNbody = spaceshipCtrl.GetNBody();

        shipOrbit.SetOrbit(shipNbody, centralMass);
        OrbitData moonOrbit = new OrbitData();
        NBody     moonNbody = GetTargetByName(moonName);

        moonOrbit.SetOrbit(moonNbody, centralMass);
        OrbitTransfer xfer = new PatchedConicXfer(shipOrbit, moonOrbit, lambda1);

        // Step 2: Make a copy of the universe state and evolve forward to find min distance to
        // moon.
        GravityEngine ge = GravityEngine.Instance();
        GravityState  gs = ge.GetGravityStateCopy();

        // there is only one maneuver to add
        gs.maneuverMgr.Add(xfer.GetManeuvers()[0]);
        // run a simulation and find the closest approach (Expensive!)
        LunarCourseCorrection lcc = new LunarCourseCorrection(shipNbody, moonNbody);

        // want to be within 10% of Earth-Moon distance, before start checking
        courseCorrectionData = new LunarCourseCorrection.CorrectionData();
        courseCorrectionData.gravityState     = gs;
        courseCorrectionData.approachDistance = 0.1f * moonOrbit.a;;
        courseCorrectionData.correction       = 0;
        courseCorrectionData.maxPhysTime      = time_to_moon_phys;
        // Direct (unthreaded) calculation
        if (async)
        {
            lcc.ClosestApproachAsync(courseCorrectionData, MoonPreviewCompleted);
            return("Calculation started...\n");
        }
        else
        {
            predictedDistance = lcc.ClosestApproach(courseCorrectionData);
            return(string.Format("Patched Conic with lambda={0} => approach={1}\n", lambda1, predictedDistance));
        }
    }
    public string CorrectionCalcAsync(double targetDistance,
                                      double targetAccuracy,
                                      double approachDistance,
                                      double maxTime,
                                      CalcCallback calcCallback)
    {
        this.calcCallback = calcCallback;
        double[] corrections = { -0.001, 0, 0.001 };
        this.targetDistance = targetDistance;
        this.targetAccuracy = targetAccuracy;

        threadsPending = 0;
        calcState      = CalcState.NOT_STARTED;

        string        s  = "";
        GravityEngine ge = GravityEngine.Instance();

        correctionData = new CorrectionData[corrections.Length];
        int i = 0;

        foreach (double correction in corrections)
        {
            // Run each computation as a dedicated thread
            correctionData[i] = new CorrectionData();
            correctionData[i].gravityState     = ge.GetGravityStateCopy();
            correctionData[i].approachDistance = approachDistance;
            correctionData[i].maxPhysTime      = maxTime;
            correctionData[i].correction       = correction;
            threadCountMutex.WaitOne();
            threadsPending++;
            threadCountMutex.ReleaseMutex();
            System.Threading.ThreadPool.QueueUserWorkItem(
                new System.Threading.WaitCallback(CalcCorrectionThread),
                new object[] { correctionData[i], CreateUnityAdapter(), (JobResultHandler)CalcResultHandler });
            i++;
        }
        calcState = CalcState.INITIAL_THREE;
        return(s);
    }