FAILURE MAP
← Case archive

FA-55221 / Astronomical coordinate conventions / Open access

Sidereal hour angle adapter: Apparent hour angle combines mean and apparent celestial quantities · case 01

The adapter reports an incorrect apparent hour angle while other fields remain valid.

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

ROOT CAUSE

Apparent hour angle combines mean and apparent celestial quantities. Faulty expression: (d['gmst']+d['lon']+d['eqeq']-d['ra'])%360

VERIFIED REPAIR

Preserve the declared model convention at this site: (d['gmst']+d['lon']+d['eqeq']-d['ra']-d['dra'])%360

Unsuccessful approach: A partial convention repair still uses (d['gmst']+d['lon']-d['ra']-d['dra'])%360

Case contract

All angular values are exact model degrees. East longitude is positive. Local mean sidereal angle is gmst+lon, apparent sidereal includes eqeq, apparent RA is ra+dra. Hour angle is sidereal minus RA, normalized into [0,360). No Earth rotation is calculated. Output fields are defined by: local_mean = (d['gmst']+d['lon'])%360; local_apparent = (d['gmst']+d['lon']+d['eqeq'])%360; apparent_ra = (d['ra']+d['dra'])%360; mean_hour_angle = (d['gmst']+d['lon']-d['ra'])%360; apparent_hour_angle = (d['gmst']+d['lon']+d['eqeq']-d['ra']-d['dra'])%360; meridian_ra = (d['gmst']+d['lon']+d['eqeq']-d['dra'])%360

Why this case matters

