示例#1
0
 public void Reset()
 {
     fidat   = Enumerable.Range(0, SEI_NEPHFILES).Select(i => new file_data()).ToArray();
     savedat = Enumerable.Range(0, SwissEph.SE_NPLANETS + 1).Select(i => new save_positions()).ToArray();
     pldat   = Enumerable.Range(0, SEI_NPLANETS).Select(i => new plan_data()).ToArray();
     nddat   = Enumerable.Range(0, SEI_NNODE_ETC).Select(i => new plan_data()).ToArray();
     topd    = new topo_data();
     dpsi    = new double[36525];
     deps    = new double[36525];
     nut     = new nut();
     nut2000 = new nut();
     nutv    = new nut();
     oec     = new epsilon();
     oec2000 = new epsilon();
 }
示例#2
0
 public void Reset(bool full)
 {
     if (full)
     {
         fidat   = Enumerable.Range(0, SEI_NEPHFILES).Select(i => new file_data()).ToArray();
         savedat = Enumerable.Range(0, SwissEph.SE_NPLANETS + 1).Select(i => new save_positions()).ToArray();
         pldat   = Enumerable.Range(0, SEI_NPLANETS).Select(i => new plan_data()).ToArray();
         nddat   = Enumerable.Range(0, SEI_NNODE_ETC).Select(i => new plan_data()).ToArray();
         topd    = new topo_data();
         //dpsi = new double[36525];
         //deps = new double[36525];
         astro_models = new Int32[Sweph.SEI_NMODELS];
     }
     oec     = new epsilon();
     oec2000 = new epsilon();
     nut     = new nut();
     nut2000 = new nut();
     nutv    = new nut();
 }
