package org.nstart.dep265.tletools.zeptomoby.orbit.norad

import org.nstart.dep265.tletools.zeptomoby.core.Globals
import org.nstart.dep265.tletools.zeptomoby.core.Globals.acTan
import org.nstart.dep265.tletools.zeptomoby.core.Globals.fmod2p
import org.nstart.dep265.tletools.zeptomoby.core.Globals.sqr
import org.nstart.dep265.tletools.zeptomoby.core.eci.EciTime
import org.nstart.dep265.tletools.zeptomoby.orbit.Orbit
import kotlin.math.*

open class NoradSDP4(o: Orbit): NoradBase(o) {

    companion object Constants {
        const val zes: Double = 0.01675
        const val zel: Double = 0.05490
        const val zns: Double = 1.19459e-05
        const val znl: Double = 1.5835218e-04
        const val thdt: Double = 4.3752691e-03
    }

    protected data class DeepSecularVars(val xmdf: Double, val omgadf: Double,
                               val xnode: Double, val em: Double,
                               val xinc: Double, val xn: Double)

    protected data class DeepPeriodicsVars(val e: Double, val xinc: Double,
                                 val omgadf: Double, val xnode: Double,
                                 val xmam: Double)

    protected data class DeepDotTermsVars(val xndot: Double, val xnddt: Double, val xldot: Double)

    protected var dpE3 = 0.0; protected var dpEe2 = 0.0; protected var dpSe2 = 0.0
    protected var dpSe3 = 0.0; protected var dpSgh2 = 0.0; protected var dpSgh3 = 0.0
    protected var dpSgh4 = 0.0; protected var dpSh2 = 0.0; protected var dpSh3 = 0.0
    protected var dpSi2 = 0.0; protected var dpSi3 = 0.0; protected var dpSl2 = 0.0
    protected var dpSl3 = 0.0; protected var dpSl4 = 0.0; protected var dpXgh2 = 0.0
    protected var dpXgh3 = 0.0; protected var dpXgh4 = 0.0; protected var dpXh2 = 0.0
    protected var dpXh3 = 0.0; protected var dpXi2 = 0.0; protected var dpXi3 = 0.0
    protected var dpXl2 = 0.0; protected var dpXl3 = 0.0; protected var dpXl4 = 0.0
    protected val dpZmol: Double; protected val dpZmos: Double

    protected var dpAtime: Double; protected var dpD2201 = 0.0; protected var dpD2211 = 0.0
    protected var dpD3210 = 0.0; protected var dpD3222 = 0.0; protected var dpD4410 = 0.0
    protected var dpD4422 = 0.0; protected var dpD5220 = 0.0; protected var dpD5232 = 0.0
    protected var dpD5421 = 0.0; protected var dpD5433 = 0.0; protected var dpDel1 = 0.0
    protected var dpDel2 = 0.0; protected var dpDel3 = 0.0; protected var dpSse: Double = 0.0
    protected var dpSsg: Double = 0.0; protected var dpSsh: Double = 0.0; protected var dpSsi: Double = 0.0
    protected var dpSsl: Double = 0.0; protected val dpStep2: Double; protected val dpStepn: Double
    protected val dpStepp: Double; protected val dpThgr: Double; protected val dpXfact: Double
    protected var dpXlamo: Double = 0.0; protected var dpXli: Double; protected var dpXni: Double

    var gpReso: Boolean
    var gpSync: Boolean