Catalog, detector, sky-coordinate, and spectroscopy adapters must preserve the association between numeric coordinates and their declared reference conventions.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(d):
    return {'local_mean': (d['gmst']+d['lon'])%360, 'local_apparent': (d['gmst']+d['lon']+d['eqeq'])%360, 'apparent_ra': (d['ra']+d['dra'])%360, 'mean_hour_angle': (d['gmst']+d['lon']-d['ra'])%360, 'apparent_hour_angle': (d['gmst']+d['lon']+d['eqeq']-d['ra'])%360, 'meridian_ra': (d['gmst']+d['lon']+d['eqeq']-d['dra'])%360}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = {1: [({'gmst': 100, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 130, 'local_apparent': 132, 'apparent_ra': 53, 'mean_hour_angle': 80, 'apparent_hour_angle': 79, 'meridian_ra': 129}), ({'gmst': 100, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 60, 'local_apparent': 62, 'apparent_ra': 53, 'mean_hour_angle': 10, 'apparent_hour_angle': 9, 'meridian_ra': 59}), ({'gmst': 100, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 130, 'local_apparent': 130, 'apparent_ra': 50, 'mean_hour_angle': 80, 'apparent_hour_angle': 80, 'meridian_ra': 130}), ({'gmst': 350, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 20, 'local_apparent': 22, 'apparent_ra': 8, 'mean_hour_angle': 15, 'apparent_hour_angle': 14, 'meridian_ra': 19}), ({'gmst': 100, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 130, 'local_apparent': 132, 'apparent_ra': 303, 'mean_hour_angle': 190, 'apparent_hour_angle': 189, 'meridian_ra': 129}), ({'gmst': 100, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 100, 'local_apparent': 97, 'apparent_ra': 48, 'mean_hour_angle': 50, 'apparent_hour_angle': 49, 'meridian_ra': 99})], 2: [({'gmst': 101, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 131, 'local_apparent': 133, 'apparent_ra': 53, 'mean_hour_angle': 81, 'apparent_hour_angle': 80, 'meridian_ra': 130}), ({'gmst': 101, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 61, 'local_apparent': 63, 'apparent_ra': 53, 'mean_hour_angle': 11, 'apparent_hour_angle': 10, 'meridian_ra': 60}), ({'gmst': 101, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 131, 'local_apparent': 131, 'apparent_ra': 50, 'mean_hour_angle': 81, 'apparent_hour_angle': 81, 'meridian_ra': 131}), ({'gmst': 351, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 21, 'local_apparent': 23, 'apparent_ra': 8, 'mean_hour_angle': 16, 'apparent_hour_angle': 15, 'meridian_ra': 20}), ({'gmst': 101, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 131, 'local_apparent': 133, 'apparent_ra': 303, 'mean_hour_angle': 191, 'apparent_hour_angle': 190, 'meridian_ra': 130}), ({'gmst': 101, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 101, 'local_apparent': 98, 'apparent_ra': 48, 'mean_hour_angle': 51, 'apparent_hour_angle': 50, 'meridian_ra': 100})], 3: [({'gmst': 102, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 132, 'local_apparent': 134, 'apparent_ra': 53, 'mean_hour_angle': 82, 'apparent_hour_angle': 81, 'meridian_ra': 131}), ({'gmst': 102, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 62, 'local_apparent': 64, 'apparent_ra': 53, 'mean_hour_angle': 12, 'apparent_hour_angle': 11, 'meridian_ra': 61}), ({'gmst': 102, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 132, 'local_apparent': 132, 'apparent_ra': 50, 'mean_hour_angle': 82, 'apparent_hour_angle': 82, 'meridian_ra': 132}), ({'gmst': 352, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 22, 'local_apparent': 24, 'apparent_ra': 8, 'mean_hour_angle': 17, 'apparent_hour_angle': 16, 'meridian_ra': 21}), ({'gmst': 102, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 132, 'local_apparent': 134, 'apparent_ra': 303, 'mean_hour_angle': 192, 'apparent_hour_angle': 191, 'meridian_ra': 131}), ({'gmst': 102, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 102, 'local_apparent': 99, 'apparent_ra': 48, 'mean_hour_angle': 52, 'apparent_hour_angle': 51, 'meridian_ra': 101})], 4: [({'gmst': 103, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 133, 'local_apparent': 135, 'apparent_ra': 53, 'mean_hour_angle': 83, 'apparent_hour_angle': 82, 'meridian_ra': 132}), ({'gmst': 103, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 63, 'local_apparent': 65, 'apparent_ra': 53, 'mean_hour_angle': 13, 'apparent_hour_angle': 12, 'meridian_ra': 62}), ({'gmst': 103, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 133, 'local_apparent': 133, 'apparent_ra': 50, 'mean_hour_angle': 83, 'apparent_hour_angle': 83, 'meridian_ra': 133}), ({'gmst': 353, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 23, 'local_apparent': 25, 'apparent_ra': 8, 'mean_hour_angle': 18, 'apparent_hour_angle': 17, 'meridian_ra': 22}), ({'gmst': 103, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 133, 'local_apparent': 135, 'apparent_ra': 303, 'mean_hour_angle': 193, 'apparent_hour_angle': 192, 'meridian_ra': 132}), ({'gmst': 103, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 103, 'local_apparent': 100, 'apparent_ra': 48, 'mean_hour_angle': 53, 'apparent_hour_angle': 52, 'meridian_ra': 102})], 5: [({'gmst': 104, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 134, 'local_apparent': 136, 'apparent_ra': 53, 'mean_hour_angle': 84, 'apparent_hour_angle': 83, 'meridian_ra': 133}), ({'gmst': 104, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 64, 'local_apparent': 66, 'apparent_ra': 53, 'mean_hour_angle': 14, 'apparent_hour_angle': 13, 'meridian_ra': 63}), ({'gmst': 104, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 134, 'local_apparent': 134, 'apparent_ra': 50, 'mean_hour_angle': 84, 'apparent_hour_angle': 84, 'meridian_ra': 134}), ({'gmst': 354, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 24, 'local_apparent': 26, 'apparent_ra': 8, 'mean_hour_angle': 19, 'apparent_hour_angle': 18, 'meridian_ra': 23}), ({'gmst': 104, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 134, 'local_apparent': 136, 'apparent_ra': 303, 'mean_hour_angle': 194, 'apparent_hour_angle': 193, 'meridian_ra': 133}), ({'gmst': 104, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 104, 'local_apparent': 101, 'apparent_ra': 48, 'mean_hour_angle': 54, 'apparent_hour_angle': 53, 'meridian_ra': 103})]}