示例#3
0
        /* converts planets from barycentric to geocentric,
         * apparent positions
         * precession and nutation
         * according to flags
         * ipli		planet number
         * iflag	flags
         */
        int app_pos_etc_plan_osc(int ipl, int ipli, Int32 iflag, ref string serr)
        {
            int i, j, niter, retc;
            double[] xx = new double[6], dx = new double[3]; double dt, dtsave_for_defl;
            double[] xearth = new double[6], xsun = new double[6], xmoon = new double[6];
            double[] xxsv = new double[6], xxsp = new double[3] { 0, 0, 0 }, xobs = new double[6], xobs2 = new double[6];
            double t;
            plan_data pdp = swed.pldat[ipli];
            plan_data pedp = swed.pldat[SEI_EARTH];
            plan_data psdp = swed.pldat[SEI_SUNBARY];
            epsilon oe = swed.oec2000;
            Int32 epheflag = SwissEph.SEFLG_DEFAULTEPH;
            dt = dtsave_for_defl = 0;	/* dummy assign to silence gcc */
            if ((iflag & SwissEph.SEFLG_MOSEPH) != 0)
                epheflag = SwissEph.SEFLG_MOSEPH;
            else if ((iflag & SwissEph.SEFLG_SWIEPH) != 0)
                epheflag = SwissEph.SEFLG_SWIEPH;
            else if ((iflag & SwissEph.SEFLG_JPLEPH) != 0)
                epheflag = SwissEph.SEFLG_JPLEPH;
            /* the conversions will be done with xx[]. */
            for (i = 0; i <= 5; i++)
                xx[i] = pdp.x[i];
            /************************************
             * barycentric position is required *
             ************************************/
            /* = heliocentric position with Moshier ephemeris */
            /************************************
             * observer: geocenter or topocenter
             ************************************/
            /* if topocentric position is wanted  */
            if ((iflag & SwissEph.SEFLG_TOPOCTR) != 0) {
                if (swed.topd.teval != pedp.teval
                  || swed.topd.teval == 0) {
                      if (swi_get_observer(pedp.teval, iflag | SwissEph.SEFLG_NONUT, DO_SAVE, xobs, ref serr) != OK)
                        return ERR;
                } else {
                    for (i = 0; i <= 5; i++)
                        xobs[i] = swed.topd.xobs[i];
                }
                /* barycentric position of observer */
                for (i = 0; i <= 5; i++)
                    xobs[i] = xobs[i] + pedp.x[i];
            } else if ((iflag & SwissEph.SEFLG_BARYCTR) != 0) {
                for (i = 0; i <= 5; i++)
                    xobs[i] = 0;
            } else if ((iflag & SwissEph.SEFLG_HELCTR) != 0) {
                if ((iflag & SwissEph.SEFLG_MOSEPH) != 0) {
                    for (i = 0; i <= 5; i++)
                        xobs[i] = 0;
                } else {
                    for (i = 0; i <= 5; i++)
                        xobs[i] = psdp.x[i];
                }
            } else {
                for (i = 0; i <= 5; i++)
                    xobs[i] = pedp.x[i];
            }
            /*******************************
             * light-time                  *
             *******************************/
            if (0 == (iflag & SwissEph.SEFLG_TRUEPOS)) {
                niter = 1;
                if ((iflag & SwissEph.SEFLG_SPEED) != 0) {
                    /*
                     * Apparent speed is influenced by the fact that dt changes with
                     * motion. This makes a difference of several hundredths of an
                     * arc second. To take this into account, we compute
                     * 1. true position - apparent position at time t - 1.
                     * 2. true position - apparent position at time t.
                     * 3. the difference between the two is the daily motion resulting from
                     * the change of dt.
                     */
                    for (i = 0; i <= 2; i++)
                        xxsv[i] = xxsp[i] = xx[i] - xx[i + 3];
                    for (j = 0; j <= niter; j++) {
                        for (i = 0; i <= 2; i++) {
                            dx[i] = xxsp[i];
                            if (0 == (iflag & SwissEph.SEFLG_HELCTR) && 0 == (iflag & SwissEph.SEFLG_BARYCTR))
                                dx[i] -= (xobs[i] - xobs[i + 3]);
                        }
                        /* new dt */
                        dt = Math.Sqrt(square_sum(dx)) * AUNIT / CLIGHT / 86400.0;
                        for (i = 0; i <= 2; i++)
                            xxsp[i] = xxsv[i] - dt * pdp.x[i + 3];/* rough apparent position */
                    }
                    /* true position - apparent position at time t-1 */
                    for (i = 0; i <= 2; i++)
                        xxsp[i] = xxsv[i] - xxsp[i];
                }
                /* dt and t(apparent) */
                for (j = 0; j <= niter; j++) {
                    for (i = 0; i <= 2; i++) {
                        dx[i] = xx[i];
                        if (0 == (iflag & SwissEph.SEFLG_HELCTR) && 0 == (iflag & SwissEph.SEFLG_BARYCTR))
                            dx[i] -= xobs[i];
                    }
                    /* new dt */
                    dt = Math.Sqrt(square_sum(dx)) * AUNIT / CLIGHT / 86400.0;
                    dtsave_for_defl = dt;
                    /* new position: subtract t * speed
                     */
                    for (i = 0; i <= 2; i++) {
                        xx[i] = pdp.x[i] - dt * pdp.x[i + 3];/**/
                        xx[i + 3] = pdp.x[i + 3];
                    }
                }
                if ((iflag & SwissEph.SEFLG_SPEED) != 0) {
                    /* part of daily motion resulting from change of dt */
                    for (i = 0; i <= 2; i++)
                        xxsp[i] = pdp.x[i] - xx[i] - xxsp[i];
                    t = pdp.teval - dt;
                    /* for accuracy in speed, we will need earth as well */
                    retc = main_planet_bary(t, SEI_EARTH, epheflag, iflag, NO_SAVE, xearth, xearth, xsun, xmoon, ref serr);
                    if (SE.SwemPlan.swi_osc_el_plan(t, xx, ipl - SwissEph.SE_FICT_OFFSET, ipli, xearth, xsun, ref serr) != OK)
                        return ERR;
                    if (retc != OK)
                        return (retc);
                    if ((iflag & SwissEph.SEFLG_TOPOCTR) != 0) {
                        if (swi_get_observer(t, iflag | SwissEph.SEFLG_NONUT, NO_SAVE, xobs2, ref serr) != OK)
                            return ERR;
                        for (i = 0; i <= 5; i++)
                            xobs2[i] += xearth[i];
                    } else {
                        for (i = 0; i <= 5; i++)
                            xobs2[i] = xearth[i];
                    }
                }
            }
            /*******************************
             * conversion to geocenter     *
             *******************************/
            for (i = 0; i <= 5; i++)
                xx[i] -= xobs[i];
            if (0 == (iflag & SwissEph.SEFLG_TRUEPOS)) {
                /*
                 * Apparent speed is also influenced by
                 * the change of dt during motion.
                 * Neglect of this would result in an error of several 0.01"
                 */
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    for (i = 3; i <= 5; i++)
                        xx[i] -= xxsp[i - 3];
            }
            if (0 == (iflag & SwissEph.SEFLG_SPEED))
                for (i = 3; i <= 5; i++)
                    xx[i] = 0;
            /************************************
             * relativistic deflection of light *
             ************************************/
            if (0 == (iflag & SwissEph.SEFLG_TRUEPOS) && 0 == (iflag & SwissEph.SEFLG_NOGDEFL))
                /* SEFLG_NOGDEFL is on, if SEFLG_HELCTR or SEFLG_BARYCTR */
                swi_deflect_light(xx, dtsave_for_defl, iflag);
            /**********************************
             * 'annual' aberration of light   *
             **********************************/
            if (0 == (iflag & SwissEph.SEFLG_TRUEPOS) && 0 == (iflag & SwissEph.SEFLG_NOABERR)) {
                /* SEFLG_NOABERR is on, if SEFLG_HELCTR or SEFLG_BARYCTR */
                swi_aberr_light(xx, xobs, iflag);
                /*
                 * Apparent speed is also influenced by
                 * the difference of speed of the earth between t and t-dt.
                 * Neglecting this would involve an error of several 0.1"
                 */
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    for (i = 3; i <= 5; i++)
                        xx[i] += xobs[i] - xobs2[i];
            }
            /* save J2000 coordinates; required for sidereal positions */
            for (i = 0; i <= 5; i++)
                xxsv[i] = xx[i];
            /************************************************
             * precession, equator 2000 . equator of date *
             ************************************************/
            if (0 == (iflag & SwissEph.SEFLG_J2000)) {
                SE.SwephLib.swi_precess(xx, pdp.teval, iflag, J2000_TO_J);
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    swi_precess_speed(xx, pdp.teval, iflag, J2000_TO_J);
                oe = swed.oec;
            } else
                oe = swed.oec2000;
            return app_pos_rest(pdp, iflag, xx, xxsv, ref oe, ref serr);
        }

        /* transforms the position of the barycentric sun:
         * precession and nutation
         * according to flags
         * iflag	flags
         * serr         error string
         */
        int app_pos_etc_sbar(Int32 iflag, ref string serr)
        {
            int i;
            double[] xx = new double[6], xxsv = new double[6]; double dt;
            plan_data psdp = swed.pldat[SEI_EARTH];
            plan_data psbdp = swed.pldat[SEI_SUNBARY];
            epsilon oe = swed.oec;
            /* the conversions will be done with xx[]. */
            for (i = 0; i <= 5; i++)
                xx[i] = psbdp.x[i];
            /**************
             * light-time *
             **************/
            if (0 == (iflag & SwissEph.SEFLG_TRUEPOS)) {
                dt = Math.Sqrt(square_sum(xx)) * AUNIT / CLIGHT / 86400.0;
                for (i = 0; i <= 2; i++)
                    xx[i] -= dt * xx[i + 3];	/* apparent position */
            }
            if (0 == (iflag & SwissEph.SEFLG_SPEED))
                for (i = 3; i <= 5; i++)
                    xx[i] = 0;
            /* ICRS to J2000 */
            if (0 == (iflag & SwissEph.SEFLG_ICRS) && get_denum(SEI_SUN, iflag) >= 403) {
                SE.SwephLib.swi_bias(xx, psdp.teval, iflag, false);
            }/**/
            /* save J2000 coordinates; required for sidereal positions */
            for (i = 0; i <= 5; i++)
                xxsv[i] = xx[i];
            /************************************************
             * precession, equator 2000 -> equator of date *
             ************************************************/
            if (0 == (iflag & SwissEph.SEFLG_J2000)) {
                SE.SwephLib.swi_precess(xx, psbdp.teval, iflag, J2000_TO_J);
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    swi_precess_speed(xx, psbdp.teval, iflag, J2000_TO_J);
                oe = swed.oec;
            } else
                oe = swed.oec2000;
            return app_pos_rest(psdp, iflag, xx, xxsv, ref oe, ref serr);
        }

        /* converts the sun from barycentric to geocentric,
         *          the earth from barycentric to heliocentric
         * computes
         * apparent position,
         * precession, and nutation
         * according to flags
         * iflag	flags
         * serr         error string
         */
        int app_pos_etc_sun(Int32 iflag, ref string serr)
        {
            int i, j, niter, retc = OK;
            Int32 flg1, flg2;
            double[] xx = new double[6], xxsv = new double[6], dx = new double[3]; double dt = 0, t = 0;
            double[] xearth = new double[6], xsun = new double[6], xobs = new double[6];
            plan_data pedp = swed.pldat[SEI_EARTH];
            plan_data psdp = swed.pldat[SEI_SUNBARY];
            epsilon oe = swed.oec2000;
            /* if the same conversions have already been done for the same
             * date, then return */
            flg1 = iflag & ~SwissEph.SEFLG_EQUATORIAL & ~SwissEph.SEFLG_XYZ;
            flg2 = pedp.xflgs & ~SwissEph.SEFLG_EQUATORIAL & ~SwissEph.SEFLG_XYZ;
            if (flg1 == flg2) {
                pedp.xflgs = iflag;
                pedp.iephe = iflag & SwissEph.SEFLG_EPHMASK;
                return OK;
            }
            /************************************
             * observer: geocenter or topocenter
             ************************************/
            /* if topocentric position is wanted  */
            if ((iflag & SwissEph.SEFLG_TOPOCTR) != 0) {
                if (swed.topd.teval != pedp.teval
                  || swed.topd.teval == 0) {
                      if (swi_get_observer(pedp.teval, iflag | SwissEph.SEFLG_NONUT, DO_SAVE, xobs, ref serr) != OK)
                        return ERR;
                } else {
                    for (i = 0; i <= 5; i++)
                        xobs[i] = swed.topd.xobs[i];
                }
                /* barycentric position of observer */
                for (i = 0; i <= 5; i++)
                    xobs[i] = xobs[i] + pedp.x[i];
            } else {
                /* barycentric position of geocenter */
                for (i = 0; i <= 5; i++)
                    xobs[i] = pedp.x[i];
            }
            /***************************************
             * true heliocentric position of earth *
             ***************************************/
            if (pedp.iephe == SwissEph.SEFLG_MOSEPH || (iflag & SwissEph.SEFLG_BARYCTR) != 0)
                for (i = 0; i <= 5; i++)
                    xx[i] = xobs[i];
            else
                for (i = 0; i <= 5; i++)
                    xx[i] = xobs[i] - psdp.x[i];
            /*******************************
             * light-time                  *
             *******************************/
            if (0 == (iflag & SwissEph.SEFLG_TRUEPOS)) {
                /* number of iterations - 1
                 * the following if() does the following:
                 * with jpl and swiss ephemeris:
                 *   with geocentric computation of sun:
                 *     light-time correction of barycentric sun position.
                 *   with heliocentric or barycentric computation of earth:
                 *     light-time correction of barycentric earth position.
                 * with moshier ephemeris (heliocentric!!!):
                 *   with geocentric computation of sun:
                 *     nothing! (aberration will be done later)
                 *   with heliocentric or barycentric computation of earth:
                 *     light-time correction of heliocentric earth position.
                 */
                if (pedp.iephe == SwissEph.SEFLG_JPLEPH || pedp.iephe == SwissEph.SEFLG_SWIEPH
                  || (iflag & SwissEph.SEFLG_HELCTR) != 0 || (iflag & SwissEph.SEFLG_BARYCTR) != 0) {
                    for (i = 0; i <= 5; i++) {
                        xearth[i] = xobs[i];
                        if (pedp.iephe == SwissEph.SEFLG_MOSEPH)
                            xsun[i] = 0;
                        else
                            xsun[i] = psdp.x[i];
                    }
                    niter = 1;	/* # of iterations */
                    for (j = 0; j <= niter; j++) {
                        /* distance earth-sun */
                        for (i = 0; i <= 2; i++) {
                            dx[i] = xearth[i];
                            if (0 == (iflag & SwissEph.SEFLG_BARYCTR))
                                dx[i] -= xsun[i];
                        }
                        /* new t */
                        dt = Math.Sqrt(square_sum(dx)) * AUNIT / CLIGHT / 86400.0;
                        t = pedp.teval - dt;
                        /* new position */
                        switch (pedp.iephe) {
                            /* if geocentric sun, new sun at t'
                             * if heliocentric or barycentric earth, new earth at t' */
                            case SwissEph.SEFLG_JPLEPH:
                                if ((iflag & SwissEph.SEFLG_HELCTR) != 0 || (iflag & SwissEph.SEFLG_BARYCTR) != 0)
                                    retc = SE.SweJPL.swi_pleph(t, SweJPL.J_EARTH, SweJPL.J_SBARY, xearth, ref serr);
                                else
                                    retc = SE.SweJPL.swi_pleph(t, SweJPL.J_SUN, SweJPL.J_SBARY, xsun, ref serr);
                                if (retc != OK) {
                                    SE.SweJPL.swi_close_jpl_file();
                                    swed.jpl_file_is_open = false;
                                    return (retc);
                                }
                                break;
                            case SwissEph.SEFLG_SWIEPH:
                                /*
                                  retc = sweph(t, SEI_SUN, SEI_FILE_PLANET, iflag, NULL, NO_SAVE, xearth, serr);
                                */
                                if ((iflag & SwissEph.SEFLG_HELCTR) != 0 || (iflag & SwissEph.SEFLG_BARYCTR) != 0)
                                    retc = sweplan(t, SEI_EARTH, SEI_FILE_PLANET, iflag, NO_SAVE, xearth, null, xsun, null, ref serr);
                                else
                                    retc = sweph(t, SEI_SUNBARY, SEI_FILE_PLANET, iflag, null, NO_SAVE, xsun, ref serr);
                                break;
                            case SwissEph.SEFLG_MOSEPH:
                                if ((iflag & SwissEph.SEFLG_HELCTR) != 0 || (iflag & SwissEph.SEFLG_BARYCTR) != 0)
                                    retc = SE.SwemPlan.swi_moshplan(t, SEI_EARTH, NO_SAVE, xearth, xearth, ref serr);
                                /* with moshier there is no barycentric sun */
                                break;
                            default:
                                retc = ERR;
                                break;
                        }
                        if (retc != OK)
                            return (retc);
                    }
                    /* apparent heliocentric earth */
                    for (i = 0; i <= 5; i++) {
                        xx[i] = xearth[i];
                        if (0 == (iflag & SwissEph.SEFLG_BARYCTR))
                            xx[i] -= xsun[i];
                    }
                }
            }
            if (0 == (iflag & SwissEph.SEFLG_SPEED))
                for (i = 3; i <= 5; i++)
                    xx[i] = 0;
            /*******************************
             * conversion to geocenter     *
             *******************************/
            if (0 == (iflag & SwissEph.SEFLG_HELCTR) && 0 == (iflag & SwissEph.SEFLG_BARYCTR))
                for (i = 0; i <= 5; i++)
                    xx[i] = -xx[i];
            /**********************************
             * 'annual' aberration of light   *
             **********************************/
            if (0 == (iflag & SwissEph.SEFLG_TRUEPOS) && 0 == (iflag & SwissEph.SEFLG_NOABERR)) {
                /* SEFLG_NOABERR is on, if SEFLG_HELCTR or SEFLG_BARYCTR */
                swi_aberr_light(xx, xobs, iflag);
            }
            if (0 == (iflag & SwissEph.SEFLG_SPEED))
                for (i = 3; i <= 5; i++)
                    xx[i] = 0;
            /* ICRS to J2000 */
            if (0 == (iflag & SwissEph.SEFLG_ICRS) && get_denum(SEI_SUN, iflag) >= 403) {
                SE.SwephLib.swi_bias(xx, t, iflag, false);
            }/**/
            /* save J2000 coordinates; required for sidereal positions */
            for (i = 0; i <= 5; i++)
                xxsv[i] = xx[i];
            /************************************************
             * precession, equator 2000 -> equator of date *
             ************************************************/
            if (0 == (iflag & SwissEph.SEFLG_J2000)) {
                SE.SwephLib.swi_precess(xx, pedp.teval, iflag, J2000_TO_J);/**/
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    swi_precess_speed(xx, pedp.teval, iflag, J2000_TO_J);/**/
                oe = swed.oec;
            } else
                oe = swed.oec2000;
            return app_pos_rest(pedp, iflag, xx, xxsv, ref oe, ref serr);
        }

        int app_pos_rest(plan_data pdp, Int32 iflag,
                               CPointer<double> xx, CPointer<double> x2000,
                               ref epsilon oe, ref string serr)
        {
            int i;
            double daya;
            double[] xxsv = new double[24];
            /************************************************
             * nutation                                     *
             ************************************************/
            if (0 == (iflag & SwissEph.SEFLG_NONUT))
                swi_nutate(xx, iflag, false);
            /* now we have equatorial cartesian coordinates; save them */
            for (i = 0; i <= 5; i++)
                pdp.xreturn[18 + i] = xx[i];
            /************************************************
             * transformation to ecliptic.                  *
             * with sidereal calc. this will be overwritten *
             * afterwards.                                  *
             ************************************************/
            SE.SwephLib.swi_coortrf2(xx, xx, oe.seps, oe.ceps);
            if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                SE.SwephLib.swi_coortrf2(xx + 3, xx + 3, oe.seps, oe.ceps);
            if (0 == (iflag & SwissEph.SEFLG_NONUT)) {
                SE.SwephLib.swi_coortrf2(xx, xx, swed.nut.snut, swed.nut.cnut);
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    SE.SwephLib.swi_coortrf2(xx + 3, xx + 3, swed.nut.snut, swed.nut.cnut);
            }
            /* now we have ecliptic cartesian coordinates */
            for (i = 0; i <= 5; i++)
                pdp.xreturn[6 + i] = xx[i];
            /************************************
             * sidereal positions               *
             ************************************/
            if ((iflag & SwissEph.SEFLG_SIDEREAL) != 0) {
                /* project onto ecliptic t0 */
                if ((swed.sidd.sid_mode & SwissEph.SE_SIDBIT_ECL_T0) != 0) {
                    if (swi_trop_ra2sid_lon(x2000, pdp.xreturn.GetPointer(6), pdp.xreturn.GetPointer(18), iflag) != OK)
                        return ERR;
                    /* project onto solar system equator */
                } else if ((swed.sidd.sid_mode & SwissEph.SE_SIDBIT_SSY_PLANE) != 0) {
                    if (swi_trop_ra2sid_lon_sosy(x2000, pdp.xreturn.GetPointer(6), iflag) != OK)
                        return ERR;
                } else {
                    /* traditional algorithm */
                    SE.SwephLib.swi_cartpol_sp(pdp.xreturn.GetPointer(6), pdp.xreturn);
                    /* note, swe_get_ayanamsa_ex() disturbs present calculations, if sun is calculated with
                     * TRUE_CHITRA ayanamsha, because the ayanamsha also calculates the sun.
                     * Therefore current values are saved... */
                    for (i = 0; i < 24; i++)
                        xxsv[i] = pdp.xreturn[i];
                    if (SE.swe_get_ayanamsa_ex(pdp.teval, iflag, out daya, ref serr) == ERR)
                        return ERR;
                    /* ... and restored */
                    for (i = 0; i < 24; i++)
                        pdp.xreturn[i] = xxsv[i];
                    pdp.xreturn[0] -= daya * SwissEph.DEGTORAD;
                    SE.SwephLib.swi_polcart_sp(pdp.xreturn, pdp.xreturn.GetPointer(6));
                }
            }
            /************************************************
             * transformation to polar coordinates          *
             ************************************************/
            SE.SwephLib.swi_cartpol_sp(pdp.xreturn.GetPointer(18), pdp.xreturn.GetPointer(12));
            SE.SwephLib.swi_cartpol_sp(pdp.xreturn.GetPointer(6), pdp.xreturn);
            /**********************
             * radians to degrees *
             **********************/
            /*if ((iflag & SEFLG_RADIANS) == 0) {*/
            for (i = 0; i < 2; i++) {
                pdp.xreturn[i] *= SwissEph.RADTODEG;		/* ecliptic */
                pdp.xreturn[i + 3] *= SwissEph.RADTODEG;
                pdp.xreturn[i + 12] *= SwissEph.RADTODEG;	/* equator */
                pdp.xreturn[i + 15] *= SwissEph.RADTODEG;
            }
            /*pdp.xreturn[12] -= (0.053 / 3600.0); */
            /*}*/
            /* save, what has been done */
            pdp.xflgs = iflag;
            pdp.iephe = iflag & SwissEph.SEFLG_EPHMASK;
            return OK;
        }

        /* calculates obliquity of ecliptic and stores it together
         * with its date, sine, and cosine
         */
        void calc_epsilon(double tjd, Int32 iflag, epsilon e)
        {
            e.teps = tjd;
            e.eps = SE.SwephLib.swi_epsiln(tjd, iflag);
            e.seps = Math.Sin(e.eps);
            e.ceps = Math.Cos(e.eps);
        }

        void calc_speed(CPointer<double> x0, CPointer<double> x1, CPointer<double> x2, double dt)
        {
            int i, j, k;
            double a, b;
            for (j = 0; j <= 18; j += 6) {
                for (i = 0; i < 3; i++) {
                    k = j + i;
                    b = (x2[k] - x0[k]) / 2;
                    a = (x2[k] + x0[k]) / 2 - x1[k];
                    x1[k + 3] = (2 * a + b) / dt;
                }
            }
        }

        void denormalize_positions(CPointer<double> x0, CPointer<double> x1, CPointer<double> x2)
        {
            int i;
            /* x*[0] = ecliptic longitude, x*[12] = rectascension */
            for (i = 0; i <= 12; i += 12) {
                if (x1[i] - x0[i] < -180)
                    x0[i] -= 360;
                if (x1[i] - x0[i] > 180)
                    x0[i] += 360;
                if (x1[i] - x2[i] < -180)
                    x2[i] -= 360;
                if (x1[i] - x2[i] > 180)
                    x2[i] += 360;
            }
        }

        /* SWISSEPH
         * reads from a file and, if necessary, reorders bytes
         * targ 	target pointer
         * size		size of item to be read
         * count	number of items
         * corrsize	in what size should it be returned
         *		(e.g. 3 byte int -> 4 byte int)
         * fp		file pointer
         * fpos		file position: if (fpos >= 0) then fseek
         * freord	reorder bytes or no
         * fendian	little/bigendian
         * ifno		file number
         * serr		error string
         */
        int do_fread(byte[] trg, int size, int count, int corrsize, ref CFile fp, Int32 fpos, int freord, int fendian, int ifno, ref string serr)
        {
            int i, j, k;
            int totsize;
            byte[] space = new byte[1000];
            CPointer<byte> targ = trg;
            totsize = size * count;
            if (fpos >= 0)
                fp.Seek(fpos, SeekOrigin.Begin);
            /* if no byte reorder has to be done, and read size == return size */
            if (0 == freord && size == corrsize) {
                if (fp.Read(trg, 0, totsize) == 0) {
                    serr = C.sprintf("Ephemeris file %s is damaged (2).", swed.fidat[ifno].fnam);
                    return (ERR);
                } else
                    return (OK);
            } else {
                if (fp.Read(space, 0, totsize) == 0) {
                    serr = C.sprintf("Ephemeris file %s is damaged (4).", swed.fidat[ifno].fnam);
                    return (ERR);
                }
                if (size != corrsize) {
                    for (int ii = 0; ii < count * corrsize; ii++) {
                        targ[ii] = 0;
                    }
                }
                for (i = 0; i < count; i++) {
                    for (j = size - 1; j >= 0; j--) {
                        if (freord != 0)
                            k = size - j - 1;
                        else
                            k = j;
                        if (size != corrsize)
                            if ((fendian == SEI_FILE_BIGENDIAN && 0 == freord) ||
                                (fendian == SEI_FILE_LITENDIAN && freord != 0))
                                k += corrsize - size;
                        targ[i * corrsize + k] = space[i * size + j];
                    }
                }
            }
            return (OK);
        }

        int do_fread(CPointer<char> trg, int size, int count, int corrsize, ref CFile fp, Int32 fpos, int freord, int fendian, int ifno, ref string serr)
        {
            var buff = new byte[count * corrsize];
            var res = do_fread(buff, size, count, corrsize, ref fp, fpos, freord, fendian, ifno, ref serr);
            for (int i = 0; i < count * corrsize; i++) {
                trg[i] = (char)buff[i];
            }
            return res;
        }

        int do_fread(CPointer<uint> trg, int size, int count, int corrsize, ref CFile fp, Int32 fpos, int freord, int fendian, int ifno, ref string serr)
        {
            var buff = new byte[count * corrsize];
            var res = do_fread(buff, size, count, corrsize, ref fp, fpos, freord, fendian, ifno, ref serr);
            for (int i = 0; i < count; i++) {
                trg[i] = BitConverter.ToUInt32(buff, i * corrsize);
            }
            return res;
        }

        int do_fread(CPointer<int> trg, int size, int count, int corrsize, ref CFile fp, Int32 fpos, int freord, int fendian, int ifno, ref string serr)
        {
            var buff = new byte[count * corrsize];
            var res = do_fread(buff, size, count, corrsize, ref fp, fpos, freord, fendian, ifno, ref serr);
            for (int i = 0; i < count; i++) {
                trg[i] = BitConverter.ToInt32(buff, i * corrsize);
            }
            return res;
        }

        int do_fread(CPointer<double> trg, int size, int count, int corrsize, ref CFile fp, Int32 fpos, int freord, int fendian, int ifno, ref string serr)
        {
            var buff = new byte[count * corrsize];
            var res = do_fread(buff, size, count, corrsize, ref fp, fpos, freord, fendian, ifno, ref serr);
            for (int i = 0; i < count; i++) {
                trg[i] = BitConverter.ToDouble(buff, i * corrsize);
            }
            return res;
        }

        int do_fread(ref short trg, int size, int count, int corrsize, ref CFile fp, Int32 fpos, int freord, int fendian, int ifno, ref string serr)
        {
            byte[] buff = BitConverter.GetBytes(trg);
            var res = do_fread(buff, size, count, corrsize, ref fp, fpos, freord, fendian, ifno, ref serr);
            trg = BitConverter.ToInt16(buff, 0);
            return res;
        }

        int do_fread(ref Int32 trg, int size, int count, int corrsize, ref CFile fp, Int32 fpos, int freord, int fendian, int ifno, ref string serr)
        {
            byte[] buff = BitConverter.GetBytes(trg);
            var res = do_fread(buff, size, count, corrsize, ref fp, fpos, freord, fendian, ifno, ref serr);
            trg = BitConverter.ToInt32(buff, 0);
            return res;
        }

        int do_fread(ref UInt32 trg, int size, int count, int corrsize, ref CFile fp, Int32 fpos, int freord, int fendian, int ifno, ref string serr)
        {
            byte[] buff = BitConverter.GetBytes(trg);
            var res = do_fread(buff, size, count, corrsize, ref fp, fpos, freord, fendian, ifno, ref serr);
            trg = BitConverter.ToUInt32(buff, 0);
            return res;
        }

        int do_fread(ref Double trg, int size, int count, int corrsize, ref CFile fp, Int32 fpos, int freord, int fendian, int ifno, ref string serr)
        {
            byte[] buff = BitConverter.GetBytes(trg);
            var res = do_fread(buff, size, count, corrsize, ref fp, fpos, freord, fendian, ifno, ref serr);
            trg = BitConverter.ToDouble(buff, 0);
            return res;
        }

        /* Adjust position from Earth-Moon barycenter to Earth
         *
         * xemb = hel./bar. position or velocity vectors of emb (input)
         *                                                  earth (output)
         * xmoon= geocentric position or velocity vector of moon
         */
        void embofs(CPointer<double> xemb, CPointer<double> xmoon)
        {
            int i;
            for (i = 0; i <= 2; i++)
                xemb[i] -= xmoon[i] / (EARTH_MOON_MRAT + 1.0);
        }

        void free_planets()
        {
            int i;
            /* free planets data space */
            for (i = 0; i < Sweph.SEI_NPLANETS; i++)
            {
                if (swed.pldat[i].segp != null)
                {
                    swed.pldat[i].segp = null;
                }
                if (swed.pldat[i].refep != null)
                {
                    swed.pldat[i].refep = null;
                }
                swed.pldat[i] = new plan_data();
            }
            for (i = 0; i <= SwissEph.SE_NPLANETS; i++) /* "<=" is correct! see decl. */
                swed.savedat[i] = new save_positions();
            /* clear node data space */
            for (i = 0; i < Sweph.SEI_NNODE_ETC; i++)
            {
                //#if 0
                //    memset((void *) &swed.nddat[i], 0, sizeof(struct node_data));
                //#else
                swed.nddat[i] = new plan_data();
                //#endif
            }
        }

        Int32 get_denum(Int32 ipli, Int32 iflag)
        {
            file_data fdp = null;
            if ((iflag & SwissEph.SEFLG_MOSEPH) != 0)
                return 403;
            if ((iflag & SwissEph.SEFLG_JPLEPH) != 0)
            {
                if (swed.jpldenum > 0)
                    return swed.jpldenum;
                else
                    return SwissEph.SE_DE_NUMBER;
            }
            if (ipli > SwissEph.SE_AST_OFFSET)
            {
                fdp = swed.fidat[SEI_FILE_ANY_AST];
            }
            else if (ipli == SEI_CHIRON
              || ipli == SEI_PHOLUS
              || ipli == SEI_CERES
              || ipli == SEI_PALLAS
              || ipli == SEI_JUNO
              || ipli == SEI_VESTA)
            {
                fdp = swed.fidat[SEI_FILE_MAIN_AST];
            }
            else if (ipli == SEI_MOON)
            {
                fdp = swed.fidat[SEI_FILE_MOON];
            }
            else
            {
                fdp = swed.fidat[SEI_FILE_PLANET];
            }
            if (fdp != null)
            {
                if (fdp.sweph_denum != 0)
                    return fdp.sweph_denum;
                else
                    return SwissEph.SE_DE_NUMBER;
            }
            return SwissEph.SE_DE_NUMBER;
        }

        /* fetch chebyshew coefficients from sweph file for
         * tjd 		time
         * ipli		planet number
         * ifno		file number
         * serr		error string
         */
        int get_new_segment(double tjd, int ipli, int ifno, ref string serr)
        {
            int i, j, k, m, n, o, icoord, retc;
            Int32 iseg;
            Int32 fpos;
            int nsizes; int[] nsize = new int[6];
            int nco;
            int idbl;
            char[] c = new char[4];
            plan_data pdp = swed.pldat[ipli];
            file_data fdp = swed.fidat[ifno];
            CFile fp = fdp.fptr;
            int freord = (int)fdp.iflg & SEI_FILE_REORD;
            int fendian = (int)fdp.iflg & SEI_FILE_LITENDIAN;
            UInt32[] longs = new UInt32[MAXORD + 1];
            /* compute segment number */
            iseg = (Int32)((tjd - pdp.tfstart) / pdp.dseg);
            /*if (tjd - pdp.tfstart < 0)
                return(NOT_AVAILABLE);*/
            pdp.tseg0 = pdp.tfstart + iseg * pdp.dseg;
            pdp.tseg1 = pdp.tseg0 + pdp.dseg;
            /* get file position of coefficients from file */
            fpos = pdp.lndx0 + iseg * 3;
            retc = do_fread(ref fpos, 3, 1, 4, ref fp, fpos, freord, fendian, ifno, ref serr);
            if (retc != OK)
                goto return_error_gns;
            fp.Seek(fpos, SeekOrigin.Begin);
            /* clear space of chebyshew coefficients */
            if (pdp.segp == null)
                pdp.segp = new double[pdp.ncoe * 3];
            //memset((void*)pdp.segp, 0, (size_t)pdp.ncoe * 3 * 8);
            for (int ii = 0; ii < pdp.segp.Length; ii++) {
                pdp.segp[ii] = 0;
            }
            /* read coefficients for 3 coordinates */
            for (icoord = 0; icoord < 3; icoord++) {
                idbl = icoord * pdp.ncoe;
                /* first read header */
                /* first bit indicates number of sizes of packed coefficients */
                retc = do_fread(c, 1, 2, 1, ref fp, SEI_CURR_FPOS, freord, fendian, ifno, ref serr);
                if (retc != OK)
                    goto return_error_gns;
                if ((c[0] & 128) != 0)
                {
                    nsizes = 6;
                    retc = do_fread(c.GetPointer(2), 1, 2, 1, ref fp, SEI_CURR_FPOS, freord, fendian, ifno, ref serr);
                    if (retc != OK)
                        goto return_error_gns;
                    nsize[0] = (int)c[1] / 16;
                    nsize[1] = (int)c[1] % 16;
                    nsize[2] = (int)c[2] / 16;
                    nsize[3] = (int)c[2] % 16;
                    nsize[4] = (int)c[3] / 16;
                    nsize[5] = (int)c[3] % 16;
                    nco = nsize[0] + nsize[1] + nsize[2] + nsize[3] + nsize[4] + nsize[5];
                } else {
                    nsizes = 4;
                    nsize[0] = (int)c[0] / 16;
                    nsize[1] = (int)c[0] % 16;
                    nsize[2] = (int)c[1] / 16;
                    nsize[3] = (int)c[1] % 16;
                    nco = nsize[0] + nsize[1] + nsize[2] + nsize[3];
                }
                /* there may not be more coefficients than interpolation
                 * order + 1 */
                if (nco > pdp.ncoe) {
                    //serr=C.sprintf("error in ephemeris file: %d coefficients instead of %d. ", nco, pdp.ncoe);
                    //if (strlen(serr) + strlen(fdp.fnam) < AS_MAXCH - 1) {
                    serr = C.sprintf("error in ephemeris file %s: %d coefficients instead of %d. ", fdp.fnam, nco, pdp.ncoe);
                    //}
                    //free(pdp.segp);
                    pdp.segp = null;
                    return (ERR);
                }
                /* now unpack */
                for (i = 0; i < nsizes; i++) {
                    if (nsize[i] == 0)
                        continue;
                    if (i < 4) {
                        j = (4 - i);
                        k = nsize[i];
                        retc = do_fread(longs, j, k, 4, ref fp, SEI_CURR_FPOS, freord, fendian, ifno, ref serr);
                        if (retc != OK)
                            goto return_error_gns;
                        for (m = 0; m < k; m++, idbl++)
                        {
                            if ((longs[m] & 1) != 0) 	/* will be negative */
                                pdp.segp[idbl] = -(((longs[m] + 1) / 2) / 1e+9 * pdp.rmax / 2);
                            else
                                pdp.segp[idbl] = (longs[m] / 2) / 1e+9 * pdp.rmax / 2;
                        }
                    } else if (i == 4) {		/* half byte packing */
                        j = 1;
                        k = (nsize[i] + 1) / 2;
                        retc = do_fread(longs, j, k, 4, ref fp, SEI_CURR_FPOS, freord, fendian, ifno, ref serr);
                        if (retc != OK)
                            goto return_error_gns;
                        for (m = 0, j = 0;
                             m < k && j < nsize[i];
                             m++) {
                            for (n = 0, o = 16;
                                 n < 2 && j < nsize[i];
                                 n++, j++, idbl++, longs[m] %= (uint)o, o /= 16) {
                                if ((longs[m] & o) != 0)
                                    pdp.segp[idbl] =
                                     -(((longs[m] + o) / o / 2) * pdp.rmax / 2 / 1e+9);
                                else
                                    pdp.segp[idbl] = (longs[m] / o / 2) * pdp.rmax / 2 / 1e+9;
                            }
                        }
                    } else if (i == 5) {		/* quarter byte packing */
                        j = 1;
                        k = (nsize[i] + 3) / 4;
                        retc = do_fread(longs, j, k, 4, ref fp, SEI_CURR_FPOS, freord, fendian, ifno, ref serr);
                        if (retc != OK)
                            return (retc);
                        for (m = 0, j = 0;
                             m < k && j < nsize[i];
                             m++) {
                            for (n = 0, o = 64;
                                 n < 4 && j < nsize[i];
                                 n++, j++, idbl++, longs[m] %= (uint)o, o /= 4) {
                                if ((longs[m] & o) != 0)
                                    pdp.segp[idbl] =
                                     -(((longs[m] + o) / o / 2) * pdp.rmax / 2 / 1e+9);
                                else
                                    pdp.segp[idbl] = (longs[m] / o / 2) * pdp.rmax / 2 / 1e+9;
                            }
                        }
                    }
                }
            }
            //#if 0
            //  if (ipli == SEI_SUNBARY) {
            //    printf("%d, %x\n", fpos, fpos);
            //    for (i = 0; i < pdp.ncoe; i++)
            //      printf("%e, %e, %e\n", pdp.segp[i], pdp.segp[i+pdp.ncoe], pdp.segp[i+2*pdp.ncoe]);
            //  }
            //#endif
            return (OK);
            return_error_gns:
            fdp.fptr.Dispose();
            fdp.fptr = null;
            free_planets();
            return ERR;
        }

        /* lunar osculating elements, i.e.
         */
        int intp_apsides(double tjd, int ipl, Int32 iflag, ref string serr)
        {
            int i;
            Int32 flg1, flg2;
            plan_data ndp;
            epsilon oe;
            nut nut;
            double daya;
            double speed_intv = 0.1;
            double t, dt;
            double[][] xpos = CreateArray<Double>(3, 6); double[] xx = new double[6], x = new double[6];
            Int32 speedf1, speedf2;
            oe = swed.oec;
            nut = swed.nut;
            ndp = swed.nddat[ipl];
            /* if same calculation was done before, return
             * if speed flag has been turned on, recompute */
            flg1 = iflag & ~SwissEph.SEFLG_EQUATORIAL & ~SwissEph.SEFLG_XYZ;
            flg2 = ndp.xflgs & ~SwissEph.SEFLG_EQUATORIAL & ~SwissEph.SEFLG_XYZ;
            speedf1 = ndp.xflgs & SwissEph.SEFLG_SPEED;
            speedf2 = iflag & SwissEph.SEFLG_SPEED;
            if (tjd == ndp.teval
              && tjd != 0
              && flg1 == flg2
              && (0 == speedf2 || speedf1 != 0)) {
                ndp.xflgs = iflag;
                ndp.iephe = iflag & SwissEph.SEFLG_MOSEPH;
                return OK;
            }
            /*********************************************
             * now three apsides *
             *********************************************/
            for (t = tjd - speed_intv, i = 0; i < 3; t += speed_intv, i++) {
                if (0 == (iflag & SwissEph.SEFLG_SPEED) && i != 1) continue;
                SE.SwemMoon.swi_intp_apsides(t, xpos[i], ipl);
            }
            /************************************************************
             * apsis with speed                                         *
             ************************************************************/
            for (i = 0; i < 3; i++) {
                xx[i] = xpos[1][i];
                xx[i + 3] = 0;
            }
            if ((iflag & SwissEph.SEFLG_SPEED) != 0) {
                xx[3] = SE.swe_difrad2n(xpos[2][0], xpos[0][0]) / speed_intv / 2.0;
                xx[4] = (xpos[2][1] - xpos[0][1]) / speed_intv / 2.0;
                xx[5] = (xpos[2][2] - xpos[0][2]) / speed_intv / 2.0;
            }
            //memset((void*)ndp.xreturn, 0, 24 * sizeof(double));
            for (int ii = 0; ii < 24; ii++) {
                ndp.xreturn[ii] = 0;
            }
            /* ecliptic polar to cartesian */
            SE.SwephLib.swi_polcart_sp(xx, xx);
            /* light-time */
            if (0 == (iflag & SwissEph.SEFLG_TRUEPOS)) {
                dt = Math.Sqrt(square_sum(xx)) * AUNIT / CLIGHT / 86400.0;
                for (i = 1; i < 3; i++)
                    xx[i] -= dt * xx[i + 3];
            }
            for (i = 0; i <= 5; i++)
                ndp.xreturn[i + 6] = xx[i];
            /*printf("%.10f, %.10f, %.10f, %.10f\n", xx[0] /DEGTORAD, xx[1] / DEGTORAD, xx [2], xx[3] /DEGTORAD);*/
            /* equatorial cartesian */
            SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(6), ndp.xreturn.GetPointer(18), -oe.seps, oe.ceps);
            if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(9), ndp.xreturn.GetPointer(21), -oe.seps, oe.ceps);
            ndp.teval = tjd;
            ndp.xflgs = iflag;
            ndp.iephe = iflag & SwissEph.SEFLG_EPHMASK;
            if ((iflag & SwissEph.SEFLG_SIDEREAL) != 0) {
                /* apogee is referred to t;
                 * the ecliptic position must be transformed to t0 */
                /* rigorous algorithm */
                if ((swed.sidd.sid_mode & SwissEph.SE_SIDBIT_ECL_T0) != 0
                || (swed.sidd.sid_mode & SwissEph.SE_SIDBIT_SSY_PLANE) != 0) {
                    for (i = 0; i <= 5; i++)
                        x[i] = ndp.xreturn[18 + i];
                    /* precess to J2000 */
                    SE.SwephLib.swi_precess(x, tjd, iflag, J_TO_J2000);
                    if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                        swi_precess_speed(x, tjd, iflag, J_TO_J2000);
                    if ((swed.sidd.sid_mode & SwissEph.SE_SIDBIT_ECL_T0) != 0)
                        swi_trop_ra2sid_lon(x, ndp.xreturn.GetPointer(6), ndp.xreturn.GetPointer(18), iflag);
                    /* project onto solar system equator */
                    else if ((swed.sidd.sid_mode & SwissEph.SE_SIDBIT_SSY_PLANE) != 0)
                        swi_trop_ra2sid_lon_sosy(x, ndp.xreturn.GetPointer(6), iflag);
                    /* to polar */
                    SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(6), ndp.xreturn);
                    SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(12));
                } else {
                    /* traditional algorithm */
                    SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(6), ndp.xreturn);
                    if (SE.swe_get_ayanamsa_ex(ndp.teval, iflag, out daya, ref serr) == ERR)
                        return ERR;
                    ndp.xreturn[0] -= daya * SwissEph.DEGTORAD;
                    SE.SwephLib.swi_polcart_sp(ndp.xreturn, ndp.xreturn.GetPointer(6));
                    SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(12));
                }
            } else if ((iflag & SwissEph.SEFLG_J2000) != 0) {
                /* node and apogee are referred to t;
                 * the ecliptic position must be transformed to J2000 */
                for (i = 0; i <= 5; i++)
                    x[i] = ndp.xreturn[18 + i];
                /* precess to J2000 */
                SE.SwephLib.swi_precess(x, tjd, iflag, J_TO_J2000);
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    swi_precess_speed(x, tjd, iflag, J_TO_J2000);
                for (i = 0; i <= 5; i++)
                    ndp.xreturn[18 + i] = x[i];
                SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(12));
                SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(6), swed.oec2000.seps, swed.oec2000.ceps);
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(21), ndp.xreturn.GetPointer(9), swed.oec2000.seps, swed.oec2000.ceps);
                SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(6), ndp.xreturn);
            } else {
                /* tropical ecliptic positions */
                /* precession has already been taken into account, but not nutation */
                if (0 == (iflag & SwissEph.SEFLG_NONUT)) {
                    swi_nutate(ndp.xreturn.GetPointer(18), iflag, false);
                }
                /* equatorial polar */
                SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(12));
                /* ecliptic cartesian */
                SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(6), oe.seps, oe.ceps);
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(21), ndp.xreturn.GetPointer(9), oe.seps, oe.ceps);
                if (0 == (iflag & SwissEph.SEFLG_NONUT)) {
                    SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(6), ndp.xreturn.GetPointer(6), nut.snut, nut.cnut);
                    if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                        SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(9), ndp.xreturn.GetPointer(9), nut.snut, nut.cnut);
                }
                /* ecliptic polar */
                SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(6), ndp.xreturn);
            }
            /**********************
             * radians to degrees *
             **********************/
            /*if (!(iflag & SwissEph.SEFLG_RADIANS)) {*/
            for (i = 0; i < 2; i++) {
                ndp.xreturn[i] *= SwissEph.RADTODEG;		/* ecliptic */
                ndp.xreturn[i + 3] *= SwissEph.RADTODEG;
                ndp.xreturn[i + 12] *= SwissEph.RADTODEG;	/* equator */
                ndp.xreturn[i + 15] *= SwissEph.RADTODEG;
            }
            ndp.xreturn[0] = SE.SwephLib.swe_degnorm(ndp.xreturn[0]);
            ndp.xreturn[12] = SE.SwephLib.swe_degnorm(ndp.xreturn[12]);
            /*}*/
            return OK;
        }

        /* jpl ephemeris.
         * this function computes
         * 1. a barycentric planet position
         * plus, under certain conditions,
         * 2. the barycentric sun,
         * 3. the barycentric earth,
         * in barycentric cartesian equatorial coordinates J2000.

         * tjd		julian day
         * ipli		sweph internal planet number
         * do_save	write new positions in save area
         * xp		array of 6 doubles for planet's position and speed vectors
         * xpe		                       earth's
         * xps		                       sun's
         * serr		pointer to error string
         *
         * xp - xps can be NULL. if do_save is TRUE, all of them can be NULL.
         * the positions will be written into the save area (swed.pldat[ipli].x)
         */
        int jplplan(double tjd, int ipli, Int32 iflag, bool do_save,
                   CPointer<double> xpret, CPointer<double> xperet, CPointer<double> xpsret, ref string serr)
        {
            int i, retc;
            bool do_earth = false, do_sunbary = false;
            double[] ss = new double[3];
            double[] xxp = new double[6], xxe = new double[6], xxs = new double[6];
            CPointer<double> xp, xpe, xps;
            int ictr = SweJPL.J_SBARY;
            plan_data pdp = swed.pldat[ipli];
            plan_data pedp = swed.pldat[SEI_EARTH];
            plan_data psdp = swed.pldat[SEI_SUNBARY];
            /* we assume Teph ~= TDB ~= TT. The maximum error is < 0.002 sec,
             * corresponding to an ephemeris error < 0.001 arcsec for the moon */
            /* double tjd_tdb, T;
               T = (tjd - 2451545.0)/36525.0;
               tjd_tdb = tjd + (0.001657 * Math.Sin(628.3076 * T + 6.2401)
                  + 0.000022 * Math.Sin(575.3385 * T + 4.2970)
                  + 0.000014 * Math.Sin(1256.6152 * T + 6.1969)) / 8640.0;*/
            if (do_save) {
                xp = pdp.x;
                xpe = pedp.x;
                xps = psdp.x;
            } else {
                xp = xxp;
                xpe = xxe;
                xps = xxs;
            }
            if (do_save || ipli == SEI_EARTH || xperet != null
              || (ipli == SEI_MOON)) /* && (iflag & (SEFLG_HELCTR | SEFLG_BARYCTR | SEFLG_NOABERR)))) */
                do_earth = true;
            if (do_save || ipli == SEI_SUNBARY || xpsret != null
              || (ipli == SEI_MOON)) /* && (iflag & (SEFLG_HELCTR | SEFLG_NOABERR)))) */
                do_sunbary = true;
            if (ipli == SEI_MOON)
                ictr = SweJPL.J_EARTH;
            /* open ephemeris, if still closed */
            if (!swed.jpl_file_is_open) {
                retc = open_jpl_file(ss, swed.jplfnam, swed.ephepath, ref serr);
                if (retc != OK)
                    return (retc);
            }
            if (do_earth) {
                /* barycentric earth */
                if (tjd != pedp.teval || tjd == 0) {
                    retc = SE.SweJPL.swi_pleph(tjd, SweJPL.J_EARTH, SweJPL.J_SBARY, xpe, ref serr);
                    if (do_save) {
                        pedp.teval = tjd;
                        pedp.xflgs = -1;	/* new light-time etc. required */
                        pedp.iephe = SwissEph.SEFLG_JPLEPH;
                    }
                    if (retc != OK)
                    {
                        SE.SweJPL.swi_close_jpl_file();
                        swed.jpl_file_is_open = false;
                        return retc;
                    }
                }
                else {
                    xpe = pedp.x;
                }
                if (xperet != null)
                    for (i = 0; i <= 5; i++)
                        xperet[i] = xpe[i];

            }
            if (do_sunbary) {
                /* barycentric sun */
                if (tjd != psdp.teval || tjd == 0) {
                    retc = SE.SweJPL.swi_pleph(tjd, SweJPL.J_SUN, SweJPL.J_SBARY, xps, ref serr);
                    if (do_save) {
                        psdp.teval = tjd;
                        psdp.xflgs = -1;
                        psdp.iephe = SwissEph.SEFLG_JPLEPH;
                    }
                    if (retc != OK)
                    {
                        SE.SweJPL.swi_close_jpl_file();
                        swed.jpl_file_is_open = false;
                        return retc;
                    }
                }
                else {
                    xps = psdp.x;
                }
                if (xpsret != null)
                    for (i = 0; i <= 5; i++)
                        xpsret[i] = xps[i];
            }
            /* earth is wanted */
            if (ipli == SEI_EARTH) {
                for (i = 0; i <= 5; i++)
                    xp[i] = xpe[i];
                /* sunbary is wanted */
            } if (ipli == SEI_SUNBARY) {
                for (i = 0; i <= 5; i++)
                    xp[i] = xps[i];
                /* other planet */
            } else {
                /* if planet already computed */
                if (tjd == pdp.teval && pdp.iephe == SwissEph.SEFLG_JPLEPH) {
                    xp = pdp.x;
                } else {
                    retc = SE.SweJPL.swi_pleph(tjd, pnoint2jpl[ipli], ictr, xp, ref serr);
                    if (do_save) {
                        pdp.teval = tjd;
                        pdp.xflgs = -1;
                        pdp.iephe = SwissEph.SEFLG_JPLEPH;
                    }
                    if (retc != OK)
                    {
                        SE.SweJPL.swi_close_jpl_file();
                        swed.jpl_file_is_open = false;
                        return retc;
                    }
                }
            }
            if (xpret != null)
                for (i = 0; i <= 5; i++)
                    xpret[i] = xp[i];
            return (OK);
        }

        void load_dpsi_deps()
        {
            CFile fp;
            string s, sdummy = String.Empty;
            string[] cpos;
            int n = 0, np, iyear, mjd = 0, mjdsv = 0;
            double dpsi, deps, TJDOFS = 2400000.5;
            if (swed.eop_dpsi_loaded > 0)
                return;
            fp = swi_fopen(-1, SwephLib.DPSI_DEPS_IAU1980_FILE_EOPC04, swed.ephepath, ref sdummy);
            if (fp == null) {
                swed.eop_dpsi_loaded = ERR;
                return;
            }
            if ((swed.dpsi = new double[Sweph.SWE_DATA_DPSI_DEPS]) == null)
            {
                swed.eop_dpsi_loaded = ERR;
                return;
            }
            if ((swed.deps = new double[Sweph.SWE_DATA_DPSI_DEPS]) == null)
            {
                swed.eop_dpsi_loaded = ERR;
                return;
            }
            swed.eop_tjd_beg_horizons = SwephLib.DPSI_DEPS_IAU1980_TJD0_HORIZONS;
            while ((s = fp.ReadLine()) != null) {
                cpos = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                np = cpos.Length;
                if ((iyear = int.Parse(cpos[0])) == 0)
                    continue;
                mjd = int.Parse(cpos[3]);
                /* is file in one-day steps? */
                if (mjdsv > 0 && mjd - mjdsv != 1) {
                    /* we cannot return error but we note it as follows: */
                    swed.eop_dpsi_loaded = -2;
                    fp.Dispose();
                    return;
                }
                if (n == 0)
                    swed.eop_tjd_beg = mjd + TJDOFS;
                swed.dpsi[n] = double.Parse(cpos[8], CultureInfo.InvariantCulture);
                swed.deps[n] = double.Parse(cpos[9], CultureInfo.InvariantCulture);
                /*    fprintf(stderr, "tjd=%f, dpsi=%f, deps=%f\n", mjd + 2400000.5, swed.dpsi[n] * 1000, swed.deps[n] * 1000);exit(0);*/
                n++;
                mjdsv = mjd;
            }
            swed.eop_tjd_end = mjd + TJDOFS;
            swed.eop_dpsi_loaded = 1;
            fp.Dispose();
            /* file finals.all may have some more data, and especially estimations
             * for the near future */
            fp = swi_fopen(-1, SwephLib.DPSI_DEPS_IAU1980_FILE_FINALS, swed.ephepath, ref sdummy);
            if (fp == null)
                return; /* return without error as existence of file is not mandatory */
            while ((s = fp.ReadLine()) != null) {
                mjd = int.Parse(s.Substring(7));
                if (mjd + TJDOFS <= swed.eop_tjd_end)
                    continue;
                if (n >= Sweph.SWE_DATA_DPSI_DEPS)
                    return;
                /* are data in one-day steps? */
                if (mjdsv > 0 && mjd - mjdsv != 1) {
                    /* no error, as we do have data; however, if this file is usefull,
                     * then swed.eop_dpsi_loaded will be set to 2 */
                    swed.eop_dpsi_loaded = -3;
                    fp.Dispose();
                    return;
                }
                /* dpsi, deps Bulletin B */
                dpsi = double.Parse(s.Substring(168), CultureInfo.InvariantCulture);
                deps = double.Parse(s.Substring(178), CultureInfo.InvariantCulture);
                if (dpsi == 0) {
                    /* try dpsi, deps Bulletin A */
                    dpsi = double.Parse(s.Substring(99), CultureInfo.InvariantCulture);
                    deps = double.Parse(s.Substring(118), CultureInfo.InvariantCulture);
                }
                if (dpsi == 0) {
                    swed.eop_dpsi_loaded = 2;
                    /*printf("dpsi from %f to %f \n", swed.eop_tjd_beg, swed.eop_tjd_end);*/
                    fp.Dispose();
                    return;
                }
                swed.eop_tjd_end = mjd + TJDOFS;
                swed.dpsi[n] = dpsi / 1000.0;
                swed.deps[n] = deps / 1000.0;
                /*fprintf(stderr, "tjd=%f, dpsi=%f, deps=%f\n", mjd + 2400000.5, swed.dpsi[n] * 1000, swed.deps[n] * 1000);*/
                n++;
                mjdsv = mjd;
            }
            swed.eop_dpsi_loaded = 2;
            fp.Dispose();
        }

        /* lunar osculating elements, i.e.
         * osculating node ('true' node) and
         * osculating apogee ('black moon', 'lilith').
         * tjd		julian day
         * ipl		body number, i.e. SEI_TRUE_NODE or SEI_OSCU_APOG
         * iflag	flags (which ephemeris, nutation, etc.)
         * serr		error string
         *
         * definitions and remarks:
         * the osculating node and the osculating apogee are defined
         * as the orbital elements of the momentary lunar orbit.
         * their advantage is that when the moon crosses the ecliptic,
         * it is really at the osculating node, and when it passes
         * its greatest distance from earth it is really at the
         * osculating apogee. with the mean elements this is not
         * the case. (some define the apogee as the second focus of
         * the lunar ellipse. but, as seen from the geocenter, both
         * points are in the same direction.)
         * problems:
         * the osculating apogee is given in the 'New International
         * Ephemerides' (Editions St. Michel) as the 'True Lilith'.
         * however, this name is misleading. this point is based on
         * the idea that the lunar orbit can be approximated by an
         * ellipse.
         * arguments against this:
         * 1. this procedure considers celestial motions as two body
         *    problems. this is quite good for planets, but not for
         *    the moon. the strong gravitational attraction of the sun
         *    destroys the idea of an ellipse.
         * 2. the NIE 'True Lilith' has strong oscillations around the
         *    mean one with an amplitude of about 30 degrees. however,
         *    when the moon is in apogee, its distance from the mean
         *    apogee never exceeds 5 degrees.
         * besides, the computation of NIE is INACCURATE. the mistake
         * reaches 20 arc minutes.
         * According to Santoni, the point was calculated using 'les 58
         * premiers termes correctifs au Perigee moyen' published by
         * Chapront and Chapront-Touze. And he adds: "Nous constatons
         * que meme en utilisant ces 58 termes CORRECTIFS, l'erreur peut
         * atteindre 0,5d!" (p. 13) We avoid this error, computing the
         * orbital elements directly from the position and the speed vector.
         *
         * how about the node? it is less problematic, because we
         * we needn't derive it from an orbital ellipse. we can say:
         * the axis of the osculating nodes is the intersection line of
         * the actual orbital plane of the moon and the plane of the
         * ecliptic. or: the osculating nodes are the intersections of
         * the two great circles representing the momentary apparent
         * orbit of the moon and the ecliptic. in this way they make
         * some sense. then, the nodes are really an axis, and they
         * have no geocentric distance. however, in this routine
         * we give a distance derived from the osculating ellipse.
         * the node could also be defined as the intersection axis
         * of the lunar orbital plane and the solar orbital plane,
         * which is not precisely identical to the ecliptic. this
         * would make a difference of several arcseconds.
         *
         * is it possible to keep the idea of a continuously moving
         * apogee that is exact at the moment when the moon passes
         * its greatest distance from earth?
         * to achieve this, we would probably have to interpolate between
         * the actual apogees.
         * the nodes could also be computed by interpolation. the resulting
         * nodes would deviate from the so-called 'true node' by less than
         * 30 arc minutes.
         *
         * sidereal and j2000 true node are first computed for the ecliptic
         * of epoch and then precessed to ecliptic of t0(ayanamsa) or J2000.
         * there is another procedure that computes the node for the ecliptic
         * of t0(ayanamsa) or J2000. it is excluded by
         * #ifdef SID_TNODE_FROM_ECL_T0
         */
        int lunar_osc_elem(double tjd, int ipl, Int32 iflag, ref string serr)
        {
            int i, j, istart;
            int ipli = SEI_MOON;
            Int32 epheflag = SwissEph.SEFLG_DEFAULTEPH;
            int retc = ERR;
            Int32 flg1, flg2;
            double daya;
            //#if 0
            //  struct node_data *ndp, *ndnp, *ndap;
            //#else
            plan_data ndp, ndnp, ndap;
            //#endif
            epsilon oe;
            double speed_intv = NODE_CALC_INTV;	/* to silence gcc warning */
            double a, b;
            double[][] xpos = CreateArray<double>(3, 6),
                xx = CreateArray<double>(3, 6),
                xxa = CreateArray<double>(3, 6);
            double[] xnorm = new double[6], r = new double[6];
            double[] xp;
            double rxy, rxyz, t, dt, fac, sgn;
            double sinnode, cosnode, sinincl, cosincl, sinu, cosu, sinE, cosE;
            double uu, ny, sema, ecce, Gmsm, c2, v2, pp;
            Int32 speedf1, speedf2;
            sid_data sip = new sid_data();
            if (SID_TNODE_FROM_ECL_T0) {
                sip = swed.sidd;
                epsilon oectmp = new epsilon();
                if ((iflag & SwissEph.SEFLG_SIDEREAL) != 0) {
                    calc_epsilon(sip.t0, iflag, oectmp);
                    oe = oectmp;
                } else if ((iflag & SwissEph.SEFLG_J2000) != 0)
                    oe = swed.oec2000;
                else
                    oe = swed.oec;
            } else
                oe = swed.oec;
            ndp = swed.nddat[ipl];
            /* if elements have already been computed for this date, return
             * if speed flag has been turned on, recompute */
            flg1 = iflag & ~SwissEph.SEFLG_EQUATORIAL & ~SwissEph.SEFLG_XYZ;
            flg2 = ndp.xflgs & ~SwissEph.SEFLG_EQUATORIAL & ~SwissEph.SEFLG_XYZ;
            speedf1 = ndp.xflgs & SwissEph.SEFLG_SPEED;
            speedf2 = iflag & SwissEph.SEFLG_SPEED;
            if (tjd == ndp.teval
              && tjd != 0
              && flg1 == flg2
              && (0 == speedf2 || speedf1 != 0)) {
                ndp.xflgs = iflag;
                ndp.iephe = iflag & SwissEph.SEFLG_EPHMASK;
                return OK;
            }
            /* the geocentric position vector and the speed vector of the
             * moon make up the lunar orbital plane. the position vector
             * of the node is along the intersection line of the orbital
             * plane and the plane of the ecliptic.
             * to calculate the osculating node, we need one lunar position
             * with speed.
             * to calculate the speed of the osculating node, we need
             * three lunar positions and the speed of each of them.
             * this is relatively cheap, if the jpl-moon or the swisseph
             * moon is used. with the moshier moon this is much more
             * expensive, because then we need 9 lunar positions for
             * three speeds. but one position and speed can normally
             * be taken from swed.pldat[moon], which corresponds to
             * three moshier moon calculations.
             * the same is also true for the osculating apogee: we need
             * three lunar positions and speeds.
             */
            /*********************************************
             * now three lunar positions with speeds     *
             *********************************************/
            if ((iflag & SwissEph.SEFLG_MOSEPH) != 0)
                epheflag = SwissEph.SEFLG_MOSEPH;
            else if ((iflag & SwissEph.SEFLG_SWIEPH) != 0)
                epheflag = SwissEph.SEFLG_SWIEPH;
            else if ((iflag & SwissEph.SEFLG_JPLEPH) != 0)
                epheflag = SwissEph.SEFLG_JPLEPH;
            /* there may be a moon of wrong ephemeris in save area
             * force new computation: */
            swed.pldat[SEI_MOON].teval = 0;
            if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                istart = 0;
            else
                istart = 2;
            //if (serr != NULL)
            //    *serr = '\0';
            serr = String.Empty;
            three_positions:
            switch (epheflag) {
                case SwissEph.SEFLG_JPLEPH:
                    speed_intv = NODE_CALC_INTV;
                    for (i = istart; i <= 2; i++) {
                        if (i == 0)
                            t = tjd - speed_intv;
                        else if (i == 1)
                            t = tjd + speed_intv;
                        else
                            t = tjd;
                        xp = xpos[i];
                        retc = jplplan(t, ipli, iflag, NO_SAVE, xp, null, null, ref serr);
                        /* read error or corrupt file */
                        if (retc == ERR)
                            return (ERR);
                        /* light-time-corrected moon for apparent node
                         * this makes a difference of several milliarcseconds with
                         * the node and 0.1" with the apogee.
                         * the simple formual 'x[j] -= dt * speed' should not be
                         * used here. the error would be greater than the advantage
                         * of computation speed. */
                        if ((iflag & SwissEph.SEFLG_TRUEPOS) == 0 && retc >= OK) {
                            dt = Math.Sqrt(square_sum(xpos[i])) * AUNIT / CLIGHT / 86400.0;
                            retc = jplplan(t - dt, ipli, iflag, NO_SAVE, xpos[i], null, null, ref serr);/**/
                            /* read error or corrupt file */
                            if (retc == ERR)
                                return (ERR);
                        }
                        /* jpl ephemeris not on disk, or date beyond ephemeris range */
                        if (retc == NOT_AVAILABLE) {
                            iflag = (iflag & ~SwissEph.SEFLG_JPLEPH) | SwissEph.SEFLG_SWIEPH;
                            epheflag = SwissEph.SEFLG_SWIEPH;
                            //if (serr != NULL && strlen(serr) + 30 < AS_MAXCH)
                            serr += " \ntrying Swiss Eph; ";
                            break;
                        } else if (retc == BEYOND_EPH_LIMITS) {
                            if (tjd > MOSHLUEPH_START && tjd < MOSHLUEPH_END) {
                                iflag = (iflag & ~SwissEph.SEFLG_JPLEPH) | SwissEph.SEFLG_MOSEPH;
                                epheflag = SwissEph.SEFLG_MOSEPH;
                                //if (serr != NULL && strlen(serr) + 30 < AS_MAXCH)
                                serr += " \nusing Moshier Eph; ";
                                break;
                            } else
                                return ERR;
                        }
                        /* precession and nutation etc. */
                        retc = swi_plan_for_osc_elem(iflag | SwissEph.SEFLG_SPEED, t, xpos[i]); /* retc is always ok */
                    }
                    break;
                case SwissEph.SEFLG_SWIEPH:
                    //#if 0
                    //      sweph_moon:
                    //#endif
                    speed_intv = NODE_CALC_INTV;
                    for (i = istart; i <= 2; i++) {
                        if (i == 0)
                            t = tjd - speed_intv;
                        else if (i == 1)
                            t = tjd + speed_intv;
                        else
                            t = tjd;
                        retc = swemoon(t, iflag | SwissEph.SEFLG_SPEED, NO_SAVE, xpos[i], ref serr);/**/
                        if (retc == ERR)
                            return (ERR);
                        /* light-time-corrected moon for apparent node (~ 0.006") */
                        if ((iflag & SwissEph.SEFLG_TRUEPOS) == 0 && retc >= OK) {
                            dt = Math.Sqrt(square_sum(xpos[i])) * AUNIT / CLIGHT / 86400.0;
                            retc = swemoon(t - dt, iflag | SwissEph.SEFLG_SPEED, NO_SAVE, xpos[i], ref serr);/**/
                            if (retc == ERR)
                                return (ERR);
                        }
                        if (retc == NOT_AVAILABLE) {
                            if (tjd > MOSHPLEPH_START && tjd < MOSHPLEPH_END) {
                                iflag = (iflag & ~SwissEph.SEFLG_SWIEPH) | SwissEph.SEFLG_MOSEPH;
                                epheflag = SwissEph.SEFLG_MOSEPH;
                                //if (serr != NULL && strlen(serr) + 30 < AS_MAXCH)
                                serr += " \nusing Moshier eph.; ";
                                break;
                            } else
                                return ERR;
                        }
                        /* precession and nutation etc. */
                        retc = swi_plan_for_osc_elem(iflag | SwissEph.SEFLG_SPEED, t, xpos[i]); /* retc is always ok */
                    }
                    break;
                case SwissEph.SEFLG_MOSEPH:
                    //#if 0
                    //      moshier_moon:
                    //#endif
                    /* with moshier moon, we need a greater speed_intv, because here the
                     * node and apogee oscillate wildly within small intervals */
                    speed_intv = NODE_CALC_INTV_MOSH;
                    for (i = istart; i <= 2; i++) {
                        if (i == 0)
                            t = tjd - speed_intv;
                        else if (i == 1)
                            t = tjd + speed_intv;
                        else
                            t = tjd;
                        retc = SE.SwemMoon.swi_moshmoon(t, NO_SAVE, xpos[i], ref serr);/**/
                        if (retc == ERR)
                            return (retc);
                        //#if 0
                        //    /* light-time-corrected moon for apparent node.
                        //     * can be neglected with moshier */
                        //    if ((iflag & SEFLG_TRUEPOS) == 0 && retc >= OK) {
                        //      dt =Math.Sqrt(square_sum(xpos[i])) * AUNIT / CLIGHT / 86400;
                        //      retc = swi_moshmoon(t-dt, NO_SAVE, xpos[i], serr);/**/
                        //        }
                        //#endif
                        /* precession and nutation etc. */
                        retc = swi_plan_for_osc_elem(iflag | SwissEph.SEFLG_SPEED, t, xpos[i]); /* retc is always ok */
                    }
                    break;
                default:
                    break;
            }
            if (retc == NOT_AVAILABLE || retc == BEYOND_EPH_LIMITS)
                goto three_positions;
            /*********************************************
             * node with speed                           *
             *********************************************/
            /* node is always needed, even if apogee is wanted */
            ndnp = swed.nddat[SEI_TRUE_NODE];
            /* three nodes */
            for (i = istart; i <= 2; i++) {
                if (Math.Abs(xpos[i][5]) < 1e-15)
                    xpos[i][5] = 1e-15;
                fac = xpos[i][2] / xpos[i][5];
                sgn = xpos[i][5] / Math.Abs(xpos[i][5]);
                for (j = 0; j <= 2; j++)
                    xx[i][j] = (xpos[i][j] - fac * xpos[i][j + 3]) * sgn;
            }
            /* now we have the correct direction of the node, the
             * intersection of the lunar plane and the ecliptic plane.
             * the distance is the distance of the point where the tangent
             * of the lunar motion penetrates the ecliptic plane.
             * this can be very large, e.g. j2415080.37372.
             * below, a new distance will be derived from the osculating
             * ellipse.
             */
            /* save position and speed */
            for (i = 0; i <= 2; i++) {
                ndnp.x[i] = xx[2][i];
                if ((iflag & SwissEph.SEFLG_SPEED) != 0) {
                    b = (xx[1][i] - xx[0][i]) / 2;
                    a = (xx[1][i] + xx[0][i]) / 2 - xx[2][i];
                    ndnp.x[i + 3] = (2 * a + b) / speed_intv;
                } else
                    ndnp.x[i + 3] = 0;
                ndnp.teval = tjd;
                ndnp.iephe = epheflag;
            }
            /************************************************************
             * apogee with speed                                        *
             * must be computed anyway to get the node's distance       *
             ************************************************************/
            ndap = swed.nddat[SEI_OSCU_APOG];
            Gmsm = GEOGCONST * (1 + 1 / EARTH_MOON_MRAT) / AUNIT / AUNIT / AUNIT * 86400.0 * 86400.0;
            /* three apogees */
            for (i = istart; i <= 2; i++) {
                /* node */
                rxy = Math.Sqrt(xx[i][0] * xx[i][0] + xx[i][1] * xx[i][1]);
                cosnode = xx[i][0] / rxy;
                sinnode = xx[i][1] / rxy;
                /* inclination */
                SE.SwephLib.swi_cross_prod(xpos[i], xpos[i].GetPointer(3), xnorm);
                rxy = xnorm[0] * xnorm[0] + xnorm[1] * xnorm[1];
                c2 = (rxy + xnorm[2] * xnorm[2]);
                rxyz = Math.Sqrt(c2);
                rxy = Math.Sqrt(rxy);
                sinincl = rxy / rxyz;
                cosincl = Math.Sqrt(1 - sinincl * sinincl);
                /* argument of latitude */
                cosu = xpos[i][0] * cosnode + xpos[i][1] * sinnode;
                sinu = xpos[i][2] / sinincl;
                uu = Math.Atan2(sinu, cosu);
                /* semi-axis */
                rxyz = Math.Sqrt(square_sum(xpos[i]));
                v2 = square_sum((xpos[i].GetPointer(3)));
                sema = 1 / (2 / rxyz - v2 / Gmsm);
                /* eccentricity */
                pp = c2 / Gmsm;
                ecce = Math.Sqrt(1 - pp / sema);
                /* eccentric anomaly */
                cosE = 1 / ecce * (1 - rxyz / sema);
                sinE = 1 / ecce / Math.Sqrt(sema * Gmsm) * dot_prod(xpos[i], (xpos[i].GetPointer(3)));
                /* true anomaly */
                ny = 2 * Math.Atan(Math.Sqrt((1 + ecce) / (1 - ecce)) * sinE / (1 + cosE));
                /* distance of apogee from ascending node */
                xxa[i][0] = SE.SwephLib.swi_mod2PI(uu - ny + PI);
                xxa[i][1] = 0;			/* latitude */
                xxa[i][2] = sema * (1 + ecce);	/* distance */
                /* transformation to ecliptic coordinates */
                SE.SwephLib.swi_polcart(xxa[i], xxa[i]);
                SE.SwephLib.swi_coortrf2(xxa[i], xxa[i], -sinincl, cosincl);
                SE.SwephLib.swi_cartpol(xxa[i], xxa[i]);
                /* adding node, we get apogee in ecl. coord. */
                xxa[i][0] += Math.Atan2(sinnode, cosnode);
                SE.SwephLib.swi_polcart(xxa[i], xxa[i]);
                /* new distance of node from orbital ellipse:
                 * true anomaly of node: */
                ny = SE.SwephLib.swi_mod2PI(ny - uu);
                /* eccentric anomaly */
                cosE = Math.Cos(2 * Math.Atan(Math.Tan(ny / 2) / Math.Sqrt((1 + ecce) / (1 - ecce))));
                /* new distance */
                r[0] = sema * (1 - ecce * cosE);
                /* old node distance */
                r[1] = Math.Sqrt(square_sum(xx[i]));
                /* correct length of position vector */
                for (j = 0; j <= 2; j++)
                    xx[i][j] *= r[0] / r[1];
            }
            /* save position and speed */
            for (i = 0; i <= 2; i++) {
                /* apogee */
                ndap.x[i] = xxa[2][i];
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    ndap.x[i + 3] = (xxa[1][i] - xxa[0][i]) / speed_intv / 2;
                else
                    ndap.x[i + 3] = 0;
                ndap.teval = tjd;
                ndap.iephe = epheflag;
                /* node */
                ndnp.x[i] = xx[2][i];
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    ndnp.x[i + 3] = (xx[1][i] - xx[0][i]) / speed_intv / 2;/**/
                else
                    ndnp.x[i + 3] = 0;
            }
            /**********************************************************************
             * precession and nutation have already been taken into account
             * because the computation is on the basis of lunar positions
             * that have gone through swi_plan_for_osc_elem.
             * light-time is already contained in lunar positions.
             * now compute polar and equatorial coordinates:
             **********************************************************************/
            for (j = 0; j <= 1; j++) {
                double[] x = new double[6];
                if (j == 0)
                    ndp = swed.nddat[SEI_TRUE_NODE];
                else
                    ndp = swed.nddat[SEI_OSCU_APOG];
                //memset(ndp.xreturn, 0, 24 * sizeof(double));
                for (int ii = 0; ii < ndp.xreturn.Length; ii++) {
                    ndp.xreturn[ii] = 0;
                }
                /* cartesian ecliptic */
                for (i = 0; i <= 5; i++)
                    ndp.xreturn[6 + i] = ndp.x[i];
                /* polar ecliptic */
                SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(6), ndp.xreturn);
                /* cartesian equatorial */
                SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(6), ndp.xreturn.GetPointer(18), -oe.seps, oe.ceps);
                if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                    SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(9), ndp.xreturn.GetPointer(21), -oe.seps, oe.ceps);
                if (SID_TNODE_FROM_ECL_T0) {
                    /* sideral: we return NORMAL equatorial coordinates, there are no
                     * sidereal ones */
                    if ((iflag & SwissEph.SEFLG_SIDEREAL) != 0) {
                        /* to J2000 */
                        SE.SwephLib.swi_precess(ndp.xreturn.GetPointer(18), sip.t0, iflag, J_TO_J2000);
                        if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                            swi_precess_speed(ndp.xreturn.GetPointer(21), sip.t0, iflag, J_TO_J2000);
                        if (0 == (iflag & SwissEph.SEFLG_J2000)) {
                            /* to tjd */
                            SE.SwephLib.swi_precess(ndp.xreturn.GetPointer(18), tjd, iflag, J2000_TO_J);
                            if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                                swi_precess_speed(ndp.xreturn.GetPointer(21), tjd, iflag, J2000_TO_J);
                        }
                    }
                }
                if (0 == (iflag & SwissEph.SEFLG_NONUT)) {
                    SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(18), -swed.nut.snut, swed.nut.cnut);
                    if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                        SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(21), ndp.xreturn.GetPointer(21), -swed.nut.snut, swed.nut.cnut);
                }
                /* polar equatorial */
                SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(12));
                ndp.xflgs = iflag;
                ndp.iephe = iflag & SwissEph.SEFLG_EPHMASK;
                if (SID_TNODE_FROM_ECL_T0) {
                    /* node and apogee are already referred to t0;
                     * nothing has to be done */
                } else {
                    if ((iflag & SwissEph.SEFLG_SIDEREAL) != 0) {
                        /* node and apogee are referred to t;
                         * the ecliptic position must be transformed to t0 */
                        /* rigorous algorithm */
                        if ((swed.sidd.sid_mode & SwissEph.SE_SIDBIT_ECL_T0) != 0
                          || (swed.sidd.sid_mode & SwissEph.SE_SIDBIT_SSY_PLANE) != 0) {
                            for (i = 0; i <= 5; i++)
                                x[i] = ndp.xreturn[18 + i];
                            /* remove nutation */
                            if (0 == (iflag & SwissEph.SEFLG_NONUT))
                                swi_nutate(x, iflag, true);
                            /* precess to J2000 */
                            SE.SwephLib.swi_precess(x, tjd, iflag, J_TO_J2000);
                            if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                                swi_precess_speed(x, tjd, iflag, J_TO_J2000);
                            if ((swed.sidd.sid_mode & SwissEph.SE_SIDBIT_ECL_T0) != 0)
                                swi_trop_ra2sid_lon(x, ndp.xreturn.GetPointer(6), ndp.xreturn.GetPointer(18), iflag);
                            /* project onto solar system equator */
                            else if ((swed.sidd.sid_mode & SwissEph.SE_SIDBIT_SSY_PLANE) != 0)
                                swi_trop_ra2sid_lon_sosy(x, ndp.xreturn.GetPointer(6), iflag);
                            /* to polar */
                            SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(6), ndp.xreturn);
                            SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(12));
                            /* traditional algorithm;
                             * this is a bit clumsy, but allows us to keep the
                             * sidereal code together */
                        } else {
                            SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(6), ndp.xreturn);
                            if (swe_get_ayanamsa_ex(ndp.teval, iflag, out daya, ref serr) == ERR)
                                return ERR;
                            ndp.xreturn[0] -= daya * SwissEph.DEGTORAD;
                            SE.SwephLib.swi_polcart_sp(ndp.xreturn, ndp.xreturn.GetPointer(6));
                        }
                    } else if ((iflag & SwissEph.SEFLG_J2000) != 0) {
                        /* node and apogee are referred to t;
                         * the ecliptic position must be transformed to J2000 */
                        for (i = 0; i <= 5; i++)
                            x[i] = ndp.xreturn[18 + i];
                        /* precess to J2000 */
                        SE.SwephLib.swi_precess(x, tjd, iflag, J_TO_J2000);
                        if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                            swi_precess_speed(x, tjd, iflag, J_TO_J2000);
                        for (i = 0; i <= 5; i++)
                            ndp.xreturn[18 + i] = x[i];
                        SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(12));
                        SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(18), ndp.xreturn.GetPointer(6), swed.oec2000.seps, swed.oec2000.ceps);
                        if ((iflag & SwissEph.SEFLG_SPEED) != 0)
                            SE.SwephLib.swi_coortrf2(ndp.xreturn.GetPointer(21), ndp.xreturn.GetPointer(9), swed.oec2000.seps, swed.oec2000.ceps);
                        SE.SwephLib.swi_cartpol_sp(ndp.xreturn.GetPointer(6), ndp.xreturn);
                    }
                }
                /**********************
                 * radians to degrees *
                 **********************/
                /*if (!(iflag & SwissEph.SEFLG_RADIANS)) {*/
                for (i = 0; i < 2; i++) {
                    ndp.xreturn[i] *= SwissEph.RADTODEG;	/* ecliptic */
                    ndp.xreturn[i + 3] *= SwissEph.RADTODEG;
                    ndp.xreturn[i + 12] *= SwissEph.RADTODEG;	/* equator */
                    ndp.xreturn[i + 15] *= SwissEph.RADTODEG;
                }
                ndp.xreturn[0] = SE.swe_degnorm(ndp.xreturn[0]);
                ndp.xreturn[12] = SE.swe_degnorm(ndp.xreturn[12]);
                /*}*/
            }
            return OK;
        }

        /* computes a main planet from any ephemeris, if it
         * has not yet been computed for this date.
         * since a geocentric position requires the earth, the
         * earth's position will be computed as well. With SWISSEPH
         * files the barycentric sun will be done as well.
         * With Moshier, the moon will be done as well.
         *
         * tjd 		= julian day
         * ipli		= body number
         * epheflag	= which ephemeris? JPL, SWISSEPH, Moshier?
         * iflag	= other flags
         *
         * the geocentric apparent position of ipli (or whatever has
         * been specified in iflag) will be saved in
         * &swed.pldat[ipli].xreturn[];
         *
         * the barycentric (heliocentric with Moshier) position J2000
         * will be kept in
         * &swed.pldat[ipli].x[];
         */
        int main_planet(double tjd, int ipli, Int32 epheflag, Int32 iflag,
                       ref string serr)
        {
            int retc;
            switch (epheflag) {
                case SwissEph.SEFLG_JPLEPH:
                    retc = jplplan(tjd, ipli, iflag, DO_SAVE, null, null, null, ref serr);
                    /* read error or corrupt file */
                    if (retc == ERR)
                        return ERR;
                    /* jpl ephemeris not on disk or date beyond ephemeris range */
                    if (retc == NOT_AVAILABLE) {
                        iflag = (iflag & ~SwissEph.SEFLG_JPLEPH) | SwissEph.SEFLG_SWIEPH;
                        serr += " \ntrying Swiss Eph; ";
                        goto sweph_planet;
                    } else if (retc == BEYOND_EPH_LIMITS) {
                        if (tjd > MOSHPLEPH_START && tjd < MOSHPLEPH_END) {
                            iflag = (iflag & ~SwissEph.SEFLG_JPLEPH) | SwissEph.SEFLG_MOSEPH;
                            serr += " \nusing Moshier Eph; ";
                            goto moshier_planet;
                        } else
                            return ERR;
                    }
                    /* geocentric, lighttime etc. */
                    if (ipli == SEI_SUN)
                        retc = app_pos_etc_sun(iflag, ref serr)/**/;
                    else
                        retc = app_pos_etc_plan(ipli, iflag, ref serr);
                    if (retc == ERR)
                        return ERR;
                    /* t for light-time beyond ephemeris range */
                    if (retc == NOT_AVAILABLE) {
                        iflag = (iflag & ~SwissEph.SEFLG_JPLEPH) | SwissEph.SEFLG_SWIEPH;
                        serr += " \ntrying Swiss Eph; ";
                        goto sweph_planet;
                    } else if (retc == BEYOND_EPH_LIMITS) {
                        if (tjd > MOSHPLEPH_START && tjd < MOSHPLEPH_END) {
                            iflag = (iflag & ~SwissEph.SEFLG_JPLEPH) | SwissEph.SEFLG_MOSEPH;
                            serr += " \nusing Moshier Eph; ";
                            goto moshier_planet;
                        } else
                            return ERR;
                    }
                    break;
                case SwissEph.SEFLG_SWIEPH:
                sweph_planet:
                    /* compute barycentric planet (+ earth, sun, moon) */
                    retc = sweplan(tjd, ipli, SEI_FILE_PLANET, iflag, DO_SAVE, null, null, null, null, ref serr);
                    if (retc == ERR)
                        return ERR;
                    /* if sweph file not found, switch to moshier */
                    if (retc == NOT_AVAILABLE) {
                        if (tjd > MOSHPLEPH_START && tjd < MOSHPLEPH_END) {
                            iflag = (iflag & ~SwissEph.SEFLG_SWIEPH) | SwissEph.SEFLG_MOSEPH;
                            serr += " \nusing Moshier eph.; ";
                            goto moshier_planet;
                        } else
                            return ERR;
                    }
                    /* geocentric, lighttime etc. */
                    if (ipli == SEI_SUN)
                        retc = app_pos_etc_sun(iflag, ref serr)/**/;
                    else
                        retc = app_pos_etc_plan(ipli, iflag, ref serr);
                    if (retc == ERR)
                        return ERR;
                    /* if sweph file for t(lighttime) not found, switch to moshier */
                    if (retc == NOT_AVAILABLE) {
                        if (tjd > MOSHPLEPH_START && tjd < MOSHPLEPH_END) {
                            iflag = (iflag & ~SwissEph.SEFLG_SWIEPH) | SwissEph.SEFLG_MOSEPH;
                            serr += " \nusing Moshier eph.; ";
                            goto moshier_planet;
                        } else
                            return ERR;
                    }
                    break;
                case SwissEph.SEFLG_MOSEPH:
                moshier_planet:
                    retc = SE.SwemPlan.swi_moshplan(tjd, ipli, DO_SAVE, null, null, ref serr);/**/
                    if (retc == ERR)
                        return ERR;
                    /* geocentric, lighttime etc. */
                    if (ipli == SEI_SUN)
                        retc = app_pos_etc_sun(iflag, ref  serr)/**/;
                    else
                        retc = app_pos_etc_plan(ipli, iflag, ref serr);
                    if (retc == ERR)
                        return ERR;
                    break;
                default:
                    break;
            }
            return OK;
        }

        /* Computes a main planet from any ephemeris or returns
         * it again, if it has been computed before.
         * In barycentric equatorial position of the J2000 equinox.
         * The earth's position is computed as well. With SWISSEPH
         * and JPL ephemeris the barycentric sun is computed, too.
         * With Moshier, the moon is returned, as well.
         *
         * tjd 		= julian day
         * ipli		= body number
         * epheflag	= which ephemeris? JPL, SWISSEPH, Moshier?
         * iflag	= other flags
         * xp, xe, xs, and xm are the pointers, where the program
         * either finds or stores (if not found) the barycentric
         * (heliocentric with Moshier) positions of the following
         * bodies:
         * xp		planet
         * xe		earth
         * xs		sun
         * xm		moon
         *
         * xm is used with Moshier only
         */
        int main_planet_bary(double tjd, int ipli, Int32 epheflag, Int32 iflag, bool do_save,
                       CPointer<double> xp, CPointer<double> xe, CPointer<double> xs, CPointer<double> xm,
                       ref string serr)
        {
            int i, retc;
            switch (epheflag) {
                case SwissEph.SEFLG_JPLEPH:
                    retc = jplplan(tjd, ipli, iflag, do_save, xp, xe, xs, ref serr);
                    /* read error or corrupt file */
                    if (retc == ERR || retc == BEYOND_EPH_LIMITS)
                        return retc;
                    /* jpl ephemeris not on disk or date beyond ephemeris range */
                    if (retc == NOT_AVAILABLE) {
                        iflag = (iflag & ~SwissEph.SEFLG_JPLEPH) | SwissEph.SEFLG_SWIEPH;
                        serr += " \ntrying Swiss Eph; ";
                        goto sweph_planet;
                    }
                    break;
                case SwissEph.SEFLG_SWIEPH:
                sweph_planet:
                    /* compute barycentric planet (+ earth, sun, moon) */
                    retc = sweplan(tjd, ipli, SEI_FILE_PLANET, iflag, do_save, xp, xe, xs, xm, ref serr);
                    //#if 1
                    if (retc == ERR || retc == NOT_AVAILABLE)
                        return retc;
                    //#else /* if barycentric moshier calculation were implemented */
                    //      if (retc == ERR)
                    //    return ERR;
                    //      /* if sweph file not found, switch to moshier */
                    //      if (retc == NOT_AVAILABLE) {
                    //    if (tjd > MOSHPLEPH_START && tjd < MOSHPLEPH_END) {
                    //      iflag = (iflag & ~SEFLG_SWIEPH) | SEFLG_MOSEPH;
                    //      if (serr != NULL && strlen(serr) + 30 < AS_MAXCH)
                    //        serr+= " \nusing Moshier eph.; ";
                    //      goto moshier_planet;
                    //    } else
                    //      goto return_error;
                    //      }
                    //#endif
                    break;
                case SwissEph.SEFLG_MOSEPH:
                    //#if 0
                    //      moshier_planet:
                    //#endif
                    retc = SE.SwemPlan.swi_moshplan(tjd, ipli, do_save, xp, xe, ref serr);/**/
                    if (retc == ERR)
                        return ERR;
                    for (i = 0; i <= 5; i++)
                        xs[i] = 0;
                    break;
                default:
                    break;
            }
            return OK;
        }

        double meff(double r)
        {
            double f, m;
            int i;
            if (r <= 0)
                return 0.0;
            else if (r >= 1)
                return 1.0;
            for (i = 0; eff_arr[i].r > r; i++)
                ;	/* empty body */
            f = (r - eff_arr[i - 1].r) / (eff_arr[i].r - eff_arr[i - 1].r);
            m = eff_arr[i - 1].m + f * (eff_arr[i].m - eff_arr[i - 1].m);
            return m;
        }

        /* calculates the nutation matrix
         * nu		pointer to nutation data structure
         * oe		pointer to epsilon data structure
         */
        void nut_matrix(nut nu, epsilon oe)
        {
            double psi, eps;
            double sinpsi, cospsi, sineps, coseps, sineps0, coseps0;
            psi = nu.nutlo[0];
            eps = oe.eps + nu.nutlo[1];
            sinpsi = Math.Sin(psi);
            cospsi = Math.Cos(psi);
            sineps0 = oe.seps;
            coseps0 = oe.ceps;
            sineps = Math.Sin(eps);
            coseps = Math.Cos(eps);
            nu.matrix[0, 0] = cospsi;
            nu.matrix[0, 1] = sinpsi * coseps;
            nu.matrix[0, 2] = sinpsi * sineps;
            nu.matrix[1, 0] = -sinpsi * coseps0;
            nu.matrix[1, 1] = cospsi * coseps * coseps0 + sineps * sineps0;
            nu.matrix[1, 2] = cospsi * sineps * coseps0 - coseps * sineps0;
            nu.matrix[2, 0] = -sinpsi * sineps0;
            nu.matrix[2, 1] = cospsi * coseps * sineps0 - sineps * coseps0;
            nu.matrix[2, 2] = cospsi * sineps * sineps0 + coseps * coseps0;
        }

        int open_jpl_file(CPointer<double> ss, string fname, string fpath, ref string serr)
        {
            int retc;
            string serr2 = String.Empty;
            retc = SE.SweJPL.swi_open_jpl_file(ss, fname, fpath, ref serr);
            /* If we fail with default JPL ephemeris (DE431), we try the second default
             * (DE406), but only if serr is not NULL and an warning message can be
             * returned. */
            if (retc != OK && fname.Contains(SwissEph.SE_FNAME_DFT)) {
                retc = SE.SweJPL.swi_open_jpl_file(ss, SwissEph.SE_FNAME_DFT2, fpath, ref serr2);
                if (retc == OK) {
                    swed.jplfnam = SwissEph.SE_FNAME_DFT2;
                    serr2 = "Error with JPL ephemeris file ";
                    serr2 += SwissEph.SE_FNAME_DFT;
                    serr2 += C.sprintf(": %s", serr);
                    serr2 += ". Defaulting to ";
                    serr2 += SwissEph.SE_FNAME_DFT2;
                    serr = serr2;
                }
            }
            if (retc == OK) {
                swed.jpldenum = (short)SE.SweJPL.swi_get_jpl_denum();
                swed.jpl_file_is_open = true;
                SE.SwephLib.swi_set_tid_acc(0, 0, swed.jpldenum, ref serr);
            }
            return retc;
        }

        Int32 plaus_iflag(Int32 iflag, Int32 ipl, double tjd, out string serr)
        {
            Int32 epheflag = 0;
            serr = null;
            int jplhor_model = swed.astro_models[SwissEph.SE_MODEL_JPLHOR_MODE];
            int jplhora_model = swed.astro_models[SwissEph.SE_MODEL_JPLHORA_MODE];
            if (jplhor_model == 0) jplhor_model = SwissEph.SEMOD_JPLHOR_DEFAULT;
            if (jplhora_model == 0) jplhora_model = SwissEph.SEMOD_JPLHORA_DEFAULT;
            /* either Horizons mode or simplified Horizons mode, not both */
            if ((iflag & SwissEph.SEFLG_JPLHOR) != 0)
                iflag &= ~SwissEph.SEFLG_JPLHOR_APPROX;
            /* if topocentric bit, turn helio- and barycentric bits off;
             */
            if ((iflag & SwissEph.SEFLG_TOPOCTR) != 0) {
                iflag = iflag & ~(SwissEph.SEFLG_HELCTR | SwissEph.SEFLG_BARYCTR);
            }
            /* if heliocentric bit, turn aberration and deflection off */
            if ((iflag & SwissEph.SEFLG_HELCTR) != 0)
                iflag |= SwissEph.SEFLG_NOABERR | SwissEph.SEFLG_NOGDEFL; /*iflag |= SwissEph.SEFLG_TRUEPOS;*/
            /* same, if barycentric bit */
            if ((iflag & SwissEph.SEFLG_BARYCTR) != 0)
                iflag |= SwissEph.SEFLG_NOABERR | SwissEph.SEFLG_NOGDEFL; /*iflag |= SwissEph.SEFLG_TRUEPOS;*/
            /* if no_precession bit is set, set also no_nutation bit */
            if ((iflag & SwissEph.SEFLG_J2000) != 0)
                iflag |= SwissEph.SEFLG_NONUT;
            /* if sidereal bit is set, set also no_nutation bit *
             * also turn JPL Horizons mode off */
            if ((iflag & SwissEph.SEFLG_SIDEREAL) != 0) {
                iflag |= SwissEph.SEFLG_NONUT;
                iflag = iflag & ~(SwissEph.SEFLG_JPLHOR | SwissEph.SEFLG_JPLHOR_APPROX);
            }
            /* if truepos is set, turn off grav. defl. and aberration */
            if ((iflag & SwissEph.SEFLG_TRUEPOS) != 0)
                iflag |= (SwissEph.SEFLG_NOGDEFL | SwissEph.SEFLG_NOABERR);
            if ((iflag & SwissEph.SEFLG_MOSEPH) != 0)
                epheflag = SwissEph.SEFLG_MOSEPH;
            if ((iflag & SwissEph.SEFLG_SWIEPH) != 0)
                epheflag = SwissEph.SEFLG_SWIEPH;
            if ((iflag & SwissEph.SEFLG_JPLEPH) != 0)
                epheflag = SwissEph.SEFLG_JPLEPH;
            if (epheflag == 0)
                epheflag = SwissEph.SEFLG_DEFAULTEPH;
            iflag = (iflag & ~SwissEph.SEFLG_EPHMASK) | epheflag;
            /* SwissEph.SEFLG_JPLHOR only with JPL and Swiss Ephemeeris */
            if ((epheflag & SwissEph.SEFLG_JPLEPH) == 0)
                iflag = iflag & ~(SwissEph.SEFLG_JPLHOR | SwissEph.SEFLG_JPLHOR_APPROX);
            /* planets that have no JPL Horizons mode */
            if (ipl == SwissEph.SE_OSCU_APOG || ipl == SwissEph.SE_TRUE_NODE
                || ipl == SwissEph.SE_MEAN_APOG || ipl == SwissEph.SE_MEAN_NODE
                || ipl == SwissEph.SE_INTP_APOG || ipl == SwissEph.SE_INTP_PERG)
                iflag = iflag & ~(SwissEph.SEFLG_JPLHOR | SwissEph.SEFLG_JPLHOR_APPROX);
            if (ipl >= SwissEph.SE_FICT_OFFSET && ipl <= SwissEph.SE_FICT_MAX)
                iflag = iflag & ~(SwissEph.SEFLG_JPLHOR | SwissEph.SEFLG_JPLHOR_APPROX);
            /* SwissEph.SEFLG_JPLHOR requires SwissEph.SEFLG_ICRS, if calculated with * precession/nutation IAU 1980 and corrections dpsi, deps */
            if ((iflag & SwissEph.SEFLG_JPLHOR) != 0) {
                if (swed.eop_dpsi_loaded <= 0
                   || ((tjd < swed.eop_tjd_beg || tjd > swed.eop_tjd_end)
                   && jplhor_model != SwissEph.SEMOD_JPLHOR_EXTENDED_1800))
                {
                    /*&& !USE_HORIZONS_METHOD_BEFORE_1980)) {*/
                    switch (swed.eop_dpsi_loaded)
                    {
                        case 0:
                            serr = "you did not call swe_set_jpl_file(); default to SEFLG_JPLHOR_APPROX";
                            break;
                        case -1:
                            serr = "file eop_1962_today.txt not found; default to SEFLG_JPLHOR_APPROX";
                            break;
                        case -2:
                            serr = "file eop_1962_today.txt corrupt; default to SEFLG_JPLHOR_APPROX";
                            break;
                        case -3:
                            serr = "file eop_finals.txt corrupt; default to SEFLG_JPLHOR_APPROX";
                            break;
                    }
                    iflag &= ~SwissEph.SEFLG_JPLHOR;
                    iflag |= SwissEph.SEFLG_JPLHOR_APPROX;
                }
            }
            if ((iflag & SwissEph.SEFLG_JPLHOR) != 0)
                iflag |= SwissEph.SEFLG_ICRS;
            /*if ((iflag & SwissEph.SEFLG_JPLHOR_APPROX) && FRAME_BIAS_APPROX_HORIZONS) */
            /*if ((iflag & SEFLG_JPLHOR_APPROX) && !APPROXIMATE_HORIZONS_ASTRODIENST)*/
            if ((iflag & SwissEph.SEFLG_JPLHOR_APPROX)!=0 && jplhora_model != SwissEph.SEMOD_JPLHORA_1)
                iflag |= SwissEph.SEFLG_ICRS;
            return iflag;
        }

        /* SWISSEPH
         * reads constants on ephemeris file
         * ifno         file #
         * serr         error string
         */
        int read_const(int ifno, ref string serr)
        {
            string /*c,*/ sp;
            //char c2;
            string s = String.Empty, s2 = String.Empty;
            string sastnam = String.Empty;
            int i, ipli, kpl, spi;
            int retc;
            int fendian, freord;
            int lastnam = 19;
            CFile fp;
            Int32 lng = 0;
            UInt32 ulng = 0;
            Int32 flen = 0, fpos = 0;
            short nplan = 0;
            Int32 testendian = 0;
            double[] doubles = new double[20];
            plan_data pdp;
            file_data fdp = swed.fidat[ifno];
            string serr_file_damage = "Ephemeris file %s is damaged (0). ";
            int nbytes_ipl = 2;
            fp = fdp.fptr;
            /*************************************
             * version number of file            *
             *************************************/
            sp = fp.ReadLine();
            if (String.IsNullOrEmpty(sp))
                goto file_damage;
            //sp = strchr(s, '\r');
            //*sp = '\0';
            //sp = s;
            //while (isdigit((int)*sp) == 0 && *sp != '\0')
            //    sp++;
            //if (*sp == '\0')
            //    goto file_damage;
            var match = Regex.Match(sp, @"^.+(\d+)$");
            if (!match.Success) goto file_damage;
            /* version unused so far */
            fdp.fversion = int.Parse(match.Groups[1].Value);
            /*************************************
             * correct file name?                *
             *************************************/
            s = fp.ReadLine().Trim();
            if (String.IsNullOrEmpty(s))
                goto file_damage;
            /* file name, without path */
            sp = fdp.fnam;
            if (sp.LastIndexOf(SwissEph.DIR_GLUE) > 0)
                sp = sp.Substring(sp.LastIndexOf(SwissEph.DIR_GLUE) + 1);
            if (!s.Equals(sp, StringComparison.CurrentCultureIgnoreCase))
            {
                serr = C.sprintf("Ephemeris file name '%s' wrong; rename '%s' ", sp, s);
                goto return_error;
            }
            /*************************************
             * copyright                         *
             *************************************/
            //sp = fgets(s, AS_MAXCH, fp);
            //if (sp == NULL || strstr(sp, "\r\n") == NULL)
            //    goto file_damage;
            s = fp.ReadLine();
            if (String.IsNullOrEmpty(s))
                goto file_damage;
            /****************************************
             * orbital elements, if single asteroid *
             ****************************************/
            if (ifno == SEI_FILE_ANY_AST)
            {
                s = fp.ReadLine();
                if (String.IsNullOrEmpty(s))
                    goto file_damage;
                spi = 0;
                /* MPC number and name; will be analyzed below:
                 * search "asteroid name" */
                sp = s.TrimStart();
                spi = sp.IndexOfFirstNot("0123456789".ToCharArray()) + 1;
                i = spi;
                sastnam = s.Substring(spi, lastnam + i);
                /* save elements, they are required for swe_plan_pheno() */
                swed.astelem = s;
                /* required for magnitude */
                swed.ast_H = double.Parse(s.Substring(35 + i, 7).Trim(), CultureInfo.InvariantCulture);
                swed.ast_G = double.Parse(s.Substring(42 + i, 7).Trim(), CultureInfo.InvariantCulture);
                if (swed.ast_G == 0) swed.ast_G = 0.15;
                /* diameter in kilometers, not always given: */
                s2 = s.Substring(51 + i, 7).Trim();
                //*(s2 + 7) = '\0';
                if (String.IsNullOrEmpty(s2))
                    swed.ast_diam = 0;
                else
                    swed.ast_diam = double.Parse(s2, CultureInfo.InvariantCulture);
                if (swed.ast_diam == 0)
                {
                    /* estimate the diameter from magnitude; assume albedo = 0.15 */
                    swed.ast_diam = 1329 / Math.Sqrt(0.15) * Math.Pow(10, -0.2 * swed.ast_H);
                }
                //#if 0
                //    i = 5;
                //    while (*(sp+i) != ' ')
                //      i++;
                //    j = i - 5;
                //    strncpy(sastnam, sp, lastnam+i);
                //    *(sastnam+lastnam+i) = 0;
                //    /* save elements, they are required for swe_plan_pheno() */
                //    strcpy(swed.astelem, s);
                //    /* required for magnitude */
                //    swed.ast_G = atof(sp + 40 + j);
                //    swed.ast_H = atof(sp + 46 + j);
                //    /* diameter in kilometers, not always given: */
                //    strncpy(s2, sp+56+j, 7);
                //    *(s2 + 7) = '\0';
                //    swed.ast_diam = atof(s2);
                //#endif
            }
            /*************************************
             * one int32 for test of byte order   *
             *************************************/
            if (!fp.Read(ref testendian))
                goto file_damage;
            /* is byte order correct?            */
            if (testendian == SEI_FILE_TEST_ENDIAN) {
                freord = SEI_FILE_NOREORD;
            } else {
                freord = SEI_FILE_REORD;
                //sp = (char*)&lng;
                //c = (char*)&testendian;
                //for (i = 0; i < 4; i++)
                //    *(sp + i) = *(c + 3 - i);
                lng = SE.SweJPL.reorder(testendian);
                if (lng != SEI_FILE_TEST_ENDIAN)
                    goto file_damage;
                /* printf("%d  %x\n", lng, lng);*/
            }
            /* is file bigendian or littlendian?
             * test first byte of test integer, which is highest if bigendian */
            //c = (char*)&testendian;
            //c2 = SEI_FILE_TEST_ENDIAN / 16777216L;
            //if (*c == c2)
            //if (testendian / 16777216 == SEI_FILE_TEST_ENDIAN / 16777216)
            //    fendian = SEI_FILE_BIGENDIAN;
            //else
            //    fendian = SEI_FILE_LITENDIAN;
            if (!BitConverter.IsLittleEndian)
                fendian = SEI_FILE_BIGENDIAN;
            else
                fendian = SEI_FILE_LITENDIAN;
            fdp.iflg = (Int32)freord | fendian;
            /*************************************
             * length of file correct?           *
             *************************************/
            retc = do_fread(ref lng, 4, 1, 4, ref fp, SEI_CURR_FPOS, freord,
                fendian, ifno, ref serr);
            if (retc != OK)
                goto return_error;
            fpos = (int)fp.Position;
            if (fp.Seek(0L, SeekOrigin.End) != 0)
                goto file_damage;
            flen = (int)fp.Position;
            if (lng != flen)
                goto file_damage;
            /**********************************************************
             * DE number of JPL ephemeris which this file is based on *
             **********************************************************/
            retc = do_fread(ref fdp.sweph_denum, 4, 1, 4, ref fp, fpos, freord,
                fendian, ifno, ref serr);
            if (retc != OK)
                goto return_error;
            //swed.jpldenum = (short)fdp.sweph_denum;
            /*************************************
             * start and end epoch of file       *
             *************************************/
            retc = do_fread(ref fdp.tfstart, 8, 1, 8, ref fp, SEI_CURR_FPOS,
                freord, fendian, ifno, ref serr);
            if (retc != OK)
                goto return_error;
            retc = do_fread(ref fdp.tfend, 8, 1, 8, ref fp, SEI_CURR_FPOS, freord,
                fendian, ifno, ref serr);
            if (retc != OK)
                goto return_error;
            /*************************************
             * how many planets are in file?     *
             *************************************/
            retc = do_fread(ref nplan, 2, 1, 2, ref fp, SEI_CURR_FPOS, freord, fendian, ifno, ref serr);
            if (retc != OK)
                goto return_error;
            if (nplan > 256)
            {
                nbytes_ipl = 4;
                nplan %= 256;
            }
            if (nplan < 1 || nplan > 20)
                goto file_damage;
            fdp.npl = nplan;
            /* which ones?                       */
            retc = do_fread(fdp.ipl, nbytes_ipl, (int)nplan, sizeof(int), ref fp, SEI_CURR_FPOS,
                freord, fendian, ifno, ref serr);
            if (retc != OK)
                goto return_error;
            /*************************************
             * asteroid name                     *
             *************************************/
            if (ifno == SEI_FILE_ANY_AST)
            {
                string sastno = String.Empty;
                int j;
                /* name of asteroid is taken from orbital elements record
                 * read above */
                j = 4;	/* old astorb.dat had only 4 characters for MPC# */
                while (sastnam[j] != ' ' && j < 10)	/* new astorb.dat has 5 */
                    j++;
                sastno = sastnam.Substring(0, j);
                //sastno[j] = '\0';
                long l;
                if (!long.TryParse(sastno, out l))
                    i = 0;
                else
                    i = (int)l;
                if (i == fdp.ipl[0] - SwissEph.SE_AST_OFFSET)
                {
                    /* element record is from bowell database */
                    fdp.astnam = sastnam.Substring(j + 1, lastnam);
                    /* overread old ast. name field */
                    if (!fp.ReadString(ref s, 30))
                        goto file_damage;
                }
                else
                {
                    /* older elements record structure: the name
                     * is taken from old name field */
                    if (!fp.ReadString(ref fdp.astnam, 30))
                        goto file_damage;
                }
                /* in worst case strlen of not null terminated area! */
                //i = fdp.astnam.Length - 1;
                //if (i < 0)
                //    i = 0;
                //sp = fdp.astnam + i;
                //while (*sp == ' ') {
                //    sp--;
                //}
                //sp[1] = '\0';
                i = fdp.astnam.IndexOf('\0');
                if (i >= 0) fdp.astnam = fdp.astnam.Substring(0, i);
                fdp.astnam = fdp.astnam.TrimEnd(' ', '\0');
            }
            /*************************************
             * check CRC                         *
             *************************************/
            fpos = (int)fp.Position;
            /* read CRC from file */
            retc = do_fread(ref ulng, 4, 1, 4, ref fp, SEI_CURR_FPOS, freord,
              fendian, ifno, ref serr);
            if (retc != OK)
                goto return_error;
            /* read check area from file */
            fp.Seek(0L, SeekOrigin.Begin);
            /* must check that defined length of s is less than fpos */
            //if (fpos - 1 > 2 * AS_MAXCH)
            //    goto file_damage;
            byte[] crcBuff = new byte[fpos];
            if (fp.Read(crcBuff, 0, fpos) != fpos)
                goto file_damage;
            //#if 1
            if (SE.SwephLib.swi_crc32(crcBuff, (int)fpos) != ulng)
                goto file_damage;
            /*printf("crc %d %d\n", ulng2, ulng);*/
            //#endif
            fp.Seek(fpos + 4, SeekOrigin.Begin);
            /*************************************
             * read general constants            *
             *************************************/
            /* clight, aunit, helgravconst, ratme, sunradius
             * these constants are currently not in use */
            retc = do_fread(doubles, 8, 5, 8, ref fp, SEI_CURR_FPOS, freord,
              fendian, ifno, ref serr);
            if (retc != OK)
                goto return_error;
            swed.gcdat.clight = doubles[0];
            swed.gcdat.aunit = doubles[1];
            swed.gcdat.helgravconst = doubles[2];
            swed.gcdat.ratme = doubles[3];
            swed.gcdat.sunradius = doubles[4];
            /*************************************
             * read constants of planets         *
             *************************************/
            for (kpl = 0; kpl < fdp.npl; kpl++)
            {
                /* get SEI_ planet number */
                ipli = fdp.ipl[kpl];
                if (ipli >= SwissEph.SE_AST_OFFSET)
                    pdp = swed.pldat[SEI_ANYBODY];
                else
                    pdp = swed.pldat[ipli];
                pdp.ibdy = ipli;
                /* file position of planet's index */
                retc = do_fread(ref pdp.lndx0, 4, 1, 4, ref fp, SEI_CURR_FPOS,
            freord, fendian, ifno, ref serr);
                if (retc != OK)
                    goto return_error;
                /* flags: helio/geocentric, rotation, reference ellipse */
                retc = do_fread(ref pdp.iflg, 1, 1, sizeof(Int32), ref fp,
            SEI_CURR_FPOS, freord, fendian, ifno, ref serr);
                if (retc != OK)
                    goto return_error;
                /* number of chebyshew coefficients / segment  */
                /* = interpolation order +1                    */
                retc = do_fread(ref pdp.ncoe, 1, 1, sizeof(int), ref fp,
            SEI_CURR_FPOS, freord, fendian, ifno, ref serr);
                if (retc != OK)
                    goto return_error;
                /* rmax = normalisation factor */
                retc = do_fread(ref lng, 4, 1, 4, ref fp, SEI_CURR_FPOS, freord,
            fendian, ifno, ref serr);
                if (retc != OK)
                    goto return_error;
                pdp.rmax = lng / 1000.0;
                /* start and end epoch of planetary ephemeris,   */
                /* segment length, and orbital elements          */
                retc = do_fread(doubles, 8, 10, 8, ref fp, SEI_CURR_FPOS, freord,
            fendian, ifno, ref serr);
                if (retc != OK)
                    goto return_error;
                pdp.tfstart = doubles[0];
                pdp.tfend = doubles[1];
                pdp.dseg = doubles[2];
                pdp.nndx = (Int32)((doubles[1] - doubles[0] + 0.1) / doubles[2]);
                pdp.telem = doubles[3];
                pdp.prot = doubles[4];
                pdp.dprot = doubles[5];
                pdp.qrot = doubles[6];
                pdp.dqrot = doubles[7];
                pdp.peri = doubles[8];
                pdp.dperi = doubles[9];
                /* alloc space for chebyshew coefficients */
                /* if reference ellipse is used, read its coefficients */
                if ((pdp.iflg & SEI_FLG_ELLIPSE) != 0)
                {
                    if (pdp.refep != null)
                    { /* if switch to other eph. file */
                        //free(pdp.refep);
                        pdp.refep = null;    /* 2015-may-5 */
                        if (pdp.segp != null)
                        {
                            //free(pdp.segp);     /* array of coefficients of */
                            pdp.segp = null;     /* ephemeris segment        */
                        }
                    }
                    //pdp.refep = malloc((size_t)pdp.ncoe * 2 * 8);
                    pdp.refep = new double[pdp.ncoe * 2];
                    retc = do_fread(pdp.refep, 8, 2 * pdp.ncoe, 8, ref fp,
              SEI_CURR_FPOS, freord, fendian, ifno, ref serr);
                    if (retc != OK)
                    {
                        //free(pdp.refep);  /* 2015-may-5 */
                        pdp.refep = null;  /* 2015-may-5 */
                        goto return_error;
                    }
                }/**/
            }
            return (OK);
            file_damage:
            //if (serr != null) {
            //*serr = '\0';
            //if (strlen(serr_file_damage) + strlen(fdp.fnam) < AS_MAXCH) {
            serr = C.sprintf(serr_file_damage, fdp.fnam);
            //}
            //}
            return_error:
            fdp.fptr.Dispose();
            fdp.fptr = null;
            free_planets();
            return (ERR);
        }

        /* SWISSEPH
         * adds reference orbit to chebyshew series (if SEI_FLG_ELLIPSE),
         * rotates series to mean equinox of J2000
         *
         * ipli		planet number
         */
        void rot_back(int ipli)
        {
            int i;
            double t, tdiff;
            double qav, pav, dn;
            double omtild, com, som, cosih2;
            double[,] x = new double[MAXORD + 1, 3];
            double[] uix = new double[3], uiy = new double[3], uiz = new double[3];
            double xrot, yrot, zrot;
            CPointer<double> chcfx, chcfy, chcfz;
            CPointer<double> refepx, refepy;
            double seps2000 = swed.oec2000.seps;
            double ceps2000 = swed.oec2000.ceps;
            plan_data pdp = swed.pldat[ipli];
            int nco = pdp.ncoe;
            t = pdp.tseg0 + pdp.dseg / 2;
            chcfx = pdp.segp;
            chcfy = chcfx + nco;
            chcfz = chcfx + 2 * nco;
            tdiff = (t - pdp.telem) / 365250.0;
            if (ipli == SEI_MOON) {
                dn = pdp.prot + tdiff * pdp.dprot;
                i = (int)(dn / TWOPI);
                dn -= i * TWOPI;
                qav = (pdp.qrot + tdiff * pdp.dqrot) * Math.Cos(dn);
                pav = (pdp.qrot + tdiff * pdp.dqrot) * Math.Sin(dn);
            } else {
                qav = pdp.qrot + tdiff * pdp.dqrot;
                pav = pdp.prot + tdiff * pdp.dprot;
            }
            /*calculate cosine and sine of average perihelion longitude. */
            for (i = 0; i < nco; i++) {
                x[i, 0] = chcfx[i];
                x[i, 1] = chcfy[i];
                x[i, 2] = chcfz[i];
            }
            if ((pdp.iflg & SEI_FLG_ELLIPSE) != 0) {
                refepx = pdp.refep;
                refepy = refepx + nco;
                omtild = pdp.peri + tdiff * pdp.dperi;
                i = (int)(omtild / TWOPI);
                omtild -= i * TWOPI;
                com = Math.Cos(omtild);
                som = Math.Sin(omtild);
                /*add reference orbit.  */
                for (i = 0; i < nco; i++) {
                    x[i, 0] = chcfx[i] + com * refepx[i] - som * refepy[i];
                    x[i, 1] = chcfy[i] + com * refepy[i] + som * refepx[i];
                }
            }
            /* construct right handed orthonormal system with first axis along
               origin of longitudes and third axis along angular momentum
               this uses the standard formulas for equinoctal variables
               (see papers by broucke and by cefola).      */
            cosih2 = 1.0 / (1.0 + qav * qav + pav * pav);
            /*     calculate orbit pole. */
            uiz[0] = 2.0 * pav * cosih2;
            uiz[1] = -2.0 * qav * cosih2;
            uiz[2] = (1.0 - qav * qav - pav * pav) * cosih2;
            /*     calculate origin of longitudes vector. */
            uix[0] = (1.0 + qav * qav - pav * pav) * cosih2;
            uix[1] = 2.0 * qav * pav * cosih2;
            uix[2] = -2.0 * pav * cosih2;
            /*     calculate vector in orbital plane orthogonal to origin of
                  longitudes.                                               */
            uiy[0] = 2.0 * qav * pav * cosih2;
            uiy[1] = (1.0 - qav * qav + pav * pav) * cosih2;
            uiy[2] = 2.0 * qav * cosih2;
            /*     rotate to actual orientation in space.         */
            for (i = 0; i < nco; i++) {
                xrot = x[i, 0] * uix[0] + x[i, 1] * uiy[0] + x[i, 2] * uiz[0];
                yrot = x[i, 0] * uix[1] + x[i, 1] * uiy[1] + x[i, 2] * uiz[1];
                zrot = x[i, 0] * uix[2] + x[i, 1] * uiy[2] + x[i, 2] * uiz[2];
                if (Math.Abs(xrot) + Math.Abs(yrot) + Math.Abs(zrot) >= 1e-14)
                    pdp.neval = i;
                x[i, 0] = xrot;
                x[i, 1] = yrot;
                x[i, 2] = zrot;
                if (ipli == SEI_MOON) {
                    /* rotate to j2000 equator */
                    x[i, 1] = ceps2000 * yrot - seps2000 * zrot;
                    x[i, 2] = seps2000 * yrot + ceps2000 * zrot;
                }
            }
            for (i = 0; i < nco; i++) {
                chcfx[i] = x[i, 0];
                chcfy[i] = x[i, 1];
                chcfz[i] = x[i, 2];
            }
        }

        Int32 swecalc(double tjd, int ipl, Int32 iflag, CPointer<double> x, out string serr)
        {
            int i;
            int ipli, ipli_ast, ifno;
            int retc;
            Int32 epheflag = SwissEph.SEFLG_DEFAULTEPH;
            plan_data pdp;
            plan_data pedp = swed.pldat[SEI_EARTH];
            plan_data psdp = swed.pldat[SEI_SUNBARY];
            //#if 0
            //  struct node_data *ndp;
            //#else
            plan_data ndp;
            //#endif
            double[] xp, xp2;
            double[] ss = new double[3];
            string serr2 = null;
            serr = null;
            /******************************************
             * iflag plausible?                       *
             ******************************************/
            iflag = plaus_iflag(iflag, ipl, tjd, out serr);
            /******************************************
             * which ephemeris is wanted, which is used?
             * Three ephemerides are possible: MOSEPH, SWIEPH, JPLEPH.
             * JPLEPH is best, SWIEPH is nearly as good, MOSEPH is least precise.
             * The availability of the various ephemerides depends on the installed
             * ephemeris files in the users ephemeris directory. This can change at
             * any time.
             * Swisseph should try to fulfil the wish of the user for a specific
             * ephemeris, but use a less precise one if the desired ephemeris is not
             * available for the given date and body.
             * If internal ephemeris errors are detected (data error, file length error)
             * an error is returned.
             * If the time range is bad but another ephemeris can deliver this range,
             * the other ephemeris is used.
             * If no ephemeris is specified, DEFAULTEPH is assumed as desired.
             * DEFAULTEPH is defined at compile time, usually as JPLEPH.
             * The caller learns from the return flag which ephemeris was used.
             * ephe_flag is extracted from iflag, but can change later if the
             * desired ephe is not available.
             ******************************************/
            if ((iflag & SwissEph.SEFLG_MOSEPH) != 0)
                epheflag = SwissEph.SEFLG_MOSEPH;
            if ((iflag & SwissEph.SEFLG_SWIEPH) != 0)
                epheflag = SwissEph.SEFLG_SWIEPH;
            if ((iflag & SwissEph.SEFLG_JPLEPH) != 0)
                epheflag = SwissEph.SEFLG_JPLEPH;
            /* no barycentric calculations with Moshier ephemeris */
            if ((iflag & SwissEph.SEFLG_BARYCTR) != 0 && (iflag & SwissEph.SEFLG_MOSEPH) != 0) {
                serr = "barycentric Moshier positions are not supported.";
                return ERR;
            }
            if (epheflag != SwissEph.SEFLG_MOSEPH && !swed.ephe_path_is_set && !swed.jpl_file_is_open)
                swe_set_ephe_path(null);
            if ((iflag & SwissEph.SEFLG_SIDEREAL) != 0 && !swed.ayana_is_set)
                swe_set_sid_mode(SwissEph.SE_SIDM_FAGAN_BRADLEY, 0, 0);
            /******************************************
             * obliquity of ecliptic 2000 and of date *
             ******************************************/
            swi_check_ecliptic(tjd, iflag);
            /******************************************
             * nutation                               *
             ******************************************/
            swi_check_nutation(tjd, iflag);
            /******************************************
             * select planet and ephemeris            *
             *                                        *
             * ecliptic and nutation                  *
             ******************************************/
            if (ipl == SwissEph.SE_ECL_NUT) {
                x[0] = swed.oec.eps + swed.nut.nutlo[1];	/* true ecliptic */
                x[1] = swed.oec.eps;			/* mean ecliptic */
                x[2] = swed.nut.nutlo[0];		/* nutation in longitude */
                x[3] = swed.nut.nutlo[1];		/* nutation in obliquity */
                /*if ((iflag & SEFLG_RADIANS) == 0)*/
                for (i = 0; i <= 3; i++)
                    x[i] *= SwissEph.RADTODEG;
                return (iflag);
                /******************************************
                 * moon                                   *
                 ******************************************/
            } else if (ipl == SwissEph.SE_MOON) {
                /* internal planet number */
                ipli = SEI_MOON;
                pdp = swed.pldat[ipli];
                xp = pdp.xreturn;
                switch (epheflag) {
                    case SwissEph.SEFLG_JPLEPH:
                        retc = jplplan(tjd, ipli, iflag, DO_SAVE, null, null, null, ref serr);
                        /* read error or corrupt file */
                        if (retc == ERR)
                            goto return_error;
                        /* jpl ephemeris not on disk or date beyond ephemeris range
                         *     or file corrupt */
                        if (retc == NOT_AVAILABLE) {
                            iflag = (iflag & ~SwissEph.SEFLG_JPLEPH) | SwissEph.SEFLG_SWIEPH;
                            serr += " \ntrying Swiss Eph; ";
                            goto sweph_moon;
                        } else if (retc == BEYOND_EPH_LIMITS) {
                            if (tjd > MOSHLUEPH_START && tjd < MOSHLUEPH_END) {
                                iflag = (iflag & ~SwissEph.SEFLG_JPLEPH) | SwissEph.SEFLG_MOSEPH;
                                serr += " \nusing Moshier Eph; ";
                                goto moshier_moon;
                            } else
                                goto return_error;
                        }
                        break;
                    case SwissEph.SEFLG_SWIEPH:
                    sweph_moon:
                        //#if 0
                        //    /* for hel. or bary. position, we need earth and sun as well;
                        //         * this is done by sweplan(), but not by swemoon() */
                        //        if (iflag & (SEFLG_HELCTR | SEFLG_BARYCTR | SEFLG_NOABERR))
                        //      retc = sweplan(tjd, ipli, SEI_FILE_MOON, iflag, DO_SAVE,
                        //            NULL, NULL, NULL, NULL, serr);
                        //    else
                        //      retc = swemoon(tjd, iflag, DO_SAVE, pdp.x, serr);/**/
                        //#else
                        retc = sweplan(tjd, ipli, SEI_FILE_MOON, iflag, DO_SAVE,
                                null, null, null, null, ref serr);
                        //#endif
                        if (retc == ERR)
                            goto return_error;
                        /* if sweph file not found, switch to moshier */
                        if (retc == NOT_AVAILABLE) {
                            if (tjd > MOSHLUEPH_START && tjd < MOSHLUEPH_END) {
                                iflag = (iflag & ~SwissEph.SEFLG_SWIEPH) | SwissEph.SEFLG_MOSEPH;
                                serr = " \nusing Moshier eph.; ";
                                goto moshier_moon;
                            } else
                                goto return_error;
                        }
                        break;
                    case SwissEph.SEFLG_MOSEPH:
                    moshier_moon:
                        retc = SE.SwemMoon.swi_moshmoon(tjd, DO_SAVE, null, ref serr);/**/
                        if (retc == ERR)
                            goto return_error;
                        /* for hel. position, we need earth as well */
                        retc = SE.SwemPlan.swi_moshplan(tjd, SEI_EARTH, DO_SAVE, null, null, ref serr);/**/
                        if (retc == ERR)
                            goto return_error;
                        break;
                    default:
                        break;
                }
                /* heliocentric, lighttime etc. */
                if ((retc = app_pos_etc_moon(iflag, ref serr)) != OK)
                    goto return_error; /* retc may be wrong with sidereal calculation */
                /**********************************************
                 * barycentric sun                            *
                 * (only JPL and SWISSEPH ephemerises)        *
                 **********************************************/
            } else if (ipl == SwissEph.SE_SUN && (iflag & SwissEph.SEFLG_BARYCTR) != 0) {
                /* barycentric sun must be handled separately because of
                 * the following reasons:
                 * ordinary planetary computations use the function
                 * main_planet() and its subfunction jplplan(),
                 * see further below.
                 * now, these functions need the swisseph internal
                 * planetary indices, where SEI_EARTH = SEI_SUN = 0.
                 * therefore they don't know the difference between
                 * a barycentric sun and a barycentric earth and
                 * always return barycentric earth.
                 * to avoid this problem, many functions would have to
                 * be changed. as an alternative, we choose a more
                 * separate handling. */
                ipli = SEI_SUN;	/* = SEI_EARTH ! */
                xp = pedp.xreturn;
                switch (epheflag) {
                    case SwissEph.SEFLG_JPLEPH:
                        /* open ephemeris, if still closed */
                        if (!swed.jpl_file_is_open) {
                            retc = open_jpl_file(ss, swed.jplfnam, swed.ephepath, ref serr);
                            if (retc != OK)
                                goto sweph_sbar;
                        }
                        retc = SE.SweJPL.swi_pleph(tjd, SweJPL.J_SUN, SweJPL.J_SBARY, psdp.x, ref serr);
                        if (retc == ERR || retc == BEYOND_EPH_LIMITS) {
                            SE.SweJPL.swi_close_jpl_file();
                            swed.jpl_file_is_open = false;
                            goto return_error;
                        }
                        /* jpl ephemeris not on disk or date beyond ephemeris range
                     *     or file corrupt */
                        if (retc == NOT_AVAILABLE) {
                            iflag = (iflag & ~SwissEph.SEFLG_JPLEPH) | SwissEph.SEFLG_SWIEPH;
                            serr = (serr ?? String.Empty) + serr;
                            goto sweph_sbar;
                        }
                        psdp.teval = tjd;
                        break;
                    case SwissEph.SEFLG_SWIEPH:
                    sweph_sbar:
                        /* sweplan() provides barycentric sun as a by-product in save area;
                         * it is saved in swed.pldat[SEI_SUNBARY].x */
                        retc = sweplan(tjd, SEI_EARTH, SEI_FILE_PLANET, iflag, DO_SAVE, null, null, null, null, ref serr);
                        //#if 1
                        if (retc == ERR || retc == NOT_AVAILABLE)
                            goto return_error;
                        //#else	/* this code would be needed if barycentric moshier calculation
                        //     * were implemented */
                        //    if (retc == ERR)
                        //      goto return_error;
                        //    /* if sweph file not found, switch to moshier */
                        //        if (retc == NOT_AVAILABLE) {
                        //      if (tjd > MOSHLUEPH_START && tjd < MOSHLUEPH_END) {
                        //        iflag = (iflag & ~SEFLG_SWIEPH) | SEFLG_MOSEPH;
                        //        if (serr != NULL && strlen(serr) + 30 < AS_MAXCH)
                        //          serr+= " \nusing Moshier; ";
                        //        goto moshier_sbar;
                        //      } else
                        //        goto return_error;
                        //    }
                        //#endif
                        psdp.teval = tjd;
                        /* pedp->teval = tjd; */
                        break;
                    default:
                        //#if 0
                        //    moshier_sbar:
                        //#endif
                        return ERR;
                    //break;
                }
                /* flags */
                if ((retc = app_pos_etc_sbar(iflag, ref serr)) != OK)
                    goto return_error;
                /* iflag has possibly changed */
                iflag = pedp.xflgs;
                /* barycentric sun is now in save area of barycentric earth.
                 * (pedp->xreturn = swed.pldat[SEI_EARTH].xreturn).
                 * in case a barycentric earth computation follows for the same
                 * date, the planetary functions will return the barycentric
                 * SUN unless we force a new computation of pedp->xreturn.
                 * this can be done by initializing the save of iflag.
                 */
                pedp.xflgs = -1;
                /******************************************
                 * mercury - pluto                        *
                 ******************************************/
            } else if (ipl == SwissEph.SE_SUN 	/* main planet */
                || ipl == SwissEph.SE_MERCURY
                || ipl == SwissEph.SE_VENUS
                || ipl == SwissEph.SE_MARS
                || ipl == SwissEph.SE_JUPITER
                || ipl == SwissEph.SE_SATURN
                || ipl == SwissEph.SE_URANUS
                || ipl == SwissEph.SE_NEPTUNE
                || ipl == SwissEph.SE_PLUTO
                || ipl == SwissEph.SE_EARTH) {
                if ((iflag & SwissEph.SEFLG_HELCTR) != 0) {
                    if (ipl == SwissEph.SE_SUN) {
                        /* heliocentric position of Sun does not exist */
                        for (i = 0; i < 24; i++)
                            x[i] = 0;
                        return iflag;
                    }
                } else if ((iflag & SwissEph.SEFLG_BARYCTR) != 0) {
                    ;
                } else {		/* geocentric */
                    if (ipl == SwissEph.SE_EARTH) {
                        /* geocentric position of Earth does not exist */
                        for (i = 0; i < 24; i++)
                            x[i] = 0;
                        return iflag;
                    }
                }
                /* internal planet number */
                ipli = pnoext2int[ipl];
                pdp = swed.pldat[ipli];
                xp = pdp.xreturn;
                retc = main_planet(tjd, ipli, epheflag, iflag, ref serr);
                if (retc == ERR)
                    goto return_error;
                /* iflag has possibly changed in main_planet() */
                iflag = pdp.xflgs;
                /*********************i************************
                 * mean lunar node                            *
                 * for comment s. moshmoon.c, swi_mean_node() *
                 **********************************************/
            } else if (ipl == SwissEph.SE_MEAN_NODE) {
                if ((iflag & SwissEph.SEFLG_HELCTR) != 0 || (iflag & SwissEph.SEFLG_BARYCTR) != 0) {
                    /* heliocentric/barycentric lunar node not allowed */
                    for (i = 0; i < 24; i++)
                        x[i] = 0;
                    return iflag;
                }
                ndp = swed.nddat[SEI_MEAN_NODE];
                xp = ndp.xreturn;
                xp2 = ndp.x;
                retc = SE.SwemMoon.swi_mean_node(tjd, xp2, ref serr);
                if (retc == ERR)
                    goto return_error;
                /* speed (is almost constant; variation < 0.001 arcsec) */
                retc = SE.SwemMoon.swi_mean_node(tjd - MEAN_NODE_SPEED_INTV, xp2.GetPointer(3), ref serr);
                if (retc == ERR)
                    goto return_error;
                xp2[3] = SE.swe_difrad2n(xp2[0], xp2[3]) / MEAN_NODE_SPEED_INTV;
                xp2[4] = xp2[5] = 0;
                ndp.teval = tjd;
                ndp.xflgs = -1;
                /* lighttime etc. */
                if ((retc = app_pos_etc_mean(SEI_MEAN_NODE, iflag, ref serr)) != OK)
                    goto return_error;
                /* to avoid infinitesimal deviations from latitude = 0
                 * that result from conversions */
                if ((iflag & SwissEph.SEFLG_SIDEREAL) == 0 && (iflag & SwissEph.SEFLG_J2000) == 0) {
                    ndp.xreturn[1] = 0.0;	/* ecl. latitude       */
                    ndp.xreturn[4] = 0.0;	/*               speed */
                    ndp.xreturn[5] = 0.0;	/*      radial   speed */
                    ndp.xreturn[8] = 0.0;	/* z coordinate        */
                    ndp.xreturn[11] = 0.0;	/*               speed */
                }
                if (retc == ERR)
                    goto return_error;
                /**********************************************
                 * mean lunar apogee ('dark moon', 'lilith')  *
                 * for comment s. moshmoon.c, swi_mean_apog() *
                 **********************************************/
            } else if (ipl == SwissEph.SE_MEAN_APOG) {
                if ((iflag & SwissEph.SEFLG_HELCTR) != 0 || (iflag & SwissEph.SEFLG_BARYCTR) != 0) {
                    /* heliocentric/barycentric lunar apogee not allowed */
                    for (i = 0; i < 24; i++)
                        x[i] = 0;
                    return iflag;
                }
                ndp = swed.nddat[SEI_MEAN_APOG];
                xp = ndp.xreturn;
                xp2 = ndp.x;
                retc = SE.SwemMoon.swi_mean_apog(tjd, xp2, ref serr);
                if (retc == ERR)
                    goto return_error;
                /* speed (is not constant! variation ~= several arcsec) */
                retc = SE.SwemMoon.swi_mean_apog(tjd - MEAN_NODE_SPEED_INTV, xp2.GetPointer(3), ref serr);
                if (retc == ERR)
                    goto return_error;
                for (i = 0; i <= 1; i++)
                    xp2[3 + i] = SE.swe_difrad2n(xp2[i], xp2[3 + i]) / MEAN_NODE_SPEED_INTV;
                xp2[5] = 0;
                ndp.teval = tjd;
                ndp.xflgs = -1;
                /* lighttime etc. */
                if ((retc = app_pos_etc_mean(SEI_MEAN_APOG, iflag, ref serr)) != OK)
                    goto return_error;
                /* to avoid infinitesimal deviations from r-speed = 0
                 * that result from conversions */
                ndp.xreturn[5] = 0.0;	/*               speed */
                if (retc == ERR)
                    goto return_error;
                /***********************************************
                 * osculating lunar node ('true node')         *
                 ***********************************************/
            } else if (ipl == SwissEph.SE_TRUE_NODE) {
                if ((iflag & SwissEph.SEFLG_HELCTR) != 0 || (iflag & SwissEph.SEFLG_BARYCTR) != 0) {
                    /* heliocentric/barycentric lunar node not allowed */
                    for (i = 0; i < 24; i++)
                        x[i] = 0;
                    return iflag;
                }
                ndp = swed.nddat[SEI_TRUE_NODE];
                xp = ndp.xreturn;
                retc = lunar_osc_elem(tjd, SEI_TRUE_NODE, iflag, ref serr);
                iflag = ndp.xflgs;
                /* to avoid infinitesimal deviations from latitude = 0
                 * that result from conversions */
                if ((iflag & SwissEph.SEFLG_SIDEREAL) == 0 && (iflag & SwissEph.SEFLG_J2000) == 0) {
                    ndp.xreturn[1] = 0.0;	/* ecl. latitude       */
                    ndp.xreturn[4] = 0.0;	/*               speed */
                    ndp.xreturn[8] = 0.0;	/* z coordinate        */
                    ndp.xreturn[11] = 0.0;	/*               speed */
                }
                if (retc == ERR)
                    goto return_error;
                /***********************************************
                 * osculating lunar apogee                     *
                 ***********************************************/
            } else if (ipl == SwissEph.SE_OSCU_APOG) {
                if ((iflag & SwissEph.SEFLG_HELCTR) != 0 || (iflag & SwissEph.SEFLG_BARYCTR) != 0) {
                    /* heliocentric/barycentric lunar apogee not allowed */
                    for (i = 0; i < 24; i++)
                        x[i] = 0;
                    return iflag;
                }
                if (tjd < MOSHLUEPH_START || tjd > MOSHLUEPH_END)
                {
                    for (i = 0; i < 24; i++)
                        x[i] = 0;
                    serr = C.sprintf("Interpolated apsides are restricted to JD %8.1f - JD %8.1f",
                            MOSHLUEPH_START, MOSHLUEPH_END);
                    return ERR;
                }
                ndp = swed.nddat[SEI_OSCU_APOG];
                xp = ndp.xreturn;
                retc = lunar_osc_elem(tjd, SEI_OSCU_APOG, iflag, ref serr);
                iflag = ndp.xflgs;
                if (retc == ERR)
                    goto return_error;
                /***********************************************
                 * interpolated lunar apogee                   *
                 ***********************************************/
            } else if (ipl == SwissEph.SE_INTP_APOG) {
                if ((iflag & SwissEph.SEFLG_HELCTR) != 0 || (iflag & SwissEph.SEFLG_BARYCTR) != 0) {
                    /* heliocentric/barycentric lunar apogee not allowed */
                    for (i = 0; i < 24; i++)
                        x[i] = 0;
                    return iflag;
                }
                if (tjd < MOSHLUEPH_START || tjd > MOSHLUEPH_END)
                {
                    for (i = 0; i < 24; i++)
                        x[i] = 0;
                    serr = C.sprintf("Interpolated apsides are restricted to JD %8.1f - JD %8.1f",
                            MOSHLUEPH_START, MOSHLUEPH_END);
                    return ERR;
                }
                ndp = swed.nddat[SEI_INTP_APOG];
                xp = ndp.xreturn;
                retc = intp_apsides(tjd, SEI_INTP_APOG, iflag, ref serr);
                iflag = ndp.xflgs;
                if (retc == ERR)
                    goto return_error;
                /***********************************************
                 * interpolated lunar perigee                  *
                 ***********************************************/
            } else if (ipl == SwissEph.SE_INTP_PERG) {
                if ((iflag & SwissEph.SEFLG_HELCTR) != 0 || (iflag & SwissEph.SEFLG_BARYCTR) != 0) {
                    /* heliocentric/barycentric lunar apogee not allowed */
                    for (i = 0; i < 24; i++)
                        x[i] = 0;
                    return iflag;
                }
                ndp = swed.nddat[SEI_INTP_PERG];
                xp = ndp.xreturn;
                retc = intp_apsides(tjd, SEI_INTP_PERG, iflag, ref serr);
                iflag = ndp.xflgs;
                if (retc == ERR)
                    goto return_error;
                /***********************************************
                 * minor planets                               *
                 ***********************************************/
            } else if (ipl == SwissEph.SE_CHIRON
              || ipl == SwissEph.SE_PHOLUS
              || ipl == SwissEph.SE_CERES		/* Ceres - Vesta */
              || ipl == SwissEph.SE_PALLAS
              || ipl == SwissEph.SE_JUNO
              || ipl == SwissEph.SE_VESTA
              || ipl > SwissEph.SE_AST_OFFSET) {
                /* internal planet number */
                if (ipl < SwissEph.SE_NPLANETS)
                    ipli = pnoext2int[ipl];
                else if (ipl <= SwissEph.SE_AST_OFFSET + MPC_VESTA) {
                    ipli = SEI_CERES + ipl - SwissEph.SE_AST_OFFSET - 1;
                    ipl = SwissEph.SE_CERES + ipl - SwissEph.SE_AST_OFFSET - 1;
                    //#if 0
                    //    } else if (ipl == SE_AST_OFFSET + MPC_CHIRON) {
                    //      ipli = SEI_CHIRON;
                    //      ipl = SE_CHIRON;
                    //    } else if (ipl == SE_AST_OFFSET + MPC_PHOLUS) {
                    //      ipli = SEI_PHOLUS;
                    //      ipl = SE_PHOLUS;
                    //#endif
                } else {			/* any asteroid except*/
                    ipli = SEI_ANYBODY;
                }
                if (ipli == SEI_ANYBODY)
                    ipli_ast = ipl;
                else
                    ipli_ast = ipli;
                pdp = swed.pldat[ipli];
                xp = pdp.xreturn;
                if (ipli_ast > SwissEph.SE_AST_OFFSET)
                    ifno = SEI_FILE_ANY_AST;
                else
                    ifno = SEI_FILE_MAIN_AST;
                if (ipli == SEI_CHIRON && (tjd < CHIRON_START || tjd > CHIRON_END)) {
                    serr = C.sprintf("Chiron's ephemeris is restricted to JD %8.1f - JD %8.1f",
                        CHIRON_START, CHIRON_END);
                    return ERR;
                }
                if (ipli == SEI_PHOLUS && (tjd < PHOLUS_START || tjd > PHOLUS_END)) {
                    serr = C.sprintf(
                        "Pholus's ephemeris is restricted to JD %8.1f - JD %8.1f",
                        PHOLUS_START, PHOLUS_END);
                    return ERR;
                }
            do_asteroid:
                /* earth and sun are also needed */
                retc = main_planet(tjd, SEI_EARTH, epheflag, iflag, ref serr);
                if (retc == ERR)
                    goto return_error;
                /* iflag (ephemeris bit) has possibly changed in main_planet() */
                iflag = swed.pldat[SEI_EARTH].xflgs;
                /* asteroid */
                if (!String.IsNullOrWhiteSpace(serr)) {
                    serr2 = serr;
                    serr = String.Empty;
                }
                /* asteroid */
                retc = sweph(tjd, ipli_ast, ifno, iflag, psdp.x, DO_SAVE, null, ref serr);
                if (retc == ERR || retc == NOT_AVAILABLE)
                    goto return_error;
                retc = app_pos_etc_plan(ipli_ast, iflag, ref serr);
                if (retc == ERR)
                    goto return_error;
                /* app_pos_etc_plan() might have failed, if t(light-time)
                 * is beyond ephemeris range. in this case redo with Moshier
                 */
                if (retc == NOT_AVAILABLE || retc == BEYOND_EPH_LIMITS) {
                    if (epheflag != SwissEph.SEFLG_MOSEPH) {
                        iflag = (iflag & ~SwissEph.SEFLG_EPHMASK) | SwissEph.SEFLG_MOSEPH;
                        epheflag = SwissEph.SEFLG_MOSEPH;
                        serr += "\nusing Moshier eph.; ";
                        goto do_asteroid;
                    } else
                        goto return_error;
                }
                /* add warnings from earth/sun computation */
                if (String.IsNullOrWhiteSpace(serr) && !String.IsNullOrWhiteSpace(serr2))
                    serr = "sun: " + serr2;
                /***********************************************
                 * fictitious planets                          *
                 * (Isis-Transpluto and Uranian planets)       *
                 ***********************************************/
            } else if (ipl >= SwissEph.SE_FICT_OFFSET && ipl <= SwissEph.SE_FICT_MAX) {
                //#if 0
                //       ipl == SE_CUPIDO
                //    || ipl == SE_HADES
                //    || ipl == SE_ZEUS
                //    || ipl == SE_KRONOS
                //    || ipl == SE_APOLLON
                //    || ipl == SE_ADMETOS
                //    || ipl == SE_VULKANUS
                //    || ipl == SE_POSEIDON
                //    || ipl == SE_ISIS
                //    || ipl == SE_NEPTUNE_LEVERRIER
                //    || ipl == SE_NEPTUNE_ADAMS)
                //#endif
                /* internal planet number */
                ipli = SEI_ANYBODY;
                pdp = swed.pldat[ipli];
                xp = pdp.xreturn;
            do_fict_plan:
                /* the earth for geocentric position */
                retc = main_planet(tjd, SEI_EARTH, epheflag, iflag, ref serr);
                /* iflag (ephemeris bit) has possibly changed in main_planet() */
                iflag = swed.pldat[SEI_EARTH].xflgs;
                /* planet from osculating elements */
                if (SE.SwemPlan.swi_osc_el_plan(tjd, pdp.x, ipl - SwissEph.SE_FICT_OFFSET, ipli, pedp.x, psdp.x, ref serr) != OK)
                    goto return_error;
                if (retc == ERR)
                    goto return_error;
                retc = app_pos_etc_plan_osc(ipl, ipli, iflag, ref serr);
                if (retc == ERR)
                    goto return_error;
                /* app_pos_etc_plan_osc() might have failed, if t(light-time)
                 * is beyond ephemeris range. in this case redo with Moshier
                 */
                if (retc == NOT_AVAILABLE || retc == BEYOND_EPH_LIMITS) {
                    if (epheflag != SwissEph.SEFLG_MOSEPH) {
                        iflag = (iflag & ~SwissEph.SEFLG_EPHMASK) | SwissEph.SEFLG_MOSEPH;
                        epheflag = SwissEph.SEFLG_MOSEPH;
                        serr += "\nusing Moshier eph.; ";
                        goto do_fict_plan;
                    } else
                        goto return_error;
                }
                /***********************************************
                 * invalid body number                         *
                 ***********************************************/
            } else {
                serr = C.sprintf("illegal planet number %d.", ipl);
                goto return_error;
            }
            for (i = 0; i < 24; i++)
                x[i] = xp[i];
            return (iflag);
            /***********************************************
             * return error                                *
             ***********************************************/
            return_error: ;
            for (i = 0; i < 24; i++)
                x[i] = 0;
            return ERR;
        }

        /* SWISSEPH
         * this routine computes heliocentric cartesian equatorial coordinates
         * of equinox 2000 of
         * geocentric moon
         *
         * tjd 		julian date
         * iflag	flag
         * do_save	save J2000 position in save area pdp->x ?
         * xp		array of 6 doubles for lunar position and speed
         * serr		error string
         */
        int swemoon(double tjd, Int32 iflag, bool do_save, CPointer<double> xpret, ref string serr)
        {
            int i, retc;
            plan_data pdp = swed.pldat[SEI_MOON];
            Int32 speedf1, speedf2;
            double[] xx = new double[6]; CPointer<double> xp;
            if (do_save)
                xp = pdp.x;
            else
                xp = xx;
            /* if planet has already been computed for this date, return
             * if speed flag has been turned on, recompute planet */
            speedf1 = pdp.xflgs & SwissEph.SEFLG_SPEED;
            speedf2 = iflag & SwissEph.SEFLG_SPEED;
            if (tjd == pdp.teval
              && pdp.iephe == SwissEph.SEFLG_SWIEPH
              && (0 == speedf2 || speedf1 != 0)) {
                xp = pdp.x;
            } else {
                /* call sweph for moon */
                retc = sweph(tjd, SEI_MOON, SEI_FILE_MOON, iflag, null, do_save, xp, ref serr);
                if (retc != OK)
                    return (retc);
                if (do_save) {
                    pdp.teval = tjd;
                    pdp.xflgs = -1;
                    pdp.iephe = SwissEph.SEFLG_SWIEPH;
                }
            }
            if (xpret != null)
                for (i = 0; i <= 5; i++)
                    xpret[i] = xp[i];
            return (OK);
        }

        /*
         * this function looks for an ephemeris file,
         * opens it, if not yet open,
         * reads constants, if not yet read,
         * computes a planet, if not yet computed
         * attention: asteroids are heliocentric
         *            other planets barycentric
         *
         * tjd 		julian date
         * ipli		SEI_ planet number
         * ifno		ephemeris file number
         * xsunb	INPUT (!) array of 6 doubles containing barycentric sun
         *              (must be given with asteroids)
         * do_save	boolean: save result in save area
         * xp		return array of 6 doubles for planet's position
         * serr		error string
         */
        int sweph(double tjd, int ipli, int ifno, Int32 iflag, CPointer<double> xsunb, bool do_save, CPointer<double> xpret, ref string serr)
        {
            int i, ipl, retc, subdirlen;
            string s = String.Empty, subdirnam = String.Empty, fname = String.Empty/*, sp*/;
            int spi;
            double t, tsv;
            double[] xemb = new double[6], xx = new double[6], xp;
            plan_data pdp;
            plan_data pedp = swed.pldat[SEI_EARTH];
            plan_data psdp = swed.pldat[SEI_SUNBARY];
            file_data fdp = swed.fidat[ifno];
            Int32 speedf1, speedf2;
            bool need_speed;
            ipl = ipli;
            if (ipli > SwissEph.SE_AST_OFFSET)
                ipl = SEI_ANYBODY;
            pdp = swed.pldat[ipl];
            if (do_save)
                xp = pdp.x;
            else
                xp = xx;
            /* if planet has already been computed for this date, return.
             * if speed flag has been turned on, recompute planet */
            speedf1 = pdp.xflgs & SwissEph.SEFLG_SPEED;
            speedf2 = iflag & SwissEph.SEFLG_SPEED;
            if (tjd == pdp.teval
              && pdp.iephe == SwissEph.SEFLG_SWIEPH
              && (0 == speedf2 || speedf1 != 0)
                  && ipl < SEI_ANYBODY) {
                if (xpret != null)
                    for (i = 0; i <= 5; i++)
                        xpret[i] = pdp.x[i];
                return (OK);
            }
            /******************************
             * get correct ephemeris file *
             ******************************/
            if (fdp.fptr != null) {
                /* if tjd is beyond file range, close old file.
                 * if new asteroid, close old file. */
                if (tjd < fdp.tfstart || tjd > fdp.tfend
                  || (ipl == SEI_ANYBODY && ipli != pdp.ibdy)) {
                    //fclose(fdp.fptr);
                    fdp.fptr.Dispose();
                    fdp.fptr = null;
                    //if (pdp.refep != null)
                    //    free((void*)pdp.refep);
                    pdp.refep = null;
                    //if (pdp.segp != null)
                    //    free((void*)pdp.segp);
                    pdp.segp = null;
                }
            }
            /* if sweph file not open, find and open it */
            if (fdp.fptr == null) {
                SE.SwephLib.swi_gen_filename(tjd, ipli, out fname);
                //sp = strrchr(subdirnam, (int)*DIR_GLUE);
                //if (sp != NULL) {
                //    *sp = '\0';
                //    subdirlen = strlen(subdirnam);
                //} else {
                //    subdirlen = 0;
                //}
                spi = fname.LastIndexOf(SwissEph.DIR_GLUE);
                if (spi > 0) {
                    subdirnam = fname.Substring(0, spi);
                    subdirlen = subdirnam.Length;
                } else {
                    subdirlen = 0;
                }
                s = fname;
            again:
                fdp.fptr = swi_fopen(ifno, s, swed.ephepath, ref serr);
                if (fdp.fptr == null) {
                    /*
                     * if it is a numbered asteroid file, try also for short files (..s.se1)
                     * On the second try, the inserted 's' will be seen and not tried again.
                     */
                    if (ipli > SwissEph.SE_AST_OFFSET) {
                        int sppi = s.IndexOf('.');
                        //if (spp > s && *(spp - 1) != 's') {	/* no 's' before '.' ? */
                        //    spp=C.sprintf("s.%s", SE_FILE_SUFFIX);	/* insert an 's' */
                        //    goto again;
                        //}
                        if (sppi > 1 && s[sppi - 1] != 's') {
                            s = C.sprintf("%ss.%s", s.Substring(0, sppi), SE_FILE_SUFFIX);
                            goto again;
                        }
                        /*
                         * if we still have 'ast0' etc. in front of the filename,
                         * we remove it now, remove the 's' also,
                         * and try in the main ephemeris directory instead of the
                         * asteroid subdirectory.
                         */
                        sppi--;	/* point to the character before '.' which must be a 's' */
                        // remove the s
                        s = s.Substring(0, sppi) + s.Substring(sppi + 1);
                        if (s.StartsWith(subdirnam)) {
                            s = s.Substring(subdirlen + 1);
                            goto again;
                        }
                    }
                    return (NOT_AVAILABLE);
                }
                /* during the search error messages may have been built, delete them */
                serr = String.Empty;
                retc = read_const(ifno, ref serr);
                if (retc != OK)
                    return (retc);
            }
            /* if first ephemeris file (J-3000), it might start a mars period
             * after -3000. if last ephemeris file (J3000), it might end a
             * 4000-day-period before 3000. */
            if (tjd < fdp.tfstart || tjd > fdp.tfend) {
                if (tjd < fdp.tfstart)
                    s = C.sprintf("jd %f < Swiss Eph. lower limit %f;",
                          tjd, fdp.tfstart);
                else
                    s = C.sprintf("jd %f > Swiss Eph. upper limit %f;",
                          tjd, fdp.tfend);
                //if (strlen(serr) + strlen(s) < AS_MAXCH)
                serr += s;
                return (NOT_AVAILABLE);
            }
            /******************************
             * get planet's position
             ******************************/
            /* get new segment, if necessary */
            if (pdp.segp == null || tjd < pdp.tseg0 || tjd > pdp.tseg1) {
                retc = get_new_segment(tjd, ipl, ifno, ref serr);
                if (retc != OK)
                    return (retc);
                /* rotate cheby coeffs back to equatorial system.
                 * if necessary, add reference orbit. */
                if ((pdp.iflg & SEI_FLG_ROTATE) != 0)
                    rot_back(ipl); /**/
                else
                    pdp.neval = pdp.ncoe;
            }
            /* evaluate chebyshew polynomial for tjd */
            t = (tjd - pdp.tseg0) / pdp.dseg;
            t = t * 2 - 1;
            /* speed is needed, if
             * 1. true position is being computed before applying light-time etc.
             *    this is the position saved in pdp.x.
             *    in this case, speed is needed for light-time correction.
             * 2. the speed flag has been specified.
             */
            need_speed = (do_save || (iflag & SwissEph.SEFLG_SPEED) != 0);
            for (i = 0; i <= 2; i++) {
                xp[i] = SE.SwephLib.swi_echeb(t, pdp.segp.GetPointer(i * pdp.ncoe), pdp.neval);
                if (need_speed)
                    xp[i + 3] = SE.SwephLib.swi_edcheb(t, pdp.segp.GetPointer(i * pdp.ncoe), pdp.neval) / pdp.dseg * 2;
                else
                    xp[i + 3] = 0;	/* von Alois als billiger fix, evtl. illegal */
            }
            /* if planet wanted is barycentric sun and must be computed
             * from heliocentric earth and barycentric earth: the
             * computation above gives heliocentric earth, therefore we
             * have to compute barycentric earth and subtract heliocentric
             * earth from it. this may be necessary with calls from
             * sweplan() and from app_pos_etc_sun() (light-time). */
            if (ipl == SEI_SUNBARY && (pdp.iflg & SEI_FLG_EMBHEL) != 0) {
                /* sweph() calls sweph() !!! for EMB.
                 * Attention: a new calculation must be forced in any case.
                 * Otherwise EARTH (instead of EMB) will possibly taken from
                 * save area.
                 * to force new computation, set pedp.teval = 0 and restore it
                 * after call of sweph(EMB).
                 */
                tsv = pedp.teval;
                pedp.teval = 0;
                retc = sweph(tjd, SEI_EMB, ifno, iflag | SwissEph.SEFLG_SPEED, null, NO_SAVE, xemb, ref serr);
                if (retc != OK)
                    return (retc);
                pedp.teval = tsv;
                for (i = 0; i <= 2; i++)
                    xp[i] = xemb[i] - xp[i];
                if (need_speed)
                    for (i = 3; i <= 5; i++)
                        xp[i] = xemb[i] - xp[i];
            }
            //#if 1
            /* asteroids are heliocentric.
             * if JPL or SWISSEPH, convert to barycentric */
            if ((iflag & SwissEph.SEFLG_JPLEPH) != 0 || (iflag & SwissEph.SEFLG_SWIEPH) != 0) {
                if (ipl >= SEI_ANYBODY) {
                    for (i = 0; i <= 2; i++)
                        xp[i] += xsunb[i];
                    if (need_speed)
                        for (i = 3; i <= 5; i++)
                            xp[i] += xsunb[i];
                }
            }
            //#endif
            if (do_save) {
                pdp.teval = tjd;
                pdp.xflgs = -1;	/* do new computation of light-time etc. */
                if (ifno == SEI_FILE_PLANET || ifno == SEI_FILE_MOON)
                    pdp.iephe = SwissEph.SEFLG_SWIEPH;/**/
                else
                    pdp.iephe = psdp.iephe;
            }
            if (xpret != null)
                for (i = 0; i <= 5; i++)
                    xpret[i] = xp[i];
            return (OK);
        }

        /* SWISSEPH
         * this function computes
         * 1. a barycentric planet
         * plus, under certain conditions,
         * 2. the barycentric sun,
         * 3. the barycentric earth, and
         * 4. the geocentric moon,
         * in barycentric cartesian equatorial coordinates J2000.
         *
         * these are the data needed for calculation of light-time etc.
         *
         * tjd 		julian date
         * ipli		SEI_ planet number
         * ifno		ephemeris file number
         * do_save	write new positions in save area
         * xp		array of 6 doubles for planet's position and velocity
         * xpe                                 earth's
         * xps                                 sun's
         * xpm                                 moon's
         * serr		error string
         *
         * xp - xpm can be NULL. if do_save is TRUE, all of them can be NULL.
         * the positions will be written into the save area (swed.pldat[ipli].x)
         */
        int sweplan(double tjd, int ipli, int ifno, Int32 iflag, bool do_save,
                   CPointer<double> xpret, CPointer<double> xperet, CPointer<double> xpsret, CPointer<double> xpmret,
                   ref string serr)
        {
            int i, retc;
            bool do_earth = false, do_moon = false, do_sunbary = false;
            plan_data pdp = swed.pldat[ipli];
            plan_data pebdp = swed.pldat[SEI_EMB];
            plan_data psbdp = swed.pldat[SEI_SUNBARY];
            plan_data pmdp = swed.pldat[SEI_MOON];
            double[] xxp = new double[6], xxm = new double[6], xxs = new double[6], xxe = new double[6];
            CPointer<double> xp, xpe, xpm, xps;
            Int32 speedf1, speedf2;
            /* xps (barycentric sun) may be necessary because some planets on sweph
             * file are heliocentric, other ones are barycentric. without xps,
             * the heliocentric ones cannot be returned barycentrically.
             */
            if (do_save || ipli == SEI_SUNBARY || (pdp.iflg & SEI_FLG_HELIO) != 0
              || xpsret != null || (iflag & SwissEph.SEFLG_HELCTR) != 0)
                do_sunbary = true;
            if (do_save || ipli == SEI_EARTH || xperet != null)
                do_earth = true;
            if (ipli == SEI_MOON) {
                //#if 0
                //  if (iflag & (SEFLG_HELCTR | SEFLG_BARYCTR | SEFLG_NOABERR))
                //      do_earth = TRUE;
                //  if (iflag & (SEFLG_HELCTR | SEFLG_NOABERR))
                //      do_sunbary = TRUE;
                //#else
                do_earth = true;
                do_sunbary = true;
                //#endif
            }
            if (do_save || ipli == SEI_MOON || ipli == SEI_EARTH || xperet != null || xpmret != null)
                do_moon = true;
            if (do_save) {
                xp = pdp.x;
                xpe = pebdp.x;
                xps = psbdp.x;
                xpm = pmdp.x;
            } else {
                xp = xxp;
                xpe = xxe;
                xps = xxs;
                xpm = xxm;
            }
            speedf2 = iflag & SwissEph.SEFLG_SPEED;
            /* barycentric sun */
            if (do_sunbary) {
                speedf1 = psbdp.xflgs & SwissEph.SEFLG_SPEED;
                /* if planet has already been computed for this date, return
                 * if speed flag has been turned on, recompute planet */
                if (tjd == psbdp.teval
                  && psbdp.iephe == SwissEph.SEFLG_SWIEPH
                  && (speedf2 == 0 || speedf1 != 0)) {
                    for (i = 0; i <= 5; i++)
                        xps[i] = psbdp.x[i];
                } else {
                    retc = sweph(tjd, SEI_SUNBARY, SEI_FILE_PLANET, iflag, null, do_save, xps, ref serr);/**/
                    if (retc != OK)
                        return (retc);
                }
                if (xpsret != null)
                    for (i = 0; i <= 5; i++)
                        xpsret[i] = xps[i];
            }
            /* moon */
            if (do_moon) {
                speedf1 = pmdp.xflgs & SwissEph.SEFLG_SPEED;
                if (tjd == pmdp.teval
                  && pmdp.iephe == SwissEph.SEFLG_SWIEPH
                  && (speedf2 == 0 || speedf1 != 0)) {
                    for (i = 0; i <= 5; i++)
                        xpm[i] = pmdp.x[i];
                } else {
                    retc = sweph(tjd, SEI_MOON, SEI_FILE_MOON, iflag, null, do_save, xpm, ref serr);
                    if (retc == ERR)
                        return (retc);
                    /* if moon file doesn't exist, take moshier moon */
                    if (swed.fidat[SEI_FILE_MOON].fptr == null) {
                        serr = " \nusing Moshier eph. for moon; ";
                        retc = SE.SwemMoon.swi_moshmoon(tjd, do_save, xpm, ref serr);
                        if (retc != OK)
                            return (retc);
                    }
                }
                if (xpmret != null)
                    for (i = 0; i <= 5; i++)
                        xpmret[i] = xpm[i];
            }
            /* barycentric earth */
            if (do_earth) {
                speedf1 = pebdp.xflgs & SwissEph.SEFLG_SPEED;
                if (tjd == pebdp.teval
                  && pebdp.iephe == SwissEph.SEFLG_SWIEPH
                  && (speedf2 == 0 || speedf1 != 0)) {
                    for (i = 0; i <= 5; i++)
                        xpe[i] = pebdp.x[i];
                } else {
                    retc = sweph(tjd, SEI_EMB, SEI_FILE_PLANET, iflag, null, do_save, xpe, ref serr);
                    if (retc != OK)
                        return (retc);
                    /* earth from emb and moon */
                    embofs(xpe, xpm);
                    /* speed is needed, if
                     * 1. true position is being computed before applying light-time etc.
                     *    this is the position saved in pdp.x.
                     *    in this case, speed is needed for light-time correction.
                     * 2. the speed flag has been specified.
                     */
                    if (xpe == pebdp.x || (iflag & SwissEph.SEFLG_SPEED) != 0)
                        embofs(xpe + 3, xpm + 3);
                }
                if (xperet != null)
                    for (i = 0; i <= 5; i++)
                        xperet[i] = xpe[i];
            }
            if (ipli == SEI_MOON) {
                for (i = 0; i <= 5; i++)
                    xp[i] = xpm[i];
            } else if (ipli == SEI_EARTH) {
                for (i = 0; i <= 5; i++)
                    xp[i] = xpe[i];
            } else if (ipli == SEI_SUN) {
                for (i = 0; i <= 5; i++)
                    xp[i] = xps[i];
            } else {
                /* planet */
                speedf1 = pdp.xflgs & SwissEph.SEFLG_SPEED;
                if (tjd == pdp.teval
                  && pdp.iephe == SwissEph.SEFLG_SWIEPH
                  && (speedf2 == 0 || speedf1 != 0)) {
                    for (i = 0; i <= 5; i++)
                        xp[i] = pdp.x[i];
                    return (OK);
                } else {
                    retc = sweph(tjd, ipli, ifno, iflag, null, do_save, xp, ref serr);
                    if (retc != OK)
                        return (retc);
                    /* if planet is heliocentric, it must be transformed to barycentric */
                    if ((pdp.iflg & SEI_FLG_HELIO) != 0) {
                        /* now barycentric planet */
                        for (i = 0; i <= 2; i++)
                            xp[i] += xps[i];
                        if (do_save || (iflag & SwissEph.SEFLG_SPEED) != 0)
                            for (i = 3; i <= 5; i++)
                                xp[i] += xps[i];
                    }
                }
            }
            if (xpret != null)
                for (i = 0; i <= 5; i++)
                    xpret[i] = xp[i];
            return (OK);
        }

        void swi_check_ecliptic(double tjd, Int32 iflag)
        {
            if (swed.oec2000.teps != J2000) {
                calc_epsilon(J2000, iflag, swed.oec2000);
            }
            if (tjd == J2000) {
                swed.oec.teps = swed.oec2000.teps;
                swed.oec.eps = swed.oec2000.eps;
                swed.oec.seps = swed.oec2000.seps;
                swed.oec.ceps = swed.oec2000.ceps;
                return;
            }
            if (swed.oec.teps != tjd || tjd == 0) {
                calc_epsilon(tjd, iflag, swed.oec);
            }
        }

        /* computes nutation, if it is wanted and has not yet been computed.
         * if speed flag has been turned on since last computation,
         * nutation is recomputed */
        void swi_check_nutation(double tjd, Int32 iflag)
        {
            Int32 speedf1, speedf2;
            double t;
            speedf1 = nutflag & SwissEph.SEFLG_SPEED;
            speedf2 = iflag & SwissEph.SEFLG_SPEED;
            if ((iflag & SwissEph.SEFLG_NONUT) == 0
              && (tjd != swed.nut.tnut || tjd == 0
              || (speedf1 == 0 && speedf2 != 0))) {
                  SE.SwephLib.swi_nutation(tjd, iflag, swed.nut.nutlo);
                swed.nut.tnut = tjd;
                swed.nut.snut = Math.Sin(swed.nut.nutlo[1]);
                swed.nut.cnut = Math.Cos(swed.nut.nutlo[1]);
                nutflag = iflag;
                nut_matrix(swed.nut, swed.oec);
                if ((iflag & SwissEph.SEFLG_SPEED) != 0) {
                    /* once more for 'speed' of nutation, which is needed for
                     * planetary speeds */
                    t = tjd - NUT_SPEED_INTV;
                    SE.SwephLib.swi_nutation(t, iflag, swed.nutv.nutlo);
                    swed.nutv.tnut = t;
                    swed.nutv.snut = Math.Sin(swed.nutv.nutlo[1]);
                    swed.nutv.cnut = Math.Cos(swed.nutv.nutlo[1]);
                    nut_matrix(swed.nutv, swed.oec);
                }
            }
        }

        /* closes all open files, frees space of planetary data,
         * deletes memory of all computed positions
         */
        void swi_close_keep_topo_etc()
        {
            int i;
            /* close SWISSEPH files */
            for (i = 0; i < SEI_NEPHFILES; i++)
            {
                if (swed.fidat[i].fptr != null)
                    swed.fidat[i].fptr.Dispose();
                swed.fidat[i] = new file_data();
            }
            free_planets();
            swed.oec = new epsilon();
            swed.oec2000 = new epsilon();
            swed.nut = new nut();
            swed.nut2000 = new nut();
            swed.nutv = new nut();
            swed.astro_models = new Int32[SEI_NMODELS];
            /* close JPL file */
            SE.SweJPL.swi_close_jpl_file();
            swed.jpl_file_is_open = false;
            swed.jpldenum = 0;
            /* close fixed stars */
            if (swed.fixfp != null)
            {
                swed.fixfp.Dispose();
                swed.fixfp = null;
            }
            SE.swe_set_tid_acc(SwissEph.SE_TIDAL_AUTOMATIC);
            swed.is_old_starfile = false;
            swed.i_saved_planet_name = 0;
            swed.saved_planet_name = String.Empty;
            swed.timeout = 0;
        }

        #endregion Methods

        #region Nested Types

        //#define SEFLG_EPHMASK	(SEFLG_JPLEPH|SEFLG_SWIEPH|SEFLG_MOSEPH)
        struct meff_ele
        {
            #region Fields

            public double r, m;

            #endregion Fields

            #region Constructors

            public meff_ele(double ar, double am)
            {
                r = ar;
                m = am;
            }

            #endregion Constructors
        }

        #endregion Nested Types

        #region Other

        //#if 0
        //void FAR PASCAL_CONV swe_set_timeout(int32 tsec)
        //{
        //  if (tsec < 0) tsec = 0;
        //  swed.timeout = tsec;
        //}
        //#endif
        //#if 0
        //int FAR PASCAL_CONV swe_time_equ(double tjd_ut, double *E, char *serr)
        // /* Algorithm according to Meeus, German, p. 190ff.*/
        //  double L0, dpsi, eps, x[6], nutlo[2];
        //  double tau = (tjd - J2000) / 365250;
        //  double tau2 = tau * tau;
        //  double tau3 = tau * tau2;
        //  double tau4 = tau * tau3;
        //  double tau5 = tau * tau4;
        //  L0 = 280.4664567 + swe_degnorm(tau * 360007.6982779)
        //           + tau2 * 0.03032028
        //           + tau3 * 1 / 49931
        //           - tau4 * 1 / 15299
        //           - tau5 * 1 / 1988000;
        //  swi_nutation(tjd, 0, nutlo);
        //  eps = (swi_epsiln(tjd) + nutlo[1]) * RADTODEG;
        //  dpsi = nutlo[0] * RADTODEG;
        //  if (swe_calc(tjd, SE_SUN, SEFLG_EQUATORIAL, x, serr) == ERR)
        //    return ERR;
        //  *E = swe_degnorm(L0 - 0.0057183 - x[0] + dpsi * Math.Cos(eps * DEGTORAD));
        //  if (*E > 180)
        //    *E -= 360;
        //  *E *= 4 / 1440.0;
        //  return OK;
        //}
        //#endif

        #endregion Other

        #if TRACE

        void trace_swe_calc(int swtch, double tjd, int ipl, Int32 iflag, CPointer<double> xx, string serr)
        {
            //  if (swi_trace_count >= TRACE_COUNT_MAX)
            //    return;
            switch (swtch) {
                case 1:
                    //trace(true, "\n/*SWE_CALC*/");
                    //trace(true, "  tjd = %.9f; ipl = %d; iflag = %d;", tjd, ipl, iflag);
                    //trace(true, "  iflgret = swe_calc(tjd, ipl, iflag, xx, serr);");
                    break;
                case 2:
                    //trace(true, "  printf(\"swe_calc: %f\\t%d\\t%d\\t%f\\t%f\\t%f\\t%f\\t%f\\t%f\\t\", ");
                    //trace(true, "\ttjd, ipl, iflgret, xx[0], xx[1], xx[2], xx[3], xx[4], xx[5]);");
                    //trace(true, "  if (*serr != '\\0') printf(serr); printf(\"\\n\");");
                    trace("swe_calc: %f\t%d\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%s", tjd, ipl, iflag, xx[0], xx[1], xx[2], xx[3], xx[4], xx[5], serr);
                    break;
                //    default:
                //      break;
            }
        }

        void trace_swe_fixstar(int swtch, string star, double tjd, Int32 iflag, CPointer<double> xx, string serr)
        {
            //if (swi_trace_count >= TRACE_COUNT_MAX)
            //    return;
            switch (swtch) {
                case 1:
                    //if (swi_fp_trace_c != NULL) {
                    //    fputs("\n/*SWE_FIXSTAR*/\n", swi_fp_trace_c);
                    //    fprintf(swi_fp_trace_c, "  strcpy(star, \"%s\");", star);
                    //    fprintf(swi_fp_trace_c, " tjd = %.9f;", tjd);
                    //    fprintf(swi_fp_trace_c, " iflag = %d;\n", iflag);
                    //    fprintf(swi_fp_trace_c, "  iflgret = swe_fixstar(star, tjd, iflag, xx, serr);");
                    //    fprintf(swi_fp_trace_c, "   /* xx = %d */\n", (int32)xx);
                    //    fflush(swi_fp_trace_c);
                    //}
                    break;
                case 2:
                    //if (swi_fp_trace_c != NULL) {
                    //    fputs("  printf(\"swe_fixstar: %s\\t%f\\t%d\\t%f\\t%f\\t%f\\t%f\\t%f\\t%f\\t\", ", swi_fp_trace_c);
                    //    fputs("\n\tstar, tjd, iflgret, xx[0], xx[1], xx[2], xx[3], xx[4], xx[5]);\n", swi_fp_trace_c);/**/
                    //    fputs("  if (*serr != '\\0')", swi_fp_trace_c);
                    //    fputs(" printf(serr);", swi_fp_trace_c);
                    //    fputs(" printf(\"\\n\");\n", swi_fp_trace_c);
                    //    fflush(swi_fp_trace_c);
                    //}
                    //if (swi_fp_trace_out != NULL) {
                    trace("swe_fixstar: %s\t%f\t%d\t%f\t%f\t%f\t%f\t%f\t%f\t%s",
                          star, tjd, iflag, xx[0], xx[1], xx[2], xx[3], xx[4], xx[5], serr);
                    //if (serr != NULL && *serr != '\0') {
                    //    fputs(serr, swi_fp_trace_out);
                    //}
                    //fputs("\n", swi_fp_trace_out);
                    //fflush(swi_fp_trace_out);
                    //}
                    break;
                default:
                    break;
            }
        }

        void trace_swe_get_planet_name(int swtch, int ipl, string s)
        {
            //if (swi_trace_count >= TRACE_COUNT_MAX)
            //    return;
            switch (swtch) {
                case 1:
                    //if (swi_fp_trace_c != NULL) {
                    //trace(true, "\n/*SWE_GET_PLANET_NAME*/");
                    //trace(true, "  ipl = %d;", ipl);
                    //trace(true, "  swe_get_planet_name(ipl, s);   /* s = %s */", s);
                    //    fflush(swi_fp_trace_c);
                    //}
                    break;
                case 2:
                    //if (swi_fp_trace_c != NULL) {
                    //trace(true, "  printf(\"swe_get_planet_name: %d\\t%s\\t\\n\", ipl, s);", ipl, s);/**/
                    //    fflush(swi_fp_trace_c);
                    //}
                    //if (swi_fp_trace_out != NULL) {
                    trace("swe_get_planet_name: %d\t%s\t", ipl, s);
                    //    fflush(swi_fp_trace_out);
                    //}
                    break;
                //default:
                //    break;
            }
        }

        #endif
    }