    init {
        val sinArg = sin(orbit.argPerigee)
        val cosArg = cos(orbit.argPerigee)
        val eqsq   = sqr(orbit.eccentricity)

        val jd = orbit.epoch
        dpThgr = jd.toGMST()

        val eq = orbit.eccentricity
        val aqnv = 1.0 / orbit.semiMajor
        val xmao = orbit.meanAnomaly
        val xpidot = omgdot + xnodot
        val sinQ = sin(orbit.raan)
        val cosQ = cos(orbit.raan)

        val day = jd.fromJan0_12h_1900()
        val dpiXnodce = 4.5236020 - 9.2422029e-4 * day
        val dpiStem = sin(dpiXnodce)
        val dpiCtem = cos(dpiXnodce)
        val dpiZcosil = 0.91375164 - 0.03568096 * dpiCtem
        val dpiZsinil = sqrt(1.0 - dpiZcosil * dpiZcosil)
        val dpiZsinhl = 0.089683511 *dpiStem / dpiZsinil
        val dpiZcoshl = sqrt(1.0 - dpiZsinhl * dpiZsinhl)
        val dpiC = 4.7199672 + 0.22997150 * day
        val dpiGam = 5.8351514 + 0.0019443680 * day

        dpZmol = fmod2p(dpiC - dpiGam)

        var dpiZx = 0.39785416 * dpiStem / dpiZsinil
        val dpiZy = dpiZcoshl * dpiCtem + 0.91744867 * dpiZsinhl * dpiStem

        dpiZx = acTan(dpiZx,dpiZy) + dpiGam - dpiXnodce

        val dpiZcosgl = cos(dpiZx)
        val dpiZsingl = sin(dpiZx)

        dpZmos   = fmod2p(6.2565837 + 0.017201977 * day)

        val zcosis = 0.91744867
        val zsinis = 0.39785416
        val c1ss   = 2.9864797e-06
        val zsings = -0.98088458
        val zcosgs =  0.1945905

        var zcosg = zcosgs
        var zsing = zsings
        var zcosi = zcosis
        var zsini = zsinis
        var zcosh = cosQ
        var zsinh = sinQ
        var cc  = c1ss
        var zn  = zns
        var ze  = zes
        val xnoi = 1.0 / orbit.meanMotion

        var se  = 0.0; var si = 0.0; var sl = 0.0
        var sgh = 0.0; var sh = 0.0

        for (pass in 1..2) {
            val a1 = zcosg * zcosh + zsing * zcosi * zsinh
            val a3 = -zsing * zcosh + zcosg * zcosi * zsinh
            val a7 = -zcosg * zsinh + zsing * zcosi * zcosh
            val a8 = zsing * zsini
            val a9 = zsing * zsinh + zcosg * zcosi * zcosh
            val a10 = zcosg * zsini

            val a2 = cosio * a7 + sinio * a8
            val a4 = cosio * a9 + sinio * a10
            val a5 = -sinio * a7 + cosio * a8
            val a6 = -sinio * a9 + cosio * a10
            val x1 = a1 * cosArg + a2 * sinArg
            val x2 = a3 * cosArg + a4 * sinArg
            val x3 = -a1 * sinArg + a2 * cosArg
            val x4 = -a3 * sinArg + a4 * cosArg
            val x5 = a5 * sinArg
            val x6 = a6 * sinArg
            val x7 = a5 * cosArg
            val x8 = a6 * cosArg
            val z31 = 12.0 * x1 * x1 - 3.0 * x3 * x3
            val z32 = 24.0 * x1 * x2 - 6.0 * x3 * x4
            val z33 = 12.0 * x2 * x2 - 3.0 * x4 * x4
            var z1 = 3.0 * (a1 * a1 + a2 * a2) + z31 * eqsq
            var z2 = 6.0 * (a1 * a3 + a2 * a4) + z32 * eqsq
            var z3 = 3.0 * (a3 * a3 + a4 * a4) + z33 * eqsq
            val z11 = -6.0 * a1 * a5 + eqsq*(-24.0 * x1 * x7 - 6.0 * x3 * x5)
            val z12 = -6.0 * (a1 * a6 + a3 * a5) +
                    eqsq * (-24.0 * (x2 * x7 + x1 * x8) - 6.0 * (x3 * x6 + x4 * x5))
            val z13 = -6.0 * a3 * a6 + eqsq * (-24.0 * x2 * x8 - 6.0 * x4 * x6)
            val z21 = 6.0 * a2 * a5 + eqsq * (24.0 * x1 * x5 - 6.0 * x3 * x7)
            val z22 = 6.0*(a4 * a5 + a2 * a6) +
                    eqsq * (24.0 * (x2 * x5 + x1 * x6) - 6.0 * (x4 * x7 + x3 * x8))
            val z23 = 6.0 * a4 * a6 + eqsq*(24.0 * x2 * x6 - 6.0 * x4 * x8)
            z1 += z1 + betao2 * z31
            z2 += z2 + betao2 * z32
            z3 += z3 + betao2 * z33
            val s3 = cc * xnoi
            val s2 = -0.5 * s3 / betao
            val s4 = s3 * betao
            val s1 = -15.0 * eq * s4
            val s5 = x1 * x3 + x2 * x4
            val s6 = x2 * x3 + x1 * x4
            val s7 = x2 * x4 - x1 * x3
            se = s1 * zn * s5
            si = s2 * zn * (z11 + z13)
            sl = -zn * s3 * (z1 + z3 - 14.0 - 6.0 * eqsq)
            sgh = s4 * zn * (z31 + z33 - 6.0)
            sh  = -zn * s2 * (z21 + z23)

            if (orbit.inclination < 5.2359877e-2) sh = 0.0

            dpEe2 =  2.0 * s1 * s6
            dpE3  =  2.0 * s1 * s7
            dpXi2 =  2.0 * s2 * z12
            dpXi3 =  2.0 * s2 * (z13 - z11)
            dpXl2 = -2.0 * s3 * z2
            dpXl3 = -2.0 * s3 * (z3 - z1)
            dpXl4 = -2.0 * s3 * (-21.0 - 9.0 * eqsq) * ze
            dpXgh2 = 2.0 * s4 * z32
            dpXgh3 = 2.0 * s4 * (z33 - z31)
            dpXgh4 = -18.0 * s4 * ze
            dpXh2 = -2.0 * s2 * z22
            dpXh3 = -2.0 * s2 * (z23 - z21)

            if (pass == 1) {
                dpSse = se
                dpSsi = si
                dpSsl = sl
                dpSsh = sh / sinio
                dpSsg = sgh - cosio * dpSsh
                dpSe2 = dpEe2
                dpSi2 = dpXi2
                dpSl2 = dpXl2
                dpSgh2 = dpXgh2
                dpSh2 = dpXh2
                dpSe3 = dpE3
                dpSi3 = dpXi3
                dpSl3 = dpXl3
                dpSgh3 = dpXgh3
                dpSh3 = dpXh3
                dpSl4 = dpXl4
                dpSgh4 = dpXgh4
                zcosg = dpiZcosgl
                zsing = dpiZsingl
                zcosi = dpiZcosil
                zsini = dpiZsinil
                zcosh = dpiZcoshl * cosQ + dpiZsinhl * sinQ
                zsinh = sinQ * dpiZcoshl - cosQ * dpiZsinhl
                zn = znl

                val c1l = 4.7968065e-07
                cc = c1l
                ze = zel
            }
        }

        dpSse += se
        dpSsi += si
        dpSsl += sl
        dpSsg = dpSsg + sgh - cosio / sinio * sh
        dpSsh += sh / sinio

        gpReso = false
        gpSync = false

        val g310: Double
        val f220: Double
        var bfact = 0.0

        if ((orbit.meanMotion > 0.0034906585) &&
            (orbit.meanMotion < 0.0052359877)) {
            gpReso = true
            gpSync = true

            val g200 = 1.0 + eqsq * (-2.5 + 0.8125 * eqsq)
            g310 = 1.0 + 2.0 * eqsq

            val g300 = 1.0 + eqsq * (-6.0 + 6.60937 * eqsq)
            f220 = 0.75 * (1.0 + cosio) * (1.0 + cosio)

            val f311 = 0.9375 * sinio * sinio * (1.0 + 3 * cosio) - 0.75 * (1.0 + cosio)
            var f330 = 1.0 + cosio

            val q22 = 1.7891679e-06
            val q31 = 2.1460748e-06
            val q33 = 2.2123015e-07

            f330 *= 1.875 * f330 * f330
            dpDel1 = 3.0 * orbit.meanMotion * orbit.meanMotion * aqnv * aqnv
            dpDel2 = 2.0 * dpDel1 * f220 * g200 * q22
            dpDel3 = 3.0 * dpDel1 * f330 * g300 * q33 * aqnv
            dpDel1 *= f311 * g310 * q31 * aqnv
            dpXlamo = xmao + orbit.raan + orbit.argPerigee - dpThgr
            bfact = xmdot + xpidot - thdt
            bfact += dpSsl + dpSsg + dpSsh
        }
        else if ((orbit.meanMotion >= 8.26e-03) &&
                 (orbit.meanMotion <= 9.24e-03) &&
                 (eq >= 0.5)) {
            gpReso = true

            val eoc  = eq * eqsq
            val g201 = -0.306 - (eq - 0.64) * 0.440

            val g211: Double; val g322: Double
            val g410: Double; val g422: Double
            val g520: Double

            if (eq <= 0.65) {
                g211 =    3.616 -  13.247  * eq + 16.290   * eqsq
                g310 =  -19.302 +  117.390 * eq - 228.419  * eqsq + 156.591  * eoc
                g322 = -18.9068 + 109.7927 * eq - 214.6334 * eqsq + 146.5816 * eoc
                g410 =  -41.122 +  242.694 * eq - 471.094  * eqsq +  313.953 * eoc
                g422 = -146.407 +  841.880 * eq - 1629.014 * eqsq + 1083.435 * eoc
                g520 = -532.114 + 3017.977 * eq - 5740.0   * eqsq + 3708.276 * eoc
            }
            else {
                g211 =   -72.099 +  331.819 * eq -  508.738 * eqsq +  266.724 * eoc
                g310 =  -346.844 + 1582.851 * eq - 2415.925 * eqsq + 1246.113 * eoc
                g322 =  -342.585 + 1554.908 * eq - 2366.899 * eqsq + 1215.972 * eoc
                g410 = -1052.797 + 4758.686 * eq - 7193.992 * eqsq + 3651.957 * eoc
                g422 = -3581.69  + 16178.11 * eq - 24462.77 * eqsq + 12422.52 * eoc

                g520 = if (eq <= 0.715) 1464.74 - 4664.75 * eq + 3763.64 * eqsq
                       else -5149.66 + 29936.92 * eq - 54087.36 * eqsq + 31324.56 * eoc

            }

            val g533: Double
            val g521: Double
            val g532: Double

            if (eq < 0.7) {
                g533 = -919.2277  + 4988.61   * eq - 9064.77   * eqsq + 5542.21  * eoc
                g521 = -822.71072 + 4568.6173 * eq - 8491.4146 * eqsq + 5337.524 * eoc
                g532 = -853.666   + 4690.25   * eq - 8624.77   * eqsq + 5341.4   * eoc
            }
            else {
                g533 = -37995.78  + 161616.52 * eq - 229838.2  * eqsq + 109377.94 * eoc
                g521 = -51752.104 + 218913.95 * eq - 309468.16 * eqsq + 146349.42 * eoc
                g532 = -40023.88  + 170470.89 * eq - 242699.48 * eqsq + 115605.82 * eoc
            }

            val sini2  = sqr(sinio)
            val theta2 = sqr(cosio)

            f220 = 0.75 * (1.0 + 2.0 * cosio + theta2)

            val root22 = 1.7891679e-06
            val root32 = 3.7393792e-07
            val root44 = 7.3636953e-09
            val root52 = 1.1428639e-07
            val root54 = 2.1765803e-09

            val f221 = 1.5 * sini2
            val f321 =  1.875 * sinio * (1.0 - 2.0 * cosio - 3.0 * theta2)
            val f322 = -1.875 * sinio * (1.0 + 2.0 * cosio - 3.0 * theta2)
            val f441 = 35.0 * sini2 * f220
            val f442 = 39.3750 * sini2 * sini2
            val f522 = 9.84375 * sinio * (sini2 * (1.0 - 2.0 * cosio - 5.0 * theta2) +
            0.33333333*(-2.0 + 4.0 * cosio + 6.0 * theta2))
            val f523 = sinio * (4.92187512 * sini2 * (-2.0 - 4.0 * cosio + 10.0 * theta2) +
                    6.56250012 * (1.0 + 2.0 * cosio - 3.0 * theta2))
            val f542 = 29.53125 * sinio * ( 2.0 - 8.0 * cosio + theta2 * (-12.0 + 8.0 * cosio + 10.0 * theta2))
            val f543 = 29.53125 * sinio * (-2.0 - 8.0 * cosio + theta2 * ( 12.0 + 8.0 * cosio - 10.0 * theta2))
            val xno2 = orbit.meanMotion * orbit.meanMotion
            val ainv2 = aqnv * aqnv
            var temp1 = 3.0 * xno2 * ainv2
            var temp  = temp1 * root22

            dpD2201 = temp * f220 * g201
            dpD2211 = temp * f221 * g211
            temp1 *= aqnv
            temp = temp1 * root32
            dpD3210 = temp * f321 * g310
            dpD3222 = temp * f322 * g322
            temp1 *= aqnv
            temp = 2.0 * temp1 * root44
            dpD4410 = temp * f441 * g410
            dpD4422 = temp * f442 * g422
            temp1 *= aqnv
            temp  = temp1 * root52
            dpD5220 = temp * f522 * g520
            dpD5232 = temp * f523 * g532
            temp = 2.0 * temp1 * root54
            dpD5421 = temp * f542 * g521
            dpD5433 = temp * f543 * g533
            dpXlamo = xmao + orbit.raan + orbit.raan - dpThgr - dpThgr
            bfact = xmdot + xnodot + xnodot - thdt - thdt
            bfact += dpSsl + dpSsh + dpSsh
        }

        if (gpReso || gpSync) {
            dpXfact = bfact - orbit.meanMotion
            dpXli   = dpXlamo
            dpXni   = orbit.meanMotion
            dpAtime = 0.0
            dpStepp = 720.0
            dpStepn = -720.0
            dpStep2 = 259200.0
        }
        else {
            dpXfact = 0.0
            dpXli   = 0.0
            dpXni   = 0.0
            dpAtime = 0.0
            dpStepp = 0.0
            dpStepn = 0.0
            dpStep2 = 0.0
        }
    }

