FA-69346 / Orbital propagation / Open access
Elliptic Kepler solver: Mean anomaly is not reduced to one revolution · case 01
Multi-revolution or negative mean anomalies return eccentric anomalies outside [0,2pi).
ROOT CAUSE
The mean anomaly is used without reduction modulo 2pi.
VERIFIED REPAIR
Reduce M into [0,2pi) before iterating.
Unsuccessful approach: Reducing modulo 360 treats a radian angle as degrees.
Case contract
Input [M, e] (radians). Return None unless 0<=e<1. Reduce M into [0,2pi), start Newton at M (e<0.8) or pi, iterate E -= (E-e sinE-M)/(1-e cosE) up to 50 times until the step is below 1e-13, and return E rounded to 9 decimals.
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):
M,e=x
if e<0 or e>=1: return None
pass
E=M if e<0.8 else math.pi
for _ in range(50):
d=(E-e*math.sin(E)-M)/(1-e*math.cos(E))
E-=d
if abs(d)<1e-13: break
return round(E,9)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987), ('elliptic kepler solver [2.0, 0.3]', [2.0, 0.3], 2.236031495), ('elliptic kepler solver [5.5, 0.6]', [5.5, 0.6], 4.911902084), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669)], [('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669), ('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654)], [('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None)], [('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882)], [('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [1.3, 0.0]', [1.3, 0.0], 1.3), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| elliptic kepler solver [-1.0, 0.2] | -1.185324204 | 5.097861103 | Failed |
| elliptic kepler solver [7.5, 0.45] | 7.948012077 | 1.66482677 | Failed |
| elliptic kepler solver [0.5, 0.1] | 0.552479987 | 0.552479987 | Passed |
| elliptic kepler solver [2.0, 0.3] | 2.236031495 | 2.236031495 | Passed |
| elliptic kepler solver [5.5, 0.6] | 4.911902084 | 4.911902084 | Passed |
| elliptic kepler solver [0.05, 0.97] | 0.588295814 | 0.588295814 | Passed |
| elliptic kepler solver [3.0, 0.99] | 3.070410669 | 3.070410669 | Passed |
SHA-256 / 4cc679f9fe972e4be8b714fc042afca378ac10bf0c5f16e290052f0ba76c1034
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
M,e=x
if e<0 or e>=1: return None
M=math.fmod(M,360)
if M<0: M+=2*math.pi
E=M if e<0.8 else math.pi
for _ in range(50):
d=(E-e*math.sin(E)-M)/(1-e*math.cos(E))
E-=d
if abs(d)<1e-13: break
return round(E,9)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987), ('elliptic kepler solver [2.0, 0.3]', [2.0, 0.3], 2.236031495), ('elliptic kepler solver [5.5, 0.6]', [5.5, 0.6], 4.911902084), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669)], [('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669), ('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654)], [('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None)], [('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882)], [('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [1.3, 0.0]', [1.3, 0.0], 1.3), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| elliptic kepler solver [-1.0, 0.2] | 5.097861103 | 5.097861103 | Passed |
| elliptic kepler solver [7.5, 0.45] | 7.948012077 | 1.66482677 | Failed |
| elliptic kepler solver [0.5, 0.1] | 0.552479987 | 0.552479987 | Passed |
| elliptic kepler solver [2.0, 0.3] | 2.236031495 | 2.236031495 | Passed |
| elliptic kepler solver [5.5, 0.6] | 4.911902084 | 4.911902084 | Passed |
| elliptic kepler solver [0.05, 0.97] | 0.588295814 | 0.588295814 | Passed |
| elliptic kepler solver [3.0, 0.99] | 3.070410669 | 3.070410669 | Passed |
SHA-256 / c029f46b5fcc14082c9bcb3fb45139c69b033418011a296d4e7954c7c83a3d0c
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
M,e=x
if e<0 or e>=1: return None
M=math.fmod(M,2*math.pi)
if M<0: M+=2*math.pi
E=M if e<0.8 else math.pi
for _ in range(50):
d=(E-e*math.sin(E)-M)/(1-e*math.cos(E))
E-=d
if abs(d)<1e-13: break
return round(E,9)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987), ('elliptic kepler solver [2.0, 0.3]', [2.0, 0.3], 2.236031495), ('elliptic kepler solver [5.5, 0.6]', [5.5, 0.6], 4.911902084), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669)], [('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [3.0, 0.99]', [3.0, 0.99], 3.070410669), ('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654)], [('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [0.0, 0.7]', [0.0, 0.7], 0.0), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None)], [('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('elliptic kepler solver [3.141592653589793, 0.5]', [3.141592653589793, 0.5], 3.141592654), ('elliptic kepler solver [1.0, 1.0]', [1.0, 1.0], None), ('elliptic kepler solver [1.0, -0.1]', [1.0, -0.1], None), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882)], [('elliptic kepler solver [-1.0, 0.2]', [-1.0, 0.2], 5.097861103), ('elliptic kepler solver [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('elliptic kepler solver [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('elliptic kepler solver [6.0, 0.93]', [6.0, 0.93], 5.162716882), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('elliptic kepler solver [1.3, 0.0]', [1.3, 0.0], 1.3), ('elliptic kepler solver [0.5, 0.1]', [0.5, 0.1], 0.552479987)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| elliptic kepler solver [-1.0, 0.2] | 5.097861103 | 5.097861103 | Passed |
| elliptic kepler solver [7.5, 0.45] | 1.66482677 | 1.66482677 | Passed |
| elliptic kepler solver [0.5, 0.1] | 0.552479987 | 0.552479987 | Passed |
| elliptic kepler solver [2.0, 0.3] | 2.236031495 | 2.236031495 | Passed |
| elliptic kepler solver [5.5, 0.6] | 4.911902084 | 4.911902084 | Passed |
| elliptic kepler solver [0.05, 0.97] | 0.588295814 | 0.588295814 | Passed |
| elliptic kepler solver [3.0, 0.99] | 3.070410669 | 3.070410669 | Passed |
SHA-256 / fe85f6ccfa5b4e1d15cdb4fa69af72c42005dc92032f0a957678fbfbfb79fb47
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:10.618463+00:00.
Case digest / f42cd844e057377e964f91e1dab6bf9e6cd778fc0c0b386500f96f7ec9b021ce