FA-69351 / Orbital propagation / Open access
Elliptic Kepler solver: Newton loop stops at a millimetre-scale tolerance · case 01
Eccentric anomalies are only good to about three decimals.
ROOT CAUSE
The convergence tolerance is 1e-3 radians.
VERIFIED REPAIR
Iterate until the step is below 1e-13.
Unsuccessful approach: A 1e-6 tolerance tested before applying the step still returns the stale iterate.
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
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-3: 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 [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('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 [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 [7.5, 0.45]', [7.5, 0.45], 1.66482677)], [('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('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 [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('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 [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('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.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 [6.0, 0.93]', [6.0, 0.93], 5.162716882), ('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 [-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.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('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.3, 0.0]', [1.3, 0.0], 1.3), ('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)]]
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 [0.05, 0.97] | 0.588296388 | 0.588295814 | 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 [3.0, 0.99] | 3.070410669 | 3.070410669 | Passed |
| elliptic kepler solver [-1.0, 0.2] | 5.097861103 | 5.097861103 | Passed |
| elliptic kepler solver [7.5, 0.45] | 1.66482684 | 1.66482677 | Failed |
SHA-256 / 0fb3057949d61e3522bb07ffeca8d60bb4676b1d53d65e539baa1d4a9590c11d
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,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))
if abs(d)<1e-6: break
E-=d
return round(E,9)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('elliptic kepler solver [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('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 [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 [7.5, 0.45]', [7.5, 0.45], 1.66482677)], [('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('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 [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('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 [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('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.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 [6.0, 0.93]', [6.0, 0.93], 5.162716882), ('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 [-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.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('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.3, 0.0]', [1.3, 0.0], 1.3), ('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)]]
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 [0.05, 0.97] | 0.588296388 | 0.588295814 | 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 [3.0, 0.99] | 3.070410669 | 3.070410669 | Passed |
| elliptic kepler solver [-1.0, 0.2] | 5.097861103 | 5.097861103 | Passed |
| elliptic kepler solver [7.5, 0.45] | 1.66482684 | 1.66482677 | Failed |
SHA-256 / 934a86a8115b11c9de81dcdcf1ac3a20502c99cb11a75b38665ea485eaf1a6f8
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 [0.05, 0.97]', [0.05, 0.97], 0.588295814), ('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 [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 [7.5, 0.45]', [7.5, 0.45], 1.66482677)], [('elliptic kepler solver [7.5, 0.45]', [7.5, 0.45], 1.66482677), ('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 [20.0, 0.05]', [20.0, 0.05], 1.196991311), ('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 [0.2, 0.85]', [0.2, 0.85], 0.823499414), ('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.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 [6.0, 0.93]', [6.0, 0.93], 5.162716882), ('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 [-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.05, 0.97]', [0.05, 0.97], 0.588295814), ('elliptic kepler solver [-8.0, 0.75]', [-8.0, 0.75], 3.999173912), ('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.3, 0.0]', [1.3, 0.0], 1.3), ('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)]]
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 [0.05, 0.97] | 0.588295814 | 0.588295814 | 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 [3.0, 0.99] | 3.070410669 | 3.070410669 | Passed |
| elliptic kepler solver [-1.0, 0.2] | 5.097861103 | 5.097861103 | Passed |
| elliptic kepler solver [7.5, 0.45] | 1.66482677 | 1.66482677 | Passed |
SHA-256 / 297f7d5315613022a1f01c0e0428c7f1862461d22db031dbd7c3244f1b83efa6
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.658583+00:00.
Case digest / 9a8abfe0fb0cdc208775f9565c0b02990b51d89d52fee2f6cdf42516f539949c