    protected fun deepSecular(xmdf: Double, omgadf: Double, xnode: Double,
                              xn: Double, tsince: Double): DeepSecularVars {
        var xmdfLocal   = xmdf + dpSsl * tsince
        var omgadfLocal = omgadf + dpSsg * tsince
        var xnodeLocal  = xnode + dpSsh * tsince
        val emmLocal    = orbit.eccentricity + dpSse * tsince
        var xinccLocal  = orbit.inclination  + dpSsi * tsince

        if (xinccLocal < 0.0) {
            xinccLocal *= -1
            xnodeLocal += Globals.pi
            omgadfLocal -= Globals.pi
        }

        if (!gpReso) return DeepSecularVars(xmdfLocal, omgadfLocal, xnodeLocal,
                                             emmLocal, xinccLocal, xn)

        val xnddt: Double
        val xndot: Double
        val xldot: Double
        var delt  = 0.0
        var fDone = false

        while (!fDone) {
            if ((dpAtime == 0.0) ||
                ((tsince >= 0.0) && (dpAtime <  0.0)) ||
                ((tsince <  0.0) && (dpAtime >= 0.0))) {
                delt = if (tsince < 0) dpStepn else dpStepp

                dpAtime = 0.0
                dpXni = orbit.meanMotion
                dpXli = dpXlamo

                fDone = true
            }
            else {
                if (abs(tsince) < abs(dpAtime)) {
                    delt = if (tsince >= 0.0) dpStepn else dpStepp
                    deepCalcIntegrator(delt)
                }
                else {
                    delt = if (tsince > 0.0) dpStepp else dpStepn
                    fDone = true
                }
            }
        }

        while (abs(tsince - dpAtime) >= dpStepp) deepCalcIntegrator(delt)

        val ft = tsince - dpAtime

        val vars = deepCalcDotTerms()
        xndot = vars.xndot
        xnddt = vars.xnddt
        xldot = vars.xldot

        val xnnLocal = dpXni + xndot * ft + xnddt * ft * ft * 0.5
        val xl = dpXli + xldot * ft + xndot * ft * ft * 0.5
        val temp = -xnodeLocal + dpThgr + tsince * thdt

        xmdfLocal = xl - omgadfLocal + temp
        if (!gpSync) xmdfLocal = xl + temp + temp

        return DeepSecularVars(xmdfLocal, omgadfLocal, xnodeLocal, emmLocal, xinccLocal, xnnLocal)
    }