for i, (record, expected) in enumerate(fixtures[N]):
    check('astronomical fixture %s' % i, solve(record), 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
astronomical fixture 0{'apparent_hour_angle': 82, 'apparent_ra': 53, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 129}{'apparent_hour_angle': 79, 'apparent_ra': 53, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 129}Failed
astronomical fixture 1{'apparent_hour_angle': 12, 'apparent_ra': 53, 'local_apparent': 62, 'local_mean': 60, 'mean_hour_angle': 10, 'meridian_ra': 59}{'apparent_hour_angle': 9, 'apparent_ra': 53, 'local_apparent': 62, 'local_mean': 60, 'mean_hour_angle': 10, 'meridian_ra': 59}Failed
astronomical fixture 2{'apparent_hour_angle': 80, 'apparent_ra': 50, 'local_apparent': 130, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 130}{'apparent_hour_angle': 80, 'apparent_ra': 50, 'local_apparent': 130, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 130}Passed
astronomical fixture 3{'apparent_hour_angle': 17, 'apparent_ra': 8, 'local_apparent': 22, 'local_mean': 20, 'mean_hour_angle': 15, 'meridian_ra': 19}{'apparent_hour_angle': 14, 'apparent_ra': 8, 'local_apparent': 22, 'local_mean': 20, 'mean_hour_angle': 15, 'meridian_ra': 19}Failed
astronomical fixture 4{'apparent_hour_angle': 192, 'apparent_ra': 303, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 190, 'meridian_ra': 129}{'apparent_hour_angle': 189, 'apparent_ra': 303, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 190, 'meridian_ra': 129}Failed
astronomical fixture 5{'apparent_hour_angle': 47, 'apparent_ra': 48, 'local_apparent': 97, 'local_mean': 100, 'mean_hour_angle': 50, 'meridian_ra': 99}{'apparent_hour_angle': 49, 'apparent_ra': 48, 'local_apparent': 97, 'local_mean': 100, 'mean_hour_angle': 50, 'meridian_ra': 99}Failed

SHA-256 / d1973d7eb071a9f42cd2bfdae0f3c150837e626c6df089ab196d451fa8b0398a

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(d):
    return {'local_mean': (d['gmst']+d['lon'])%360, 'local_apparent': (d['gmst']+d['lon']+d['eqeq'])%360, 'apparent_ra': (d['ra']+d['dra'])%360, 'mean_hour_angle': (d['gmst']+d['lon']-d['ra'])%360, 'apparent_hour_angle': (d['gmst']+d['lon']-d['ra']-d['dra'])%360, 'meridian_ra': (d['gmst']+d['lon']+d['eqeq']-d['dra'])%360}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = {1: [({'gmst': 100, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 130, 'local_apparent': 132, 'apparent_ra': 53, 'mean_hour_angle': 80, 'apparent_hour_angle': 79, 'meridian_ra': 129}), ({'gmst': 100, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 60, 'local_apparent': 62, 'apparent_ra': 53, 'mean_hour_angle': 10, 'apparent_hour_angle': 9, 'meridian_ra': 59}), ({'gmst': 100, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 130, 'local_apparent': 130, 'apparent_ra': 50, 'mean_hour_angle': 80, 'apparent_hour_angle': 80, 'meridian_ra': 130}), ({'gmst': 350, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 20, 'local_apparent': 22, 'apparent_ra': 8, 'mean_hour_angle': 15, 'apparent_hour_angle': 14, 'meridian_ra': 19}), ({'gmst': 100, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 130, 'local_apparent': 132, 'apparent_ra': 303, 'mean_hour_angle': 190, 'apparent_hour_angle': 189, 'meridian_ra': 129}), ({'gmst': 100, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 100, 'local_apparent': 97, 'apparent_ra': 48, 'mean_hour_angle': 50, 'apparent_hour_angle': 49, 'meridian_ra': 99})], 2: [({'gmst': 101, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 131, 'local_apparent': 133, 'apparent_ra': 53, 'mean_hour_angle': 81, 'apparent_hour_angle': 80, 'meridian_ra': 130}), ({'gmst': 101, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 61, 'local_apparent': 63, 'apparent_ra': 53, 'mean_hour_angle': 11, 'apparent_hour_angle': 10, 'meridian_ra': 60}), ({'gmst': 101, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 131, 'local_apparent': 131, 'apparent_ra': 50, 'mean_hour_angle': 81, 'apparent_hour_angle': 81, 'meridian_ra': 131}), ({'gmst': 351, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 21, 'local_apparent': 23, 'apparent_ra': 8, 'mean_hour_angle': 16, 'apparent_hour_angle': 15, 'meridian_ra': 20}), ({'gmst': 101, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 131, 'local_apparent': 133, 'apparent_ra': 303, 'mean_hour_angle': 191, 'apparent_hour_angle': 190, 'meridian_ra': 130}), ({'gmst': 101, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 101, 'local_apparent': 98, 'apparent_ra': 48, 'mean_hour_angle': 51, 'apparent_hour_angle': 50, 'meridian_ra': 100})], 3: [({'gmst': 102, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 132, 'local_apparent': 134, 'apparent_ra': 53, 'mean_hour_angle': 82, 'apparent_hour_angle': 81, 'meridian_ra': 131}), ({'gmst': 102, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 62, 'local_apparent': 64, 'apparent_ra': 53, 'mean_hour_angle': 12, 'apparent_hour_angle': 11, 'meridian_ra': 61}), ({'gmst': 102, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 132, 'local_apparent': 132, 'apparent_ra': 50, 'mean_hour_angle': 82, 'apparent_hour_angle': 82, 'meridian_ra': 132}), ({'gmst': 352, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 22, 'local_apparent': 24, 'apparent_ra': 8, 'mean_hour_angle': 17, 'apparent_hour_angle': 16, 'meridian_ra': 21}), ({'gmst': 102, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 132, 'local_apparent': 134, 'apparent_ra': 303, 'mean_hour_angle': 192, 'apparent_hour_angle': 191, 'meridian_ra': 131}), ({'gmst': 102, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 102, 'local_apparent': 99, 'apparent_ra': 48, 'mean_hour_angle': 52, 'apparent_hour_angle': 51, 'meridian_ra': 101})], 4: [({'gmst': 103, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 133, 'local_apparent': 135, 'apparent_ra': 53, 'mean_hour_angle': 83, 'apparent_hour_angle': 82, 'meridian_ra': 132}), ({'gmst': 103, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 63, 'local_apparent': 65, 'apparent_ra': 53, 'mean_hour_angle': 13, 'apparent_hour_angle': 12, 'meridian_ra': 62}), ({'gmst': 103, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 133, 'local_apparent': 133, 'apparent_ra': 50, 'mean_hour_angle': 83, 'apparent_hour_angle': 83, 'meridian_ra': 133}), ({'gmst': 353, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 23, 'local_apparent': 25, 'apparent_ra': 8, 'mean_hour_angle': 18, 'apparent_hour_angle': 17, 'meridian_ra': 22}), ({'gmst': 103, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 133, 'local_apparent': 135, 'apparent_ra': 303, 'mean_hour_angle': 193, 'apparent_hour_angle': 192, 'meridian_ra': 132}), ({'gmst': 103, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 103, 'local_apparent': 100, 'apparent_ra': 48, 'mean_hour_angle': 53, 'apparent_hour_angle': 52, 'meridian_ra': 102})], 5: [({'gmst': 104, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 134, 'local_apparent': 136, 'apparent_ra': 53, 'mean_hour_angle': 84, 'apparent_hour_angle': 83, 'meridian_ra': 133}), ({'gmst': 104, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 64, 'local_apparent': 66, 'apparent_ra': 53, 'mean_hour_angle': 14, 'apparent_hour_angle': 13, 'meridian_ra': 63}), ({'gmst': 104, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 134, 'local_apparent': 134, 'apparent_ra': 50, 'mean_hour_angle': 84, 'apparent_hour_angle': 84, 'meridian_ra': 134}), ({'gmst': 354, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 24, 'local_apparent': 26, 'apparent_ra': 8, 'mean_hour_angle': 19, 'apparent_hour_angle': 18, 'meridian_ra': 23}), ({'gmst': 104, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 134, 'local_apparent': 136, 'apparent_ra': 303, 'mean_hour_angle': 194, 'apparent_hour_angle': 193, 'meridian_ra': 133}), ({'gmst': 104, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 104, 'local_apparent': 101, 'apparent_ra': 48, 'mean_hour_angle': 54, 'apparent_hour_angle': 53, 'meridian_ra': 103})]}
for i, (record, expected) in enumerate(fixtures[N]):
    check('astronomical fixture %s' % i, solve(record), 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
astronomical fixture 0{'apparent_hour_angle': 77, 'apparent_ra': 53, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 129}{'apparent_hour_angle': 79, 'apparent_ra': 53, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 129}Failed
astronomical fixture 1{'apparent_hour_angle': 7, 'apparent_ra': 53, 'local_apparent': 62, 'local_mean': 60, 'mean_hour_angle': 10, 'meridian_ra': 59}{'apparent_hour_angle': 9, 'apparent_ra': 53, 'local_apparent': 62, 'local_mean': 60, 'mean_hour_angle': 10, 'meridian_ra': 59}Failed
astronomical fixture 2{'apparent_hour_angle': 80, 'apparent_ra': 50, 'local_apparent': 130, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 130}{'apparent_hour_angle': 80, 'apparent_ra': 50, 'local_apparent': 130, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 130}Passed
astronomical fixture 3{'apparent_hour_angle': 12, 'apparent_ra': 8, 'local_apparent': 22, 'local_mean': 20, 'mean_hour_angle': 15, 'meridian_ra': 19}{'apparent_hour_angle': 14, 'apparent_ra': 8, 'local_apparent': 22, 'local_mean': 20, 'mean_hour_angle': 15, 'meridian_ra': 19}Failed
astronomical fixture 4{'apparent_hour_angle': 187, 'apparent_ra': 303, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 190, 'meridian_ra': 129}{'apparent_hour_angle': 189, 'apparent_ra': 303, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 190, 'meridian_ra': 129}Failed
astronomical fixture 5{'apparent_hour_angle': 52, 'apparent_ra': 48, 'local_apparent': 97, 'local_mean': 100, 'mean_hour_angle': 50, 'meridian_ra': 99}{'apparent_hour_angle': 49, 'apparent_ra': 48, 'local_apparent': 97, 'local_mean': 100, 'mean_hour_angle': 50, 'meridian_ra': 99}Failed

SHA-256 / bd98feecc005cc96833881b7f71092ebf014fe6289a3c02794dce94edcd8df78

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(d):
    return {'local_mean': (d['gmst']+d['lon'])%360, 'local_apparent': (d['gmst']+d['lon']+d['eqeq'])%360, 'apparent_ra': (d['ra']+d['dra'])%360, 'mean_hour_angle': (d['gmst']+d['lon']-d['ra'])%360, 'apparent_hour_angle': (d['gmst']+d['lon']+d['eqeq']-d['ra']-d['dra'])%360, 'meridian_ra': (d['gmst']+d['lon']+d['eqeq']-d['dra'])%360}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = {1: [({'gmst': 100, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 130, 'local_apparent': 132, 'apparent_ra': 53, 'mean_hour_angle': 80, 'apparent_hour_angle': 79, 'meridian_ra': 129}), ({'gmst': 100, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 60, 'local_apparent': 62, 'apparent_ra': 53, 'mean_hour_angle': 10, 'apparent_hour_angle': 9, 'meridian_ra': 59}), ({'gmst': 100, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 130, 'local_apparent': 130, 'apparent_ra': 50, 'mean_hour_angle': 80, 'apparent_hour_angle': 80, 'meridian_ra': 130}), ({'gmst': 350, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 20, 'local_apparent': 22, 'apparent_ra': 8, 'mean_hour_angle': 15, 'apparent_hour_angle': 14, 'meridian_ra': 19}), ({'gmst': 100, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 130, 'local_apparent': 132, 'apparent_ra': 303, 'mean_hour_angle': 190, 'apparent_hour_angle': 189, 'meridian_ra': 129}), ({'gmst': 100, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 100, 'local_apparent': 97, 'apparent_ra': 48, 'mean_hour_angle': 50, 'apparent_hour_angle': 49, 'meridian_ra': 99})], 2: [({'gmst': 101, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 131, 'local_apparent': 133, 'apparent_ra': 53, 'mean_hour_angle': 81, 'apparent_hour_angle': 80, 'meridian_ra': 130}), ({'gmst': 101, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 61, 'local_apparent': 63, 'apparent_ra': 53, 'mean_hour_angle': 11, 'apparent_hour_angle': 10, 'meridian_ra': 60}), ({'gmst': 101, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 131, 'local_apparent': 131, 'apparent_ra': 50, 'mean_hour_angle': 81, 'apparent_hour_angle': 81, 'meridian_ra': 131}), ({'gmst': 351, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 21, 'local_apparent': 23, 'apparent_ra': 8, 'mean_hour_angle': 16, 'apparent_hour_angle': 15, 'meridian_ra': 20}), ({'gmst': 101, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 131, 'local_apparent': 133, 'apparent_ra': 303, 'mean_hour_angle': 191, 'apparent_hour_angle': 190, 'meridian_ra': 130}), ({'gmst': 101, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 101, 'local_apparent': 98, 'apparent_ra': 48, 'mean_hour_angle': 51, 'apparent_hour_angle': 50, 'meridian_ra': 100})], 3: [({'gmst': 102, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 132, 'local_apparent': 134, 'apparent_ra': 53, 'mean_hour_angle': 82, 'apparent_hour_angle': 81, 'meridian_ra': 131}), ({'gmst': 102, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 62, 'local_apparent': 64, 'apparent_ra': 53, 'mean_hour_angle': 12, 'apparent_hour_angle': 11, 'meridian_ra': 61}), ({'gmst': 102, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 132, 'local_apparent': 132, 'apparent_ra': 50, 'mean_hour_angle': 82, 'apparent_hour_angle': 82, 'meridian_ra': 132}), ({'gmst': 352, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 22, 'local_apparent': 24, 'apparent_ra': 8, 'mean_hour_angle': 17, 'apparent_hour_angle': 16, 'meridian_ra': 21}), ({'gmst': 102, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 132, 'local_apparent': 134, 'apparent_ra': 303, 'mean_hour_angle': 192, 'apparent_hour_angle': 191, 'meridian_ra': 131}), ({'gmst': 102, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 102, 'local_apparent': 99, 'apparent_ra': 48, 'mean_hour_angle': 52, 'apparent_hour_angle': 51, 'meridian_ra': 101})], 4: [({'gmst': 103, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 133, 'local_apparent': 135, 'apparent_ra': 53, 'mean_hour_angle': 83, 'apparent_hour_angle': 82, 'meridian_ra': 132}), ({'gmst': 103, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 63, 'local_apparent': 65, 'apparent_ra': 53, 'mean_hour_angle': 13, 'apparent_hour_angle': 12, 'meridian_ra': 62}), ({'gmst': 103, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 133, 'local_apparent': 133, 'apparent_ra': 50, 'mean_hour_angle': 83, 'apparent_hour_angle': 83, 'meridian_ra': 133}), ({'gmst': 353, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 23, 'local_apparent': 25, 'apparent_ra': 8, 'mean_hour_angle': 18, 'apparent_hour_angle': 17, 'meridian_ra': 22}), ({'gmst': 103, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 133, 'local_apparent': 135, 'apparent_ra': 303, 'mean_hour_angle': 193, 'apparent_hour_angle': 192, 'meridian_ra': 132}), ({'gmst': 103, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 103, 'local_apparent': 100, 'apparent_ra': 48, 'mean_hour_angle': 53, 'apparent_hour_angle': 52, 'meridian_ra': 102})], 5: [({'gmst': 104, 'lon': 30, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 134, 'local_apparent': 136, 'apparent_ra': 53, 'mean_hour_angle': 84, 'apparent_hour_angle': 83, 'meridian_ra': 133}), ({'gmst': 104, 'lon': -40, 'eqeq': 2, 'ra': 50, 'dra': 3}, {'local_mean': 64, 'local_apparent': 66, 'apparent_ra': 53, 'mean_hour_angle': 14, 'apparent_hour_angle': 13, 'meridian_ra': 63}), ({'gmst': 104, 'lon': 30, 'eqeq': 0, 'ra': 50, 'dra': 0}, {'local_mean': 134, 'local_apparent': 134, 'apparent_ra': 50, 'mean_hour_angle': 84, 'apparent_hour_angle': 84, 'meridian_ra': 134}), ({'gmst': 354, 'lon': 30, 'eqeq': 2, 'ra': 5, 'dra': 3}, {'local_mean': 24, 'local_apparent': 26, 'apparent_ra': 8, 'mean_hour_angle': 19, 'apparent_hour_angle': 18, 'meridian_ra': 23}), ({'gmst': 104, 'lon': 30, 'eqeq': 2, 'ra': 300, 'dra': 3}, {'local_mean': 134, 'local_apparent': 136, 'apparent_ra': 303, 'mean_hour_angle': 194, 'apparent_hour_angle': 193, 'meridian_ra': 133}), ({'gmst': 104, 'lon': 0, 'eqeq': -3, 'ra': 50, 'dra': -2}, {'local_mean': 104, 'local_apparent': 101, 'apparent_ra': 48, 'mean_hour_angle': 54, 'apparent_hour_angle': 53, 'meridian_ra': 103})]}
for i, (record, expected) in enumerate(fixtures[N]):
    check('astronomical fixture %s' % i, solve(record), 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
astronomical fixture 0{'apparent_hour_angle': 79, 'apparent_ra': 53, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 129}{'apparent_hour_angle': 79, 'apparent_ra': 53, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 129}Passed
astronomical fixture 1{'apparent_hour_angle': 9, 'apparent_ra': 53, 'local_apparent': 62, 'local_mean': 60, 'mean_hour_angle': 10, 'meridian_ra': 59}{'apparent_hour_angle': 9, 'apparent_ra': 53, 'local_apparent': 62, 'local_mean': 60, 'mean_hour_angle': 10, 'meridian_ra': 59}Passed
astronomical fixture 2{'apparent_hour_angle': 80, 'apparent_ra': 50, 'local_apparent': 130, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 130}{'apparent_hour_angle': 80, 'apparent_ra': 50, 'local_apparent': 130, 'local_mean': 130, 'mean_hour_angle': 80, 'meridian_ra': 130}Passed
astronomical fixture 3{'apparent_hour_angle': 14, 'apparent_ra': 8, 'local_apparent': 22, 'local_mean': 20, 'mean_hour_angle': 15, 'meridian_ra': 19}{'apparent_hour_angle': 14, 'apparent_ra': 8, 'local_apparent': 22, 'local_mean': 20, 'mean_hour_angle': 15, 'meridian_ra': 19}Passed
astronomical fixture 4{'apparent_hour_angle': 189, 'apparent_ra': 303, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 190, 'meridian_ra': 129}{'apparent_hour_angle': 189, 'apparent_ra': 303, 'local_apparent': 132, 'local_mean': 130, 'mean_hour_angle': 190, 'meridian_ra': 129}Passed
astronomical fixture 5{'apparent_hour_angle': 49, 'apparent_ra': 48, 'local_apparent': 97, 'local_mean': 100, 'mean_hour_angle': 50, 'meridian_ra': 99}{'apparent_hour_angle': 49, 'apparent_ra': 48, 'local_apparent': 97, 'local_mean': 100, 'mean_hour_angle': 50, 'meridian_ra': 99}Passed

SHA-256 / b07be2936bb304c385d2d3d7615fe06ae3fd00d379a9a98700c506bc0e000365

Verification & scope

Explicitly stipulated finite algebraic adapter; no standards conformance, physical accuracy, or production-library claim. Inputs are the documented finite valid model domain. 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:45:55.813401+00:00.

Case digest / 6f5978586610d229dfe8a706cd2a5e8ad1c30fa26a05ab602650608709e67d0f