FAILURE MAP
← Case archive

FA-69571 / Orbital propagation / Open access

J2 secular node and perigee drift: J2 rates use the semi-major axis instead of p · case 01

Eccentric orbits get too little nodal regression.

Verified by executionVariant 1 · 7 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The (Re/p)^2 factor is computed with a.

VERIFIED REPAIR

Use p=a(1-e^2).

Unsuccessful approach: Using Re/p without squaring still misstates the dependence.

Case contract

Input [a km, e, i deg, days]. With mu=398600.4418, Re=6378.137, J2=1.08263e-3, n=sqrt(mu/a^3), p=a(1-e^2): dRAAN/dt=-1.5 n J2 (Re/p)^2 cos i, dargp/dt=0.75 n J2 (Re/p)^2 (5cos^2 i-1). Return [RAAN drift deg, argp drift deg over the interval (rounded 4), sun-synchronous flag: |RAAN rate in deg/day - 0.9856|<0.01].

Why this case matters

Orbit determination and mission planning chain many small conversions; one wrong branch or unit silently moves a spacecraft by kilometres.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    a,e,i,days=x
    mu,Re,J2=398600.4418,6378.137,1.08263e-3
    n=math.sqrt(mu/a**3)
    p=a*(1-e*e)
    ci=math.cos(math.radians(i))
    fac=n*J2*(Re/a)**2
    dO=-1.5*fac*ci
    dw=0.75*fac*(5*ci*ci-1)
    t=days*86400
    sso=abs(math.degrees(dO)*86400-0.9856)<0.01
    return [round(math.degrees(dO*t),4),round(math.degrees(dw*t),4),sso]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1]', [7078.0, 0.001, 51.6, 1], [-4.2991, 3.2153, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1]', [7078.0, 0.001, 98.19, 1], [0.986, -3.1094, True]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True]), ('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False])], [('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1]', [7078.0, 0.001, 51.6, 1], [-4.2991, 3.2153, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True]), ('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False]), ('j2 secular node and perigee drift [42164.0, 0.0, 0.1, 30]', [42164.0, 0.0, 0.1, 30], [-0.4024, 0.8049, False])], [('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False]), ('j2 secular node and perigee drift [42164.0, 0.0, 0.1, 30]', [42164.0, 0.0, 0.1, 30], [-0.4024, 0.8049, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 81.81, 1]', [7078.0, 0.001, 81.81, 1], [-0.986, -3.1094, False]), ('j2 secular node and perigee drift [8000.0, 0.1, 45.0, 0.5]', [8000.0, 0.1, 45.0, 0.5], [-1.6264, 1.7251, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1]', [7078.0, 0.001, 98.19, 1], [0.986, -3.1094, True])], [('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [8000.0, 0.1, 45.0, 0.5]', [8000.0, 0.1, 45.0, 0.5], [-1.6264, 1.7251, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1]', [7078.0, 0.001, 98.19, 1], [0.986, -3.1094, True]), ('j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1]', [7078.0, 0.001, 51.6, 1], [-4.2991, 3.2153, False]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True])], [('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [42164.0, 0.0, 0.1, 30]', [42164.0, 0.0, 0.1, 30], [-0.4024, 0.8049, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 81.81, 1]', [7078.0, 0.001, 81.81, 1], [-0.986, -3.1094, False])]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1][-4.299, 3.2153, False][-4.2991, 3.2153, False]Failed
j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1][0.986, -3.1094, True][0.986, -3.1094, True]Passed
j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10][-0.3028, 0.0008, False][-1.4793, 0.004, False]Failed
j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3][-18.9688, 30.8831, False][-20.5825, 33.5103, False]Failed
j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2][1.971, -7.0169, True][1.971, -7.0169, True]Passed
j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5][-3.9326, -12.76, False][-3.9523, -12.8241, False]Failed
j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7][3.8177, 0.9544, False][4.6102, 1.1525, False]Failed

SHA-256 / 8865e80d5c77a8ac90bc33c003374a48f7169142cb7b1b9adde1a726c167ea7c

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    a,e,i,days=x
    mu,Re,J2=398600.4418,6378.137,1.08263e-3
    n=math.sqrt(mu/a**3)
    p=a*(1-e*e)
    ci=math.cos(math.radians(i))
    fac=n*J2*(Re/p)
    dO=-1.5*fac*ci
    dw=0.75*fac*(5*ci*ci-1)
    t=days*86400
    sso=abs(math.degrees(dO)*86400-0.9856)<0.01
    return [round(math.degrees(dO*t),4),round(math.degrees(dw*t),4),sso]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1]', [7078.0, 0.001, 51.6, 1], [-4.2991, 3.2153, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1]', [7078.0, 0.001, 98.19, 1], [0.986, -3.1094, True]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True]), ('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False])], [('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1]', [7078.0, 0.001, 51.6, 1], [-4.2991, 3.2153, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True]), ('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False]), ('j2 secular node and perigee drift [42164.0, 0.0, 0.1, 30]', [42164.0, 0.0, 0.1, 30], [-0.4024, 0.8049, False])], [('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False]), ('j2 secular node and perigee drift [42164.0, 0.0, 0.1, 30]', [42164.0, 0.0, 0.1, 30], [-0.4024, 0.8049, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 81.81, 1]', [7078.0, 0.001, 81.81, 1], [-0.986, -3.1094, False]), ('j2 secular node and perigee drift [8000.0, 0.1, 45.0, 0.5]', [8000.0, 0.1, 45.0, 0.5], [-1.6264, 1.7251, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1]', [7078.0, 0.001, 98.19, 1], [0.986, -3.1094, True])], [('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [8000.0, 0.1, 45.0, 0.5]', [8000.0, 0.1, 45.0, 0.5], [-1.6264, 1.7251, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1]', [7078.0, 0.001, 98.19, 1], [0.986, -3.1094, True]), ('j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1]', [7078.0, 0.001, 51.6, 1], [-4.2991, 3.2153, False]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True])], [('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [42164.0, 0.0, 0.1, 30]', [42164.0, 0.0, 0.1, 30], [-0.4024, 0.8049, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 81.81, 1]', [7078.0, 0.001, 81.81, 1], [-0.986, -3.1094, False])]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1][-4.7708, 3.5681, False][-4.2991, 3.2153, False]Failed
j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1][1.0941, -3.4506, False][0.986, -3.1094, True]Failed
j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10][-2.7869, 0.0076, False][-1.4793, 0.004, False]Failed
j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3][-21.6857, 35.3064, False][-20.5825, 33.5103, False]Failed
j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2][2.1254, -7.5668, False][1.971, -7.0169, True]Failed
j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5][-4.6359, -15.042, False][-3.9523, -12.8241, False]Failed
j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7][7.893, 1.9733, False][4.6102, 1.1525, False]Failed

