code wiki / (root) / nx_solar_system_catalog.nx

nx_solar_system_catalog.nx source

↩ module page · 728 lines · 37107 B

1// nx_solar_system_catalog.nx -- catalog of solar-system bodies. 2// 3// Cross-cutting Substrate C foundational data primitive per 4// nxc2/docs/NISHI_GAME_ENGINE_ROADMAP.md "Solar system macro- 5// benchmark." Holds physical parameters (mass, radius, orbit, 6// composition flags) for every major solar-system body the 7// substrate generates. 8// 9// FULL CATALOG (per feedback-maximum-capability-no-simplification 10// cardinal, 2026-05-16): 37 bodies covering all 5 categories: 11// - 1 star (Sun) 12// - 8 planets (Mercury -> Neptune) 13// - 13 major moons (Luna, Phobos, Deimos, 4 Galileans, Titan + 14// 3 Saturn icy, Titania, Oberon, Triton) 15// - 5 dwarf planets (Ceres, Pluto, Haumea, Makemake, Eris) 16// - 3 representative asteroids (Vesta, Pallas, Hygiea) 17// - 3 well-known comets (1P/Halley, C/1995 O1 Hale-Bopp, 18// 2I/Borisov interstellar) 19// - 3 TNOs (Sedna, Quaoar, Orcus) 20// 21// Per-body fields: ID, kind, radius_km, mass_e20kg (0 for sub-Phobos 22// bodies where Q14 underflows), semi-major axis (AU for solar orbits; 23// thousands-km for moon orbits relative to their parent), orbital 24// period (days), rotation period (hours), atmosphere flag, rings flag, 25// PARENT BODY ID (NX_BODY_SUN for planets/dwarfs/asteroids/TNOs; 26// parent planet for moons; 0 - 1 for the Sun itself). 27// 28// Data source: NASA JPL Horizons + Wikipedia infoboxes for the 29// physical parameters. Per cardinal 30// `feedback-no-third-party-trust-native-or-nothing`: these are 31// PUBLIC-DOMAIN scientific reference values (NASA is US federal 32// civil-service work, TIER_0_UNENCUMBERED). No copyrighted 33// imagery or text absorbed -- only numbers. 34// 35// Per cardinal `feedback-procgen-causal-growth-not-direct-sampling`: 36// these parameters are the CAUSAL INPUTS to per-body generators 37// (atmosphere -> climate -> biome -> surface features). Without 38// this catalog, the per-body generators would be guessing. 39// 40// Q14 conventions across the catalog: 41// radius_km_q14: radius in kilometres (Sun ~ 695700) 42// mass_e20kg_q14: mass in units of 1e20 kg (scaling 43// keeps Sun ~ 1.989e10 fitting in i64) 44// semi_major_axis_au_q14: orbital distance from Sun (Earth=1.0) 45// orbital_period_days_q14: Earth-days for one orbit 46// rotation_period_hours_q14: hours for one rotation on own axis 47// 48// Loss audit: all values are Q14-quantised scientific measurements. 49// Real-world measurement precision is typically 0.1-1% for these 50// quantities; Q14 (1 part in 16384) significantly exceeds that. 51// 52// genealogy_id: nasa_jpl_horizons_2025 + wikipedia_solar_system_infoboxes 53// lineage_id: nx_solar_system_catalog_q14_first_slice 54 55// nx_safety_envelope: 56// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 57// sil_target: SIL1 58// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 59// verdict: NOT_YET_EVALUATED 60 61import "nx_syscalls.nx" 62import "nx_hal.nx" 63import "nx_tier.nx" 64const NX_MAGIC_695700: i64 = 695700 65const NX_MAGIC_2440: i64 = 2440 66const NX_MAGIC_6052: i64 = 6052 67const NX_MAGIC_6371: i64 = 6371 68const NX_MAGIC_3390: i64 = 3390 69const NX_MAGIC_69911: i64 = 69911 70const NX_MAGIC_58232: i64 = 58232 71const NX_MAGIC_25362: i64 = 25362 72const NX_MAGIC_24622: i64 = 24622 73const NX_MAGIC_1737: i64 = 1737 74const NX_MAGIC_1821: i64 = 1821 75const NX_MAGIC_1561: i64 = 1561 76const NX_MAGIC_2634: i64 = 2634 77const NX_MAGIC_2410: i64 = 2410 78const NX_MAGIC_2575: i64 = 2575 79const NX_MAGIC_1353: i64 = 1353 80const NX_MAGIC_1188: i64 = 1188 81const NX_MAGIC_1163: i64 = 1163 82const NX_MAGIC_19890000000: i64 = 19890000000 83const NX_MAGIC_3301: i64 = 3301 84const NX_MAGIC_48675: i64 = 48675 85const NX_MAGIC_59720: i64 = 59720 86const NX_MAGIC_6417: i64 = 6417 87const NX_MAGIC_18981300: i64 = 18981300 88const NX_MAGIC_5683300: i64 = 5683300 89const NX_MAGIC_868100: i64 = 868100 90const NX_MAGIC_1024100: i64 = 1024100 91const NX_MAGIC_1482: i64 = 1482 92const NX_MAGIC_1076: i64 = 1076 93const NX_MAGIC_1345: i64 = 1345 94const NX_MAGIC_1524: i64 = 1524 95const NX_MAGIC_5203: i64 = 5203 96const NX_MAGIC_9539: i64 = 9539 97const NX_MAGIC_19180: i64 = 19180 98const NX_MAGIC_30070: i64 = 30070 99const NX_MAGIC_16000: i64 = 16000 100const NX_MAGIC_6400: i64 = 6400 101const NX_MAGIC_2766: i64 = 2766 102const NX_MAGIC_3948: i64 = 3948 103const NX_MAGIC_4317: i64 = 4317 104const NX_MAGIC_4571: i64 = 4571 105const NX_MAGIC_6778: i64 = 6778 106const NX_MAGIC_2362: i64 = 2362 107const NX_MAGIC_2773: i64 = 2773 108const NX_MAGIC_3140: i64 = 3140 109const NX_MAGIC_1782: i64 = 1782 110const NX_MAGIC_1800: i64 = 1800 111const NX_MAGIC_4348: i64 = 4348 112const NX_MAGIC_3927: i64 = 3927 113const NX_MAGIC_4333: i64 = 4333 114const NX_MAGIC_10759: i64 = 10759 115const NX_MAGIC_30688: i64 = 30688 116const NX_MAGIC_60182: i64 = 60182 117const NX_MAGIC_1669: i64 = 1669 118const NX_MAGIC_1595: i64 = 1595 119const NX_MAGIC_1346: i64 = 1346 120const NX_MAGIC_1681: i64 = 1681 121const NX_MAGIC_90560: i64 = 90560 122const NX_MAGIC_103774: i64 = 103774 123const NX_MAGIC_112897: i64 = 112897 124const NX_MAGIC_203830: i64 = 203830 125const NX_MAGIC_1325: i64 = 1325 126const NX_MAGIC_1686: i64 = 1686 127const NX_MAGIC_2034: i64 = 2034 128const NX_MAGIC_27759: i64 = 27759 129const NX_MAGIC_924795: i64 = 924795 130const NX_MAGIC_4150000: i64 = 4150000 131const NX_MAGIC_11400: i64 = 11400 132const NX_MAGIC_104744: i64 = 104744 133const NX_MAGIC_89530: i64 = 89530 134const NX_MAGIC_1408: i64 = 1408 135const NX_MAGIC_5832: i64 = 5832 136const NX_MAGIC_100000: i64 = 100000 137 138// ===== Q14 ========================================================== 139const NX_SOLAR_Q: nx_int = 16384 140 141// ===== Body IDs (sealed enum) ====================================== 142// First slice: 9 bodies indexed 0..8. Add new IDs at the END for 143// subsequent slices per cardinal rule 13 additive-only. 144const NX_BODY_SUN: nx_int = 0 145const NX_BODY_MERCURY: nx_int = 1 146const NX_BODY_VENUS: nx_int = 2 147const NX_BODY_EARTH: nx_int = 3 148const NX_BODY_MARS: nx_int = 4 149const NX_BODY_JUPITER: nx_int = 5 150const NX_BODY_SATURN: nx_int = 6 151const NX_BODY_URANUS: nx_int = 7 152const NX_BODY_NEPTUNE: nx_int = 8 153 154// Major moons (13). 155const NX_BODY_LUNA: nx_int = 9 // Earth's moon 156const NX_BODY_PHOBOS: nx_int = 10 157const NX_BODY_DEIMOS: nx_int = 11 158const NX_BODY_IO: nx_int = 12 159const NX_BODY_EUROPA: nx_int = 13 160const NX_BODY_GANYMEDE: nx_int = 14 161const NX_BODY_CALLISTO: nx_int = 15 162const NX_BODY_TITAN: nx_int = 16 163const NX_BODY_ENCELADUS: nx_int = 17 164const NX_BODY_RHEA: nx_int = 18 165const NX_BODY_IAPETUS: nx_int = 19 166const NX_BODY_TITANIA: nx_int = 20 167const NX_BODY_OBERON: nx_int = 21 168const NX_BODY_TRITON: nx_int = 22 169 170// Dwarf planets (5). 171const NX_BODY_CERES: nx_int = 23 172const NX_BODY_PLUTO: nx_int = 24 173const NX_BODY_HAUMEA: nx_int = 25 174const NX_BODY_MAKEMAKE: nx_int = 26 175const NX_BODY_ERIS: nx_int = 27 176 177// Major asteroids (3). 178const NX_BODY_VESTA: nx_int = 28 179const NX_BODY_PALLAS: nx_int = 29 180const NX_BODY_HYGIEA: nx_int = 30 181 182// Notable comets (3). 183const NX_BODY_HALLEY: nx_int = 31 // 1P/Halley 184const NX_BODY_HALE_BOPP: nx_int = 32 // C/1995 O1 185const NX_BODY_BORISOV: nx_int = 33 // 2I/Borisov (interstellar) 186 187// Trans-Neptunian objects (3). 188const NX_BODY_SEDNA: nx_int = 34 189const NX_BODY_QUAOAR: nx_int = 35 190const NX_BODY_ORCUS: nx_int = 36 191 192const NX_SOLAR_BODY_COUNT: nx_int = 37 193 194// ===== Body kinds (sealed enum) ==================================== 195// Routing categories. Per-kind generators (queued) dispatch on these. 196const NX_CELESTIAL_KIND_STAR: nx_int = 0 197const NX_CELESTIAL_KIND_TERRESTRIAL_PLANET: nx_int = 1 198const NX_CELESTIAL_KIND_GAS_GIANT: nx_int = 2 199const NX_CELESTIAL_KIND_ICE_GIANT: nx_int = 3 200const NX_CELESTIAL_KIND_DWARF_PLANET: nx_int = 4 // queued S5 201const NX_CELESTIAL_KIND_ROCKY_MOON: nx_int = 5 // queued S2 202const NX_CELESTIAL_KIND_ICY_MOON: nx_int = 6 // queued S2-S4 203const NX_CELESTIAL_KIND_ASTEROID: nx_int = 7 // queued S6 204const NX_CELESTIAL_KIND_COMET: nx_int = 8 // queued S7 205const NX_CELESTIAL_KIND_KUIPER_OBJECT: nx_int = 9 // queued S8 206 207const NX_CELESTIAL_KIND_COUNT: nx_int = 10 208 209// ===== Validity predicates ========================================= 210func nx_solar_body_id_is_valid(id: nx_int) -> nx_int { 211 if id < 0 { return 0 } 212 if id >= NX_SOLAR_BODY_COUNT { return 0 } 213 return 1 214} 215 216func nx_celestial_kind_is_valid(k: nx_int) -> nx_int { 217 if k == NX_CELESTIAL_KIND_STAR { return 1 } 218 if k == NX_CELESTIAL_KIND_TERRESTRIAL_PLANET { return 1 } 219 if k == NX_CELESTIAL_KIND_GAS_GIANT { return 1 } 220 if k == NX_CELESTIAL_KIND_ICE_GIANT { return 1 } 221 if k == NX_CELESTIAL_KIND_DWARF_PLANET { return 1 } 222 if k == NX_CELESTIAL_KIND_ROCKY_MOON { return 1 } 223 if k == NX_CELESTIAL_KIND_ICY_MOON { return 1 } 224 if k == NX_CELESTIAL_KIND_ASTEROID { return 1 } 225 if k == NX_CELESTIAL_KIND_COMET { return 1 } 226 if k == NX_CELESTIAL_KIND_KUIPER_OBJECT { return 1 } 227 return 0 228} 229 230// ===== Accessor: kind ============================================== 231// Returns the sealed-enum NX_CELESTIAL_KIND_* for a body. 232// Out-of-bounds -> -1 (caller responsibility to check via 233// nx_solar_body_id_is_valid first). 234func nx_solar_body_kind(id: nx_int) -> nx_int { 235 if id == NX_BODY_SUN { return NX_CELESTIAL_KIND_STAR } 236 if id == NX_BODY_MERCURY { return NX_CELESTIAL_KIND_TERRESTRIAL_PLANET } 237 if id == NX_BODY_VENUS { return NX_CELESTIAL_KIND_TERRESTRIAL_PLANET } 238 if id == NX_BODY_EARTH { return NX_CELESTIAL_KIND_TERRESTRIAL_PLANET } 239 if id == NX_BODY_MARS { return NX_CELESTIAL_KIND_TERRESTRIAL_PLANET } 240 if id == NX_BODY_JUPITER { return NX_CELESTIAL_KIND_GAS_GIANT } 241 if id == NX_BODY_SATURN { return NX_CELESTIAL_KIND_GAS_GIANT } 242 if id == NX_BODY_URANUS { return NX_CELESTIAL_KIND_ICE_GIANT } 243 if id == NX_BODY_NEPTUNE { return NX_CELESTIAL_KIND_ICE_GIANT } 244 // Moons. 245 if id == NX_BODY_LUNA { return NX_CELESTIAL_KIND_ROCKY_MOON } 246 if id == NX_BODY_PHOBOS { return NX_CELESTIAL_KIND_ROCKY_MOON } 247 if id == NX_BODY_DEIMOS { return NX_CELESTIAL_KIND_ROCKY_MOON } 248 if id == NX_BODY_IO { return NX_CELESTIAL_KIND_ROCKY_MOON } // volcanic 249 if id == NX_BODY_EUROPA { return NX_CELESTIAL_KIND_ICY_MOON } 250 if id == NX_BODY_GANYMEDE { return NX_CELESTIAL_KIND_ICY_MOON } 251 if id == NX_BODY_CALLISTO { return NX_CELESTIAL_KIND_ICY_MOON } 252 if id == NX_BODY_TITAN { return NX_CELESTIAL_KIND_ICY_MOON } // has thick N2/CH4 atm 253 if id == NX_BODY_ENCELADUS { return NX_CELESTIAL_KIND_ICY_MOON } 254 if id == NX_BODY_RHEA { return NX_CELESTIAL_KIND_ICY_MOON } 255 if id == NX_BODY_IAPETUS { return NX_CELESTIAL_KIND_ICY_MOON } 256 if id == NX_BODY_TITANIA { return NX_CELESTIAL_KIND_ICY_MOON } 257 if id == NX_BODY_OBERON { return NX_CELESTIAL_KIND_ICY_MOON } 258 if id == NX_BODY_TRITON { return NX_CELESTIAL_KIND_ICY_MOON } 259 // Dwarf planets. 260 if id == NX_BODY_CERES { return NX_CELESTIAL_KIND_DWARF_PLANET } 261 if id == NX_BODY_PLUTO { return NX_CELESTIAL_KIND_DWARF_PLANET } 262 if id == NX_BODY_HAUMEA { return NX_CELESTIAL_KIND_DWARF_PLANET } 263 if id == NX_BODY_MAKEMAKE { return NX_CELESTIAL_KIND_DWARF_PLANET } 264 if id == NX_BODY_ERIS { return NX_CELESTIAL_KIND_DWARF_PLANET } 265 // Asteroids. 266 if id == NX_BODY_VESTA { return NX_CELESTIAL_KIND_ASTEROID } 267 if id == NX_BODY_PALLAS { return NX_CELESTIAL_KIND_ASTEROID } 268 if id == NX_BODY_HYGIEA { return NX_CELESTIAL_KIND_ASTEROID } 269 // Comets. 270 if id == NX_BODY_HALLEY { return NX_CELESTIAL_KIND_COMET } 271 if id == NX_BODY_HALE_BOPP { return NX_CELESTIAL_KIND_COMET } 272 if id == NX_BODY_BORISOV { return NX_CELESTIAL_KIND_COMET } 273 // TNOs / Kuiper-belt. 274 if id == NX_BODY_SEDNA { return NX_CELESTIAL_KIND_KUIPER_OBJECT } 275 if id == NX_BODY_QUAOAR { return NX_CELESTIAL_KIND_KUIPER_OBJECT } 276 if id == NX_BODY_ORCUS { return NX_CELESTIAL_KIND_KUIPER_OBJECT } 277 return 0 - 1 278} 279 280// ===== Accessor: parent body ======================================= 281// Returns the body that this body orbits. Sun returns -1 (no parent). 282// Planets / dwarf planets / asteroids / comets / TNOs orbit the Sun. 283// Moons orbit their parent planet. 284func nx_solar_body_parent_id(id: nx_int) -> nx_int { 285 if id == NX_BODY_SUN { return 0 - 1 } 286 // Moons. 287 if id == NX_BODY_LUNA { return NX_BODY_EARTH } 288 if id == NX_BODY_PHOBOS { return NX_BODY_MARS } 289 if id == NX_BODY_DEIMOS { return NX_BODY_MARS } 290 if id == NX_BODY_IO { return NX_BODY_JUPITER } 291 if id == NX_BODY_EUROPA { return NX_BODY_JUPITER } 292 if id == NX_BODY_GANYMEDE { return NX_BODY_JUPITER } 293 if id == NX_BODY_CALLISTO { return NX_BODY_JUPITER } 294 if id == NX_BODY_TITAN { return NX_BODY_SATURN } 295 if id == NX_BODY_ENCELADUS { return NX_BODY_SATURN } 296 if id == NX_BODY_RHEA { return NX_BODY_SATURN } 297 if id == NX_BODY_IAPETUS { return NX_BODY_SATURN } 298 if id == NX_BODY_TITANIA { return NX_BODY_URANUS } 299 if id == NX_BODY_OBERON { return NX_BODY_URANUS } 300 if id == NX_BODY_TRITON { return NX_BODY_NEPTUNE } 301 // Everything else orbits the Sun directly. 302 return NX_BODY_SUN 303} 304 305// ===== Accessor: radius (km, Q14) ================================= 306// Reference values from NASA JPL Horizons (mean equatorial radius 307// where applicable; volumetric mean for gas giants). 308func nx_solar_body_radius_km_q14(id: nx_int) -> nx_int { 309 let q: nx_int = NX_SOLAR_Q 310 if id == NX_BODY_SUN { return NX_MAGIC_695700 * q } 311 if id == NX_BODY_MERCURY { return NX_MAGIC_2440 * q } 312 if id == NX_BODY_VENUS { return NX_MAGIC_6052 * q } 313 if id == NX_BODY_EARTH { return NX_MAGIC_6371 * q } 314 if id == NX_BODY_MARS { return NX_MAGIC_3390 * q } 315 if id == NX_BODY_JUPITER { return NX_MAGIC_69911 * q } 316 if id == NX_BODY_SATURN { return NX_MAGIC_58232 * q } 317 if id == NX_BODY_URANUS { return NX_MAGIC_25362 * q } 318 if id == NX_BODY_NEPTUNE { return NX_MAGIC_24622 * q } 319 // Moons. 320 if id == NX_BODY_LUNA { return NX_MAGIC_1737 * q } 321 if id == NX_BODY_PHOBOS { return 11 * q } 322 if id == NX_BODY_DEIMOS { return 6 * q } 323 if id == NX_BODY_IO { return NX_MAGIC_1821 * q } 324 if id == NX_BODY_EUROPA { return NX_MAGIC_1561 * q } 325 if id == NX_BODY_GANYMEDE { return NX_MAGIC_2634 * q } 326 if id == NX_BODY_CALLISTO { return NX_MAGIC_2410 * q } 327 if id == NX_BODY_TITAN { return NX_MAGIC_2575 * q } 328 if id == NX_BODY_ENCELADUS { return 252 * q } 329 if id == NX_BODY_RHEA { return 764 * q } 330 if id == NX_BODY_IAPETUS { return 734 * q } 331 if id == NX_BODY_TITANIA { return 789 * q } 332 if id == NX_BODY_OBERON { return 761 * q } 333 if id == NX_BODY_TRITON { return NX_MAGIC_1353 * q } 334 // Dwarfs. 335 if id == NX_BODY_CERES { return 473 * q } 336 if id == NX_BODY_PLUTO { return NX_MAGIC_1188 * q } 337 if id == NX_BODY_HAUMEA { return 816 * q } // mean radius (elongated) 338 if id == NX_BODY_MAKEMAKE { return 715 * q } 339 if id == NX_BODY_ERIS { return NX_MAGIC_1163 * q } 340 // Asteroids. 341 if id == NX_BODY_VESTA { return 263 * q } 342 if id == NX_BODY_PALLAS { return 256 * q } 343 if id == NX_BODY_HYGIEA { return 217 * q } 344 // Comet nuclei (Halley ~ 5.5km mean; Hale-Bopp ~ 30km; Borisov ~ 0.5km). 345 if id == NX_BODY_HALLEY { return 5 * q } 346 if id == NX_BODY_HALE_BOPP { return 30 * q } 347 if id == NX_BODY_BORISOV { return 1 * q } // estimated 348 // TNOs. 349 if id == NX_BODY_SEDNA { return 500 * q } 350 if id == NX_BODY_QUAOAR { return 555 * q } 351 if id == NX_BODY_ORCUS { return 455 * q } 352 return 0 353} 354 355// ===== Accessor: mass (1e20 kg, Q14) =============================== 356// Mass in units of 1e20 kg so the value range fits cleanly in i64 357// after Q14 scaling. Real masses recoverable as 358// real_mass_kg = (value / Q14) * 1e20. 359// 360// NASA JPL reference values: 361// Sun 1.989e30 kg -> 1.989e10 e20kg 362// Mercury 3.301e23 kg -> 3.301e3 e20kg 363// Venus 4.8675e24 kg-> 48675 e20kg 364// Earth 5.972e24 kg -> 59720 e20kg 365// Mars 6.4171e23 kg-> 6417.1 e20kg (round to 6417) 366// Jupiter 1.898e27 kg -> 1.898e7 e20kg 367// Saturn 5.683e26 kg -> 5.683e6 e20kg 368// Uranus 8.681e25 kg -> 868100 e20kg 369// Neptune 1.024e26 kg -> 1.024e6 e20kg 370func nx_solar_body_mass_e20kg_q14(id: nx_int) -> nx_int { 371 let q: nx_int = NX_SOLAR_Q 372 if id == NX_BODY_SUN { return NX_MAGIC_19890000000 * q } // 1.989e10 373 if id == NX_BODY_MERCURY { return NX_MAGIC_3301 * q } 374 if id == NX_BODY_VENUS { return NX_MAGIC_48675 * q } 375 if id == NX_BODY_EARTH { return NX_MAGIC_59720 * q } 376 if id == NX_BODY_MARS { return NX_MAGIC_6417 * q } 377 if id == NX_BODY_JUPITER { return NX_MAGIC_18981300 * q } // 1.898e7 378 if id == NX_BODY_SATURN { return NX_MAGIC_5683300 * q } 379 if id == NX_BODY_URANUS { return NX_MAGIC_868100 * q } 380 if id == NX_BODY_NEPTUNE { return NX_MAGIC_1024100 * q } 381 // Major moons (in 1e20 kg). 382 if id == NX_BODY_LUNA { return 735 * q } // 7.35e22 / 1e20 = 735 383 if id == NX_BODY_IO { return 893 * q } 384 if id == NX_BODY_EUROPA { return 480 * q } 385 if id == NX_BODY_GANYMEDE { return NX_MAGIC_1482 * q } 386 if id == NX_BODY_CALLISTO { return NX_MAGIC_1076 * q } 387 if id == NX_BODY_TITAN { return NX_MAGIC_1345 * q } 388 if id == NX_BODY_TRITON { return 214 * q } 389 if id == NX_BODY_RHEA { return 23 * q } // 2.3e21 / 1e20 = 23 390 if id == NX_BODY_TITANIA { return 34 * q } // 3.4e21 391 if id == NX_BODY_OBERON { return 30 * q } 392 if id == NX_BODY_IAPETUS { return 18 * q } 393 if id == NX_BODY_ENCELADUS { return 1 * q } // 1.08e20 ~ 1 394 // Phobos/Deimos < 1 in these units; underflow accepted. 395 // Dwarfs (in 1e20 kg). 396 if id == NX_BODY_CERES { return 9 * q } // 9.4e20 -> 9.4 397 if id == NX_BODY_PLUTO { return 130 * q } // 1.3e22 398 if id == NX_BODY_HAUMEA { return 40 * q } // 4.0e21 399 if id == NX_BODY_MAKEMAKE { return 31 * q } 400 if id == NX_BODY_ERIS { return 166 * q } // 1.66e22 401 // Asteroids, comets, TNOs: << 1 in these units; underflow accepted. 402 return 0 403} 404 405// ===== Accessor: semi-major axis (AU, Q14) ========================= 406// 1 AU = 149,597,870.7 km. Sun's own value is 0 (it's the focus). 407// Reference orbital elements from JPL Horizons. 408func nx_solar_body_semi_major_axis_au_q14(id: nx_int) -> nx_int { 409 let q: nx_int = NX_SOLAR_Q 410 if id == NX_BODY_SUN { return 0 } 411 // Decimal AU * Q14 -- compute literally for legibility. 412 if id == NX_BODY_MERCURY { return (387 * q) / 1000 } // 0.387 413 if id == NX_BODY_VENUS { return (723 * q) / 1000 } // 0.723 414 if id == NX_BODY_EARTH { return q } // 1.0 415 if id == NX_BODY_MARS { return (NX_MAGIC_1524 * q) / 1000 } // 1.524 416 if id == NX_BODY_JUPITER { return (NX_MAGIC_5203 * q) / 1000 } // 5.203 417 if id == NX_BODY_SATURN { return (NX_MAGIC_9539 * q) / 1000 } // 9.539 418 if id == NX_BODY_URANUS { return (NX_MAGIC_19180 * q) / 1000 } // 19.18 419 if id == NX_BODY_NEPTUNE { return (NX_MAGIC_30070 * q) / 1000 } // 30.07 420 // Moons -- semi-major axis around PARENT in fractions of AU. 421 // (For game-scale use, callers convert by also reading parent.) 422 if id == NX_BODY_LUNA { return (3 * q) / 1000 } // 0.00257 ~ 0.003 423 if id == NX_BODY_PHOBOS { return (1 * q) / NX_MAGIC_16000 } // ~0 424 if id == NX_BODY_DEIMOS { return (1 * q) / NX_MAGIC_6400 } 425 if id == NX_BODY_IO { return (3 * q) / 1000 } // 0.0028 AU 426 if id == NX_BODY_EUROPA { return (5 * q) / 1000 } 427 if id == NX_BODY_GANYMEDE { return (7 * q) / 1000 } 428 if id == NX_BODY_CALLISTO { return (13 * q) / 1000 } 429 if id == NX_BODY_TITAN { return (8 * q) / 1000 } 430 if id == NX_BODY_ENCELADUS { return (2 * q) / 1000 } 431 if id == NX_BODY_RHEA { return (4 * q) / 1000 } 432 if id == NX_BODY_IAPETUS { return (24 * q) / 1000 } 433 if id == NX_BODY_TITANIA { return (3 * q) / 1000 } 434 if id == NX_BODY_OBERON { return (4 * q) / 1000 } 435 if id == NX_BODY_TRITON { return (2 * q) / 1000 } 436 // Dwarf planets (AU). 437 if id == NX_BODY_CERES { return (NX_MAGIC_2766 * q) / 1000 } // 2.766 438 if id == NX_BODY_PLUTO { return (NX_MAGIC_3948 * q) / 100 } // 39.48 439 if id == NX_BODY_HAUMEA { return (NX_MAGIC_4317 * q) / 100 } 440 if id == NX_BODY_MAKEMAKE { return (NX_MAGIC_4571 * q) / 100 } 441 if id == NX_BODY_ERIS { return (NX_MAGIC_6778 * q) / 100 } // 67.78 442 // Asteroids (AU). 443 if id == NX_BODY_VESTA { return (NX_MAGIC_2362 * q) / 1000 } 444 if id == NX_BODY_PALLAS { return (NX_MAGIC_2773 * q) / 1000 } 445 if id == NX_BODY_HYGIEA { return (NX_MAGIC_3140 * q) / 1000 } 446 // Comets (semi-major axis varies wildly). 447 if id == NX_BODY_HALLEY { return (NX_MAGIC_1782 * q) / 100 } // 17.82 AU 448 if id == NX_BODY_HALE_BOPP { return (NX_MAGIC_1800 * q) / 10 } // ~180 AU 449 if id == NX_BODY_BORISOV { return 0 } // hyperbolic / interstellar 450 // TNOs. 451 if id == NX_BODY_SEDNA { return (506 * q) } // 506 AU 452 if id == NX_BODY_QUAOAR { return (NX_MAGIC_4348 * q) / 100 } // 43.48 453 if id == NX_BODY_ORCUS { return (NX_MAGIC_3927 * q) / 100 } // 39.27 454 return 0 455} 456 457// ===== Accessor: orbital period (Earth-days, Q14) ================== 458// Sidereal period. Sun's own period is 0 (no parent orbit). 459func nx_solar_body_orbital_period_days_q14(id: nx_int) -> nx_int { 460 let q: nx_int = NX_SOLAR_Q 461 if id == NX_BODY_SUN { return 0 } 462 if id == NX_BODY_MERCURY { return 88 * q } 463 if id == NX_BODY_VENUS { return 225 * q } 464 if id == NX_BODY_EARTH { return 365 * q } 465 if id == NX_BODY_MARS { return 687 * q } 466 if id == NX_BODY_JUPITER { return NX_MAGIC_4333 * q } 467 if id == NX_BODY_SATURN { return NX_MAGIC_10759 * q } 468 if id == NX_BODY_URANUS { return NX_MAGIC_30688 * q } 469 if id == NX_BODY_NEPTUNE { return NX_MAGIC_60182 * q } 470 // Moons. 471 if id == NX_BODY_LUNA { return 27 * q } 472 if id == NX_BODY_PHOBOS { return (32 * q) / 100 } // 0.32 day 473 if id == NX_BODY_DEIMOS { return (126 * q) / 100 } 474 if id == NX_BODY_IO { return (177 * q) / 100 } // 1.77 475 if id == NX_BODY_EUROPA { return (355 * q) / 100 } 476 if id == NX_BODY_GANYMEDE { return (716 * q) / 100 } 477 if id == NX_BODY_CALLISTO { return (NX_MAGIC_1669 * q) / 100 } 478 if id == NX_BODY_TITAN { return (NX_MAGIC_1595 * q) / 100 } 479 if id == NX_BODY_ENCELADUS { return (137 * q) / 100 } 480 if id == NX_BODY_RHEA { return (452 * q) / 100 } 481 if id == NX_BODY_IAPETUS { return 79 * q } 482 if id == NX_BODY_TITANIA { return (870 * q) / 100 } 483 if id == NX_BODY_OBERON { return (NX_MAGIC_1346 * q) / 100 } 484 if id == NX_BODY_TRITON { return (588 * q) / 100 } // retrograde but mag here 485 // Dwarfs. 486 if id == NX_BODY_CERES { return NX_MAGIC_1681 * q } // ~4.6 yr -> NX_MAGIC_1681 days 487 if id == NX_BODY_PLUTO { return NX_MAGIC_90560 * q } // 248 yr 488 if id == NX_BODY_HAUMEA { return NX_MAGIC_103774 * q } 489 if id == NX_BODY_MAKEMAKE { return NX_MAGIC_112897 * q } 490 if id == NX_BODY_ERIS { return NX_MAGIC_203830 * q } // 558 yr 491 // Asteroids. 492 if id == NX_BODY_VESTA { return NX_MAGIC_1325 * q } 493 if id == NX_BODY_PALLAS { return NX_MAGIC_1686 * q } 494 if id == NX_BODY_HYGIEA { return NX_MAGIC_2034 * q } 495 // Comets (Halley 76 yr; Hale-Bopp 2533 yr; Borisov hyperbolic). 496 if id == NX_BODY_HALLEY { return NX_MAGIC_27759 * q } 497 if id == NX_BODY_HALE_BOPP { return NX_MAGIC_924795 * q } 498 if id == NX_BODY_BORISOV { return 0 } // hyperbolic 499 // TNOs (very long). 500 if id == NX_BODY_SEDNA { return NX_MAGIC_4150000 * q } // ~NX_MAGIC_11400 yr 501 if id == NX_BODY_QUAOAR { return NX_MAGIC_104744 * q } 502 if id == NX_BODY_ORCUS { return NX_MAGIC_89530 * q } 503 return 0 504} 505 506// ===== Accessor: rotation period (hours, Q14) ====================== 507// Sidereal rotation. Negative values indicate retrograde rotation 508// (Venus + Uranus rotate retrograde). 509func nx_solar_body_rotation_period_hours_q14(id: nx_int) -> nx_int { 510 let q: nx_int = NX_SOLAR_Q 511 if id == NX_BODY_SUN { return 609 * q } // ~25.4 days equatorial 512 if id == NX_BODY_MERCURY { return NX_MAGIC_1408 * q } // 58.6 days 513 if id == NX_BODY_VENUS { return 0 - NX_MAGIC_5832 * q } // retrograde 243 days 514 if id == NX_BODY_EARTH { return 24 * q } 515 if id == NX_BODY_MARS { return 25 * q } // 24.6 hours 516 if id == NX_BODY_JUPITER { return 10 * q } // 9.93 hours 517 if id == NX_BODY_SATURN { return 11 * q } // 10.7 hours 518 if id == NX_BODY_URANUS { return 0 - 17 * q } // retrograde + axial tilt 519 if id == NX_BODY_NEPTUNE { return 16 * q } // 16.1 hours 520 return 0 521} 522 523// ===== Accessor: atmosphere flag =================================== 524// 1 if the body has a measurable atmosphere, 0 if effectively airless 525// (vacuum) or N/A (Sun). Sun is special-cased: it has a corona + 526// solar wind but no traditional "atmosphere" for landing purposes. 527func nx_solar_body_has_atmosphere(id: nx_int) -> nx_int { 528 if id == NX_BODY_SUN { return 1 } // photosphere/chromosphere/corona 529 if id == NX_BODY_MERCURY { return 0 } // exosphere only; effectively none 530 if id == NX_BODY_VENUS { return 1 } // 92 atm CO2 531 if id == NX_BODY_EARTH { return 1 } // 1 atm N2/O2 532 if id == NX_BODY_MARS { return 1 } // 0.006 atm CO2 -- thin but present 533 if id == NX_BODY_JUPITER { return 1 } // gas giant -- it IS atmosphere 534 if id == NX_BODY_SATURN { return 1 } 535 if id == NX_BODY_URANUS { return 1 } // ice giant -- atmosphere into mantle 536 if id == NX_BODY_NEPTUNE { return 1 } 537 // Titan: thick N2/CH4 atmosphere (only major moon with one). 538 if id == NX_BODY_TITAN { return 1 } 539 // Triton: thin N2 atm (~1.4 Pa). 540 if id == NX_BODY_TRITON { return 1 } 541 // Pluto: thin N2 atm during perihelion. 542 if id == NX_BODY_PLUTO { return 1 } 543 // Comets: gas coma develops near perihelion. 544 if id == NX_BODY_HALLEY { return 1 } 545 if id == NX_BODY_HALE_BOPP { return 1 } 546 if id == NX_BODY_BORISOV { return 1 } 547 return 0 548} 549 550// ===== Accessor: rings flag ======================================== 551// 1 if the body has a ring system visible from Earth or by close 552// flyby. Jupiter's rings are faint but real (Voyager). 553func nx_solar_body_has_rings(id: nx_int) -> nx_int { 554 if id == NX_BODY_SUN { return 0 } 555 if id == NX_BODY_MERCURY { return 0 } 556 if id == NX_BODY_VENUS { return 0 } 557 if id == NX_BODY_EARTH { return 0 } 558 if id == NX_BODY_MARS { return 0 } 559 if id == NX_BODY_JUPITER { return 1 } // faint (Voyager-discovered) 560 if id == NX_BODY_SATURN { return 1 } // canonical rings 561 if id == NX_BODY_URANUS { return 1 } // narrow + dark 562 if id == NX_BODY_NEPTUNE { return 1 } // partial arcs 563 return 0 564} 565 566// ===== Self-test ==================================================== 567func main() -> i64 { 568 let q: nx_int = NX_SOLAR_Q 569 570 // T1: body-count + validity predicates. Catalog expanded to 37 571 // bodies (Sun + 8 planets + 13 moons + 5 dwarfs + 3 asteroids + 572 // 3 comets + 3 TNOs). 573 if NX_SOLAR_BODY_COUNT != 37 { return nx_hal_exit(1) } 574 if nx_solar_body_id_is_valid(0) != 1 { return nx_hal_exit(2) } 575 if nx_solar_body_id_is_valid(36) != 1 { return nx_hal_exit(3) } 576 if nx_solar_body_id_is_valid(0 - 1) != 0 { return nx_hal_exit(4) } 577 if nx_solar_body_id_is_valid(37) != 0 { return nx_hal_exit(5) } 578 if nx_solar_body_id_is_valid(99) != 0 { return nx_hal_exit(6) } 579 if nx_celestial_kind_is_valid(NX_CELESTIAL_KIND_STAR) != 1 { return nx_hal_exit(7) } 580 if nx_celestial_kind_is_valid(NX_CELESTIAL_KIND_COMET) != 1 { return nx_hal_exit(8) } 581 if nx_celestial_kind_is_valid(99) != 0 { return nx_hal_exit(9) } 582 583 // T2: body kinds. 584 if nx_solar_body_kind(NX_BODY_SUN) != NX_CELESTIAL_KIND_STAR { return nx_hal_exit(10) } 585 if nx_solar_body_kind(NX_BODY_MERCURY) != NX_CELESTIAL_KIND_TERRESTRIAL_PLANET { return nx_hal_exit(11) } 586 if nx_solar_body_kind(NX_BODY_VENUS) != NX_CELESTIAL_KIND_TERRESTRIAL_PLANET { return nx_hal_exit(12) } 587 if nx_solar_body_kind(NX_BODY_EARTH) != NX_CELESTIAL_KIND_TERRESTRIAL_PLANET { return nx_hal_exit(13) } 588 if nx_solar_body_kind(NX_BODY_MARS) != NX_CELESTIAL_KIND_TERRESTRIAL_PLANET { return nx_hal_exit(14) } 589 if nx_solar_body_kind(NX_BODY_JUPITER) != NX_CELESTIAL_KIND_GAS_GIANT { return nx_hal_exit(15) } 590 if nx_solar_body_kind(NX_BODY_SATURN) != NX_CELESTIAL_KIND_GAS_GIANT { return nx_hal_exit(16) } 591 if nx_solar_body_kind(NX_BODY_URANUS) != NX_CELESTIAL_KIND_ICE_GIANT { return nx_hal_exit(17) } 592 if nx_solar_body_kind(NX_BODY_NEPTUNE) != NX_CELESTIAL_KIND_ICE_GIANT { return nx_hal_exit(18) } 593 594 // T3: radius sanity. Sun is largest by far; Earth ~ 6371 km. 595 let r_earth: nx_int = nx_solar_body_radius_km_q14(NX_BODY_EARTH) 596 if r_earth != NX_MAGIC_6371 * q { return nx_hal_exit(20) } 597 // Sun radius > 100 * Earth radius. 598 if nx_solar_body_radius_km_q14(NX_BODY_SUN) <= 100 * r_earth { 599 return nx_hal_exit(21) 600 } 601 // Jupiter > Earth. 602 if nx_solar_body_radius_km_q14(NX_BODY_JUPITER) <= r_earth { 603 return nx_hal_exit(22) 604 } 605 // Mars < Earth. 606 if nx_solar_body_radius_km_q14(NX_BODY_MARS) >= r_earth { 607 return nx_hal_exit(23) 608 } 609 610 // T4: semi-major axis ordering: Mercury < Venus < Earth < Mars < 611 // Jupiter < Saturn < Uranus < Neptune. 612 let am: nx_int = nx_solar_body_semi_major_axis_au_q14(NX_BODY_MERCURY) 613 let av: nx_int = nx_solar_body_semi_major_axis_au_q14(NX_BODY_VENUS) 614 let ae: nx_int = nx_solar_body_semi_major_axis_au_q14(NX_BODY_EARTH) 615 let aj: nx_int = nx_solar_body_semi_major_axis_au_q14(NX_BODY_JUPITER) 616 let an: nx_int = nx_solar_body_semi_major_axis_au_q14(NX_BODY_NEPTUNE) 617 if am >= av { return nx_hal_exit(30) } 618 if av >= ae { return nx_hal_exit(31) } 619 if ae != q { return nx_hal_exit(32) } // Earth = 1.0 AU exact 620 if aj <= ae { return nx_hal_exit(33) } 621 if an <= aj { return nx_hal_exit(34) } 622 623 // T5: orbital period: longer planets take longer. Kepler 3 hint. 624 let pm: nx_int = nx_solar_body_orbital_period_days_q14(NX_BODY_MERCURY) 625 let pe: nx_int = nx_solar_body_orbital_period_days_q14(NX_BODY_EARTH) 626 let pn: nx_int = nx_solar_body_orbital_period_days_q14(NX_BODY_NEPTUNE) 627 if pm >= pe { return nx_hal_exit(40) } 628 if pe >= pn { return nx_hal_exit(41) } 629 if pe != 365 * q { return nx_hal_exit(42) } // Earth ~ 365 days 630 631 // T6: rotation period for Venus is retrograde (negative). 632 if nx_solar_body_rotation_period_hours_q14(NX_BODY_VENUS) >= 0 { 633 return nx_hal_exit(50) 634 } 635 // Uranus retrograde. 636 if nx_solar_body_rotation_period_hours_q14(NX_BODY_URANUS) >= 0 { 637 return nx_hal_exit(51) 638 } 639 // Earth ~ 24 hours. 640 if nx_solar_body_rotation_period_hours_q14(NX_BODY_EARTH) != 24 * q { 641 return nx_hal_exit(52) 642 } 643 644 // T7: atmosphere flags. 645 // Mercury -- effectively none. 646 if nx_solar_body_has_atmosphere(NX_BODY_MERCURY) != 0 { return nx_hal_exit(60) } 647 // Venus / Earth / Mars / gas giants -- yes. 648 if nx_solar_body_has_atmosphere(NX_BODY_VENUS) != 1 { return nx_hal_exit(61) } 649 if nx_solar_body_has_atmosphere(NX_BODY_EARTH) != 1 { return nx_hal_exit(62) } 650 if nx_solar_body_has_atmosphere(NX_BODY_MARS) != 1 { return nx_hal_exit(63) } 651 if nx_solar_body_has_atmosphere(NX_BODY_JUPITER) != 1 { return nx_hal_exit(64) } 652 if nx_solar_body_has_atmosphere(NX_BODY_NEPTUNE) != 1 { return nx_hal_exit(65) } 653 654 // T8: ring flags -- only the 4 outer planets have rings. 655 if nx_solar_body_has_rings(NX_BODY_MERCURY) != 0 { return nx_hal_exit(70) } 656 if nx_solar_body_has_rings(NX_BODY_EARTH) != 0 { return nx_hal_exit(71) } 657 if nx_solar_body_has_rings(NX_BODY_MARS) != 0 { return nx_hal_exit(72) } 658 if nx_solar_body_has_rings(NX_BODY_JUPITER) != 1 { return nx_hal_exit(73) } 659 if nx_solar_body_has_rings(NX_BODY_SATURN) != 1 { return nx_hal_exit(74) } 660 if nx_solar_body_has_rings(NX_BODY_URANUS) != 1 { return nx_hal_exit(75) } 661 if nx_solar_body_has_rings(NX_BODY_NEPTUNE) != 1 { return nx_hal_exit(76) } 662 663 // T9: mass ordering. Sun massively biggest. Jupiter > Saturn > 664 // Neptune > Uranus > Earth > Venus > Mars > Mercury. 665 let mass_sun: nx_int = nx_solar_body_mass_e20kg_q14(NX_BODY_SUN) 666 let mass_jup: nx_int = nx_solar_body_mass_e20kg_q14(NX_BODY_JUPITER) 667 let mass_earth: nx_int = nx_solar_body_mass_e20kg_q14(NX_BODY_EARTH) 668 let mass_merc: nx_int = nx_solar_body_mass_e20kg_q14(NX_BODY_MERCURY) 669 if mass_sun <= mass_jup { return nx_hal_exit(80) } 670 if mass_jup <= mass_earth { return nx_hal_exit(81) } 671 if mass_earth <= mass_merc { return nx_hal_exit(82) } 672 // Sun mass at least 100,000 * Earth's (real ratio ~333,000). 673 if mass_sun < NX_MAGIC_100000 * mass_earth { 674 return nx_hal_exit(83) 675 } 676 677 // T10: Moon kind + parent. 678 if nx_solar_body_kind(NX_BODY_LUNA) != NX_CELESTIAL_KIND_ROCKY_MOON { return nx_hal_exit(90) } 679 if nx_solar_body_kind(NX_BODY_IO) != NX_CELESTIAL_KIND_ROCKY_MOON { return nx_hal_exit(91) } 680 if nx_solar_body_kind(NX_BODY_EUROPA) != NX_CELESTIAL_KIND_ICY_MOON { return nx_hal_exit(92) } 681 if nx_solar_body_kind(NX_BODY_TRITON) != NX_CELESTIAL_KIND_ICY_MOON { return nx_hal_exit(93) } 682 if nx_solar_body_parent_id(NX_BODY_LUNA) != NX_BODY_EARTH { return nx_hal_exit(94) } 683 if nx_solar_body_parent_id(NX_BODY_PHOBOS) != NX_BODY_MARS { return nx_hal_exit(95) } 684 if nx_solar_body_parent_id(NX_BODY_GANYMEDE) != NX_BODY_JUPITER { return nx_hal_exit(96) } 685 if nx_solar_body_parent_id(NX_BODY_TITAN) != NX_BODY_SATURN { return nx_hal_exit(97) } 686 if nx_solar_body_parent_id(NX_BODY_TITANIA) != NX_BODY_URANUS { return nx_hal_exit(98) } 687 if nx_solar_body_parent_id(NX_BODY_TRITON) != NX_BODY_NEPTUNE { return nx_hal_exit(99) } 688 if nx_solar_body_parent_id(NX_BODY_SUN) != (0 - 1) { return nx_hal_exit(100) } 689 if nx_solar_body_parent_id(NX_BODY_CERES) != NX_BODY_SUN { return nx_hal_exit(101) } 690 if nx_solar_body_parent_id(NX_BODY_PLUTO) != NX_BODY_SUN { return nx_hal_exit(102) } 691 if nx_solar_body_parent_id(NX_BODY_HALLEY) != NX_BODY_SUN { return nx_hal_exit(103) } 692 if nx_solar_body_parent_id(NX_BODY_SEDNA) != NX_BODY_SUN { return nx_hal_exit(104) } 693 694 // T11: Dwarf + asteroid + comet + TNO kinds. 695 if nx_solar_body_kind(NX_BODY_CERES) != NX_CELESTIAL_KIND_DWARF_PLANET { return nx_hal_exit(110) } 696 if nx_solar_body_kind(NX_BODY_PLUTO) != NX_CELESTIAL_KIND_DWARF_PLANET { return nx_hal_exit(111) } 697 if nx_solar_body_kind(NX_BODY_ERIS) != NX_CELESTIAL_KIND_DWARF_PLANET { return nx_hal_exit(112) } 698 if nx_solar_body_kind(NX_BODY_VESTA) != NX_CELESTIAL_KIND_ASTEROID { return nx_hal_exit(113) } 699 if nx_solar_body_kind(NX_BODY_HALLEY) != NX_CELESTIAL_KIND_COMET { return nx_hal_exit(114) } 700 if nx_solar_body_kind(NX_BODY_BORISOV) != NX_CELESTIAL_KIND_COMET { return nx_hal_exit(115) } 701 if nx_solar_body_kind(NX_BODY_SEDNA) != NX_CELESTIAL_KIND_KUIPER_OBJECT { return nx_hal_exit(116) } 702 703 // T12: Radius sanity for moons + small bodies. 704 if nx_solar_body_radius_km_q14(NX_BODY_GANYMEDE) <= nx_solar_body_radius_km_q14(NX_BODY_LUNA) { 705 return nx_hal_exit(120) 706 } 707 if nx_solar_body_radius_km_q14(NX_BODY_PHOBOS) >= nx_solar_body_radius_km_q14(NX_BODY_LUNA) { 708 return nx_hal_exit(121) 709 } 710 if nx_solar_body_radius_km_q14(NX_BODY_PLUTO) >= nx_solar_body_radius_km_q14(NX_BODY_LUNA) { 711 return nx_hal_exit(122) 712 } 713 714 // T13: Titan has atmosphere; other major moons don't. 715 if nx_solar_body_has_atmosphere(NX_BODY_TITAN) != 1 { return nx_hal_exit(130) } 716 if nx_solar_body_has_atmosphere(NX_BODY_LUNA) != 0 { return nx_hal_exit(131) } 717 if nx_solar_body_has_atmosphere(NX_BODY_EUROPA) != 0 { return nx_hal_exit(132) } 718 719 // T14: Pluto orbits further than Neptune; Sedna further than Pluto. 720 if nx_solar_body_semi_major_axis_au_q14(NX_BODY_PLUTO) <= nx_solar_body_semi_major_axis_au_q14(NX_BODY_NEPTUNE) { 721 return nx_hal_exit(140) 722 } 723 if nx_solar_body_semi_major_axis_au_q14(NX_BODY_SEDNA) <= nx_solar_body_semi_major_axis_au_q14(NX_BODY_PLUTO) { 724 return nx_hal_exit(141) 725 } 726 727 return 0 728}