    protected fun deepCalcDotTerms(): DeepDotTermsVars {
        val fasx2 = 0.13130908
        val fasx4 = 2.8843198
        val fasx6 = 0.37448087

        val xndotLocal: Double
        var xnddtLocal: Double

        if (gpSync) {
            xndotLocal = dpDel1 * sin(dpXli - fasx2) +
                dpDel2 * sin(2.0 * (dpXli - fasx4)) +
                dpDel3 * sin(3.0 * (dpXli - fasx6))
            xnddtLocal = dpDel1 * cos(dpXli - fasx2) +
                2.0 * dpDel2 * cos(2.0 * (dpXli - fasx4)) +
                3.0 * dpDel3 * cos(3.0 * (dpXli - fasx6))
        }
        else {
            val g22 = 5.7686396
            val g32 = 0.95240898
            val g44 = 1.8014998
            val g52 = 1.0508330
            val g54 = 4.4108898

            val xomi  = orbit.argPerigee + omgdot * dpAtime
            val x2omi = xomi + xomi
            val x2li  = dpXli + dpXli

            xndotLocal = dpD2201 * sin(x2omi + dpXli - g22) +
                dpD2211 * sin(dpXli - g22)         +
                dpD3210 * sin( xomi + dpXli - g32) +
                dpD3222 * sin(-xomi + dpXli - g32) +
                dpD4410 * sin(x2omi + x2li - g44)   +
                dpD4422 * sin(x2li - g44)           +
                dpD5220 * sin( xomi + dpXli - g52) +
                dpD5232 * sin(-xomi + dpXli - g52) +
                dpD5421 * sin( xomi + x2li - g54)   +
                dpD5433 * sin(-xomi + x2li - g54)

            xnddtLocal = dpD2201 * cos(x2omi + dpXli - g22) +
                dpD2211 * cos(dpXli - g22)         +
                dpD3210 * cos( xomi + dpXli - g32) +
                dpD3222 * cos(-xomi + dpXli - g32) +
                dpD5220 * cos( xomi + dpXli - g52) +
                dpD5232 * cos(-xomi + dpXli - g52) +
                2.0 * (dpD4410 * cos(x2omi + x2li - g44) +
                dpD4422 * cos(x2li - g44)         +
                dpD5421 * cos( xomi + x2li - g54) +
                dpD5433 * cos(-xomi + x2li - g54))
        }

        val xldotLocal = dpXni + dpXfact
        xnddtLocal *= xldotLocal

        return DeepDotTermsVars(xndotLocal, xnddtLocal, xldotLocal)
    }