SHA-256 / 688e92d6da5150f1e8cea0a3c4b69cb2e06a45ef8195b5e6b6c59baf86c40b53

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    a,e,i,days=x
    mu,Re,J2=398600.4418,6378.137,1.08263e-3
    n=math.sqrt(mu/a**3)
    p=a*(1-e*e)
    ci=math.cos(math.radians(i))
    fac=n*J2*(Re/p)**2
    dO=-1.5*fac*ci
    dw=0.75*fac*(5*ci*ci-1)
    t=days*86400
    sso=abs(math.degrees(dO)*86400-0.9856)<0.01
    return [round(math.degrees(dO*t),4),round(math.degrees(dw*t),4),sso]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1]', [7078.0, 0.001, 51.6, 1], [-4.2991, 3.2153, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1]', [7078.0, 0.001, 98.19, 1], [0.986, -3.1094, True]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True]), ('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False])], [('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1]', [7078.0, 0.001, 51.6, 1], [-4.2991, 3.2153, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True]), ('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False]), ('j2 secular node and perigee drift [42164.0, 0.0, 0.1, 30]', [42164.0, 0.0, 0.1, 30], [-0.4024, 0.8049, False])], [('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False]), ('j2 secular node and perigee drift [42164.0, 0.0, 0.1, 30]', [42164.0, 0.0, 0.1, 30], [-0.4024, 0.8049, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 81.81, 1]', [7078.0, 0.001, 81.81, 1], [-0.986, -3.1094, False]), ('j2 secular node and perigee drift [8000.0, 0.1, 45.0, 0.5]', [8000.0, 0.1, 45.0, 0.5], [-1.6264, 1.7251, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1]', [7078.0, 0.001, 98.19, 1], [0.986, -3.1094, True])], [('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [8000.0, 0.1, 45.0, 0.5]', [8000.0, 0.1, 45.0, 0.5], [-1.6264, 1.7251, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1]', [7078.0, 0.001, 98.19, 1], [0.986, -3.1094, True]), ('j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1]', [7078.0, 0.001, 51.6, 1], [-4.2991, 3.2153, False]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True])], [('j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7]', [12000.0, 0.3, 120.0, 7], [4.6102, 1.1525, False]), ('j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2]', [6878.0, 0.0, 97.4, 2], [1.971, -7.0169, True]), ('j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10]', [26560.0, 0.74, 63.4, 10], [-1.4793, 0.004, False]), ('j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3]', [7000.0, 0.2, 28.5, 3], [-20.5825, 33.5103, False]), ('j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5]', [7500.0, 0.05, 82.0, 5], [-3.9523, -12.8241, False]), ('j2 secular node and perigee drift [42164.0, 0.0, 0.1, 30]', [42164.0, 0.0, 0.1, 30], [-0.4024, 0.8049, False]), ('j2 secular node and perigee drift [7078.0, 0.001, 81.81, 1]', [7078.0, 0.001, 81.81, 1], [-0.986, -3.1094, False])]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
j2 secular node and perigee drift [7078.0, 0.001, 51.6, 1][-4.2991, 3.2153, False][-4.2991, 3.2153, False]Passed
j2 secular node and perigee drift [7078.0, 0.001, 98.19, 1][0.986, -3.1094, True][0.986, -3.1094, True]Passed
j2 secular node and perigee drift [26560.0, 0.74, 63.4, 10][-1.4793, 0.004, False][-1.4793, 0.004, False]Passed
j2 secular node and perigee drift [7000.0, 0.2, 28.5, 3][-20.5825, 33.5103, False][-20.5825, 33.5103, False]Passed
j2 secular node and perigee drift [6878.0, 0.0, 97.4, 2][1.971, -7.0169, True][1.971, -7.0169, True]Passed
j2 secular node and perigee drift [7500.0, 0.05, 82.0, 5][-3.9523, -12.8241, False][-3.9523, -12.8241, False]Passed
j2 secular node and perigee drift [12000.0, 0.3, 120.0, 7][4.6102, 1.1525, False][4.6102, 1.1525, False]Passed

SHA-256 / e60976526b9c50aaf80e99930e72ef6b4fb7a865e15ec94e4048343a1777a0ee

Verification & scope

A deterministic toy two-body model with stipulated constants and conventions; not flight dynamics software or a validated SGP4 implementation. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:48:12.581389+00:00.

Case digest / 653276520a010312b33c3aa4f4dd2c3efdc0fcbc3ad2d2f2d7f1035928c631c8