示例#4
0
 /*
  * input coordinates are J2000, cartesian.
  * xout 	ecliptical sidereal position
  * xoutr 	equatorial sidereal position
  */
 public int swi_trop_ra2sid_lon(CPointer<double> xin, CPointer<double> xout, CPointer<double> xoutr, Int32 iflag)
 {
     double[] x = new double[6];
     int i;
     sid_data sip = swed.sidd;
     epsilon oectmp = new epsilon();
     for (i = 0; i <= 5; i++)
         x[i] = xin[i];
     if (sip.t0 != J2000) {
         /* iflag must not contain SwissEph.SEFLG_JPLHOR here */
         SE.SwephLib.swi_precess(x, sip.t0, 0, J2000_TO_J);
         SE.SwephLib.swi_precess(x.GetPointer(3), sip.t0, 0, J2000_TO_J);	/* speed */
     }
     for (i = 0; i <= 5; i++)
         xoutr[i] = x[i];
     calc_epsilon(swed.sidd.t0, iflag, oectmp);
     SE.SwephLib.swi_coortrf2(x, x, oectmp.seps, oectmp.ceps);
     if ((iflag & SwissEph.SEFLG_SPEED) != 0)
         SE.SwephLib.swi_coortrf2(x.GetPointer(3), x.GetPointer(3), oectmp.seps, oectmp.ceps);
     /* to polar coordinates */
     SE.SwephLib.swi_cartpol_sp(x, x);
     /* subtract ayan_t0 */
     x[0] -= sip.ayan_t0 * SwissEph.DEGTORAD;
     /* back to cartesian */
     SE.SwephLib.swi_polcart_sp(x, xout);
     return OK;
 }
示例#5
0
        /* transforms the position of the moon in a way we can use it
         * for calculation of osculating node and apogee:
         * precession and nutation (attention to speed vector!)
         * according to flags
         * iflag	flags
         * tjd          time for which the element is computed
         *              i.e. date of ecliptic
         * xx           array equatorial cartesian position and speed
         * serr         error string
         */
        public int swi_plan_for_osc_elem(Int32 iflag, double tjd, CPointer<double> xx)
        {
            int i;
            double[] x = new double[6];
            nut nuttmp = new nut();
            nut nutp = nuttmp;	/* dummy assign, to silence gcc warning */
            epsilon oe = swed.oec;
            epsilon oectmp = new epsilon();
            sid_data sip = new sid_data(); ;
            /* ICRS to J2000 */
            if (0 == (iflag & SwissEph.SEFLG_ICRS) && get_denum(SEI_SUN, iflag) >= 403) {
                SE.SwephLib.swi_bias(xx, tjd, iflag, false);
            }/**/
            /************************************************
             * precession, equator 2000 -> equator of date  *
             * attention: speed vector has to be rotated,   *
             * but daily precession 0.137" may not be added!*/
            /*
            #if SID_TNODE_FROM_ECL_T0
              sid_data sip = &swed.sidd;
              /* For sidereal calculation we need node refered*
               * to ecliptic of t0 of ayanamsa                *
               ************************************************ /
              if (iflag & SEFLG_SIDEREAL) {
            tjd = sip.t0;
            swi_precess(xx, tjd, iflag, J2000_TO_J);
            swi_precess(xx+3, tjd, iflag, J2000_TO_J);
            calc_epsilon(tjd, iflag, &oectmp);
            oe = &oectmp;
              } else if (!(iflag & SEFLG_J2000)) {
            #endif
            swi_precess(xx, tjd, iflag, J2000_TO_J);
            swi_precess(xx + 3, tjd, iflag, J2000_TO_J);
            /* epsilon * /
            if (tjd == swed.oec.teps)
                oe = swed.oec;
            else if (tjd == J2000)
                oe = swed.oec2000;
            else {
                calc_epsilon(tjd, iflag, oectmp);
                oe = oectmp;
            }
            #if SID_TNODE_FROM_ECL_T0
              } else	/* if SEFLG_J2000 * /
            oe = &swed.oec2000;
            #endif
            */

            if (SID_TNODE_FROM_ECL_T0) {
                sip = swed.sidd;
                /* For sidereal calculation we need node refered*
                 * to ecliptic of t0 of ayanamsa                *
                 ************************************************/
                if ((iflag & SwissEph.SEFLG_SIDEREAL) != 0) {
                    tjd = sip.t0;
                    SE.SwephLib.swi_precess(xx, tjd, iflag, J2000_TO_J);
                    SE.SwephLib.swi_precess(xx + 3, tjd, iflag, J2000_TO_J);
                    calc_epsilon(tjd, iflag, oectmp);
                    oe = oectmp;
                } else if (0 == (iflag & SwissEph.SEFLG_J2000)) {
                    SE.SwephLib.swi_precess(xx, tjd, iflag, J2000_TO_J);
                    SE.SwephLib.swi_precess(xx + 3, tjd, iflag, J2000_TO_J);
                    /* epsilon */
                    if (tjd == swed.oec.teps)
                        oe = swed.oec;
                    else if (tjd == J2000)
                        oe = swed.oec2000;
                    else {
                        calc_epsilon(tjd, iflag, oectmp);
                        oe = oectmp;
                    }

                } else	/* if SEFLG_J2000 */
                    oe = swed.oec2000;
            } else {
                SE.SwephLib.swi_precess(xx, tjd, iflag, J2000_TO_J);
                SE.SwephLib.swi_precess(xx + 3, tjd, iflag, J2000_TO_J);
                /* epsilon */
                if (tjd == swed.oec.teps)
                    oe = swed.oec;
                else if (tjd == J2000)
                    oe = swed.oec2000;
                else {
                    calc_epsilon(tjd, iflag, oectmp);
                    oe = oectmp;
                }
            }

            /************************************************
               * nutation                                     *
               * again: speed vector must be rotated, but not *
               * added 'speed' of nutation                    *
               ************************************************/
            if (0 == (iflag & SwissEph.SEFLG_NONUT)) {
                if (tjd == swed.nut.tnut) {
                    nutp = swed.nut;
                } else if (tjd == J2000) {
                    nutp = swed.nut2000;
                } else if (tjd == swed.nutv.tnut) {
                    nutp = swed.nutv;
                } else {
                    nutp = nuttmp;
                    SE.SwephLib.swi_nutation(tjd, iflag, nutp.nutlo);
                    nutp.tnut = tjd;
                    nutp.snut = Math.Sin(nutp.nutlo[1]);
                    nutp.cnut = Math.Cos(nutp.nutlo[1]);
                    nut_matrix(nutp, oe);
                }
                for (i = 0; i <= 2; i++) {
                    x[i] = xx[0] * nutp.matrix[0, i] +
                       xx[1] * nutp.matrix[1, i] +
                       xx[2] * nutp.matrix[2, i];
                }
                /* speed:
                 * rotation only */
                for (i = 0; i <= 2; i++) {
                    x[i + 3] = xx[3] * nutp.matrix[0, i] +
                         xx[4] * nutp.matrix[1, i] +
                         xx[5] * nutp.matrix[2, i];
                }
                for (i = 0; i <= 5; i++)
                    xx[i] = x[i];
            }
            /************************************************
             * transformation to ecliptic                   *
             ************************************************/
            SE.SwephLib.swi_coortrf2(xx, xx, oe.seps, oe.ceps);
            SE.SwephLib.swi_coortrf2(xx + 3, xx + 3, oe.seps, oe.ceps);
            if (SID_TNODE_FROM_ECL_T0 && (iflag & SwissEph.SEFLG_SIDEREAL) != 0) {
                /* subtract ayan_t0 */
                SE.SwephLib.swi_cartpol_sp(xx, xx);
                xx[0] -= sip.ayan_t0;
                SE.SwephLib.swi_polcart_sp(xx, xx);
            } else
                if (0 == (iflag & SwissEph.SEFLG_NONUT)) {
                    SE.SwephLib.swi_coortrf2(xx, xx, nutp.snut, nutp.cnut);
                    SE.SwephLib.swi_coortrf2(xx + 3, xx + 3, nutp.snut, nutp.cnut);
                }
            return (OK);
        }
示例#6
0
 public void Reset(bool full)
 {
     if (full)
     {
         fidat = Enumerable.Range(0, SEI_NEPHFILES).Select(i => new file_data()).ToArray();
         savedat = Enumerable.Range(0, SwissEph.SE_NPLANETS + 1).Select(i => new save_positions()).ToArray();
         pldat = Enumerable.Range(0, SEI_NPLANETS).Select(i => new plan_data()).ToArray();
         nddat = Enumerable.Range(0, SEI_NNODE_ETC).Select(i => new plan_data()).ToArray();
         topd = new topo_data();
         //dpsi = new double[36525];
         //deps = new double[36525];
         astro_models = new Int32[Sweph.SEI_NMODELS];
     }
     oec = new epsilon();
     oec2000 = new epsilon();
     nut = new nut();
     nut2000 = new nut();
     nutv = new nut();
 }