    protected fun deepCalcIntegrator(delt: Double): DeepDotTermsVars {
        val vars = deepCalcDotTerms()

        dpXli += vars.xldot * delt + vars.xndot * dpStep2
        dpXni += vars.xndot * delt + vars.xnddt * dpStep2
        dpAtime += delt

        return vars
    }

    protected fun deepPeriodics(e: Double, xinc: Double,
                                omgadf: Double, xnode: Double,
                                xmam: Double, tsince: Double): DeepPeriodicsVars{
        val sinis = sin(xinc)
        val cosis = cos(xinc)

        val sghs: Double
        val shs: Double
        val sh1: Double
        val pe: Double
        val pinc: Double
        val pl: Double
        val sghl: Double

        var zm = dpZmos + zns * tsince
        var zf = zm + 2.0 * zes * sin(zm)
        var sinzf = sin(zf)
        var f2  = 0.5 * sinzf * sinzf - 0.25
        var f3  = -0.5 * sinzf * cos(zf)
        val ses = dpSe2 * f2 + dpSe3 * f3
        val sis = dpSi2 * f2 + dpSi3 * f3
        val sls = dpSl2 * f2 + dpSl3 * f3 + dpSl4 * sinzf

        sghs = dpSgh2 * f2 + dpSgh3 * f3 + dpSgh4 * sinzf
        shs  = dpSh2  * f2 + dpSh3  * f3
        zm = dpZmol + znl * tsince
        zf = zm + 2.0 * zel * sin(zm)
        sinzf = sin(zf)
        f2 = 0.5 * sinzf * sinzf - 0.25
        f3 = -0.5 * sinzf * cos(zf)

        val sel  = dpEe2 * f2 + dpE3  * f3
        val sil  = dpXi2 * f2 + dpXi3 * f3
        val sll  = dpXl2 * f2 + dpXl3 * f3 + dpXl4 * sinzf

        sghl = dpXgh2 * f2 + dpXgh3 * f3 + dpXgh4 * sinzf
        sh1  = dpXh2  * f2 + dpXh3  * f3
        pe   = ses + sel
        pinc = sis + sil
        pl   = sls + sll

        var pgh = sghs + sghl
        var ph = shs + sh1

        val xinccLocal = xinc + pinc
        val eLocal  = e + pe
        val omgadfLocal: Double
        val xnodeLocal: Double
        val xmamLocal: Double

        if (orbit.inclination >= 0.2) {
            ph /= sinio
            pgh -= cosio * ph
            omgadfLocal = omgadf + pgh
            xnodeLocal = xnode + ph
            xmamLocal = xmam + pl
        }
        else {
            val sinok = sin(xnode)
            val cosok = cos(xnode)
            var alfdp = sinis * sinok
            var betdp = sinis * cosok
            val dalf  =  ph * cosok + pinc * cosis * sinok
            val dbet  = -ph * sinok + pinc * cosis * cosok

            alfdp += dalf
            betdp += dbet

            var xls = xmam + omgadf + cosis * xnode
            val dls = pl + pgh - pinc * xnode * sinis

            xls += dls
            xnodeLocal  = acTan(alfdp, betdp)
            xmamLocal   = xmam + pl
            omgadfLocal = xls - xmamLocal - cos(xinccLocal) * xnodeLocal
        }

        return DeepPeriodicsVars(eLocal, xinccLocal, omgadfLocal, xnodeLocal, xmamLocal)
    }

    override fun position(tsince: Double): EciTime {
        var xmdf = orbit.meanAnomaly + xmdot  * tsince
        var omgadf = orbit.argPerigee  + omgdot * tsince
        val xnoddf = orbit.raan + xnodot * tsince
        val tsq = tsince * tsince
        var xnode = xnoddf + xnodcf * tsq
        val tempa = 1.0 - c1 * tsince
        val tempe = orbit.bStar * c4 * tsince
        val templ = t2cof * tsq
        var xn = orbit.meanMotion
        val em: Double
        var xinc: Double

        val vars1 = deepSecular(xmdf, omgadf, xnode, xn, tsince)
        xmdf = vars1.xmdf
        omgadf = vars1.omgadf
        xnode = vars1.xnode
        xn = vars1.xn
        em = vars1.em
        xinc = vars1.xinc

        val a = (Globals.xke / xn).pow(2.0 / 3.0) * sqr(tempa)
        var e = em - tempe
        var xmam = xmdf + orbit.meanMotion * templ

        val vars2 = deepPeriodics(e, xinc, omgadf, xnode, xmam, tsince)
        e = vars2.e
        xinc = vars2.xinc
        omgadf = vars2.omgadf
        xnode = vars2.xnode
        xmam = vars2.xmam

        val xl = xmam + omgadf + xnode
        xn = Globals.xke / a.pow(1.5)

        return finalPosition(xinc, omgadf, e, a, xl, xnode, xn, tsince)
    }

    override fun clone(o: Orbit): NoradBase = NoradSDP4(o)
}