T1 scripts/birth_sky.py: real alt/az of the seven visible grahas (swe.calc_ut + swe.azalt, geometric altitude, azimuth converted from swe's south origin to north-east) and Yale BSC5 stars V<=4.0 (IAU 1976 precession); phase day / twilight_morning / twilight_evening / night. Thin POST /api/birth_sky registration in do_POST (no new handler method, no __new__). T2 GET /api/birth-sky?subject=: birth truth read server side through loadSubjectBirth + prepareChartViewProfile; zod contract; no billing, no writes, no birth data in the response. T3/T4 lib/birth-sky: sentence table, pure Canvas 2D drawBirthSky (night / paper palettes from DESIGN tokens, east on the left, serif headline with 1.5 s font wait), PNG export via navigator.share or Blob + a.download. Chart page prefetches and paints the cover once the chart is ok; the header button appears only when the PNG is ready. Dialog is a lazy chunk. T7 DESIGN §17, VOICE, CONTEXT glossary, CHANGELOG, real-device checklist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199rbQDTsUbCVw84wc8BTFe
89 lines
3.5 KiB
Python
89 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Rebuild scripts/data/bright_stars.json from the Yale Bright Star Catalogue.
|
|
|
|
Source: Hoffleit & Warren, *The Bright Star Catalogue, 5th Revised Ed.*
|
|
(NASA Astronomical Data Center, 1991), distributed by CDS as VizieR V/50.
|
|
A US-government (NASA ADC) data product, redistributed by CDS without
|
|
restriction; treated as public domain. No star-chart software data is used.
|
|
|
|
Usage (the catalogue itself is not committed; the script checks its sha256):
|
|
|
|
curl -o /tmp/bsc5_catalog.gz https://cdsarc.cds.unistra.fr/ftp/V/50/catalog.gz
|
|
python3 scripts/build_bright_stars.py /tmp/bsc5_catalog.gz
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import gzip
|
|
import hashlib
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
SOURCE_URL = "https://cdsarc.cds.unistra.fr/ftp/V/50/catalog.gz"
|
|
SOURCE_SHA256 = "3dc44b1e90be8fbe5bcc7656032560f51275f985c7e3f783c9028e1838ec7bed"
|
|
MAX_MAGNITUDE = 4.0
|
|
OUT = Path(__file__).resolve().parent / "data" / "bright_stars.json"
|
|
|
|
# Asterism polylines by HR number. Each inner list is one continuous stroke.
|
|
LINES = [
|
|
{"id": "big_dipper", "strokes": [[5191, 5054, 4905, 4660, 4554, 4295, 4301, 4660]]},
|
|
{"id": "cassiopeia", "strokes": [[21, 168, 264, 403, 542]]},
|
|
{
|
|
"id": "orion",
|
|
"strokes": [
|
|
[2061, 1879, 1790],
|
|
[2061, 1948, 1903, 1852, 1790],
|
|
[1948, 2004],
|
|
[1852, 1713],
|
|
],
|
|
},
|
|
]
|
|
|
|
|
|
def _parse(raw: bytes) -> list[dict]:
|
|
stars: list[dict] = []
|
|
for line in raw.decode("latin-1").splitlines():
|
|
if len(line) < 107 or not line[75:77].strip() or not line[102:107].strip():
|
|
continue # HR entries without a J2000 position or V magnitude (novae, removed objects)
|
|
vmag = float(line[102:107])
|
|
if vmag > MAX_MAGNITUDE:
|
|
continue
|
|
hr = int(line[0:4])
|
|
ra = (int(line[75:77]) + int(line[77:79]) / 60 + float(line[79:83]) / 3600) * 15
|
|
dec = int(line[84:86]) + int(line[86:88]) / 60 + int(line[88:90]) / 3600
|
|
if line[83] == "-":
|
|
dec = -dec
|
|
stars.append({"hr": hr, "ra": round(ra, 5), "dec": round(dec, 5), "vmag": vmag})
|
|
return stars
|
|
|
|
|
|
def main(path: str) -> None:
|
|
packed = Path(path).read_bytes()
|
|
digest = hashlib.sha256(packed).hexdigest()
|
|
if digest != SOURCE_SHA256:
|
|
raise SystemExit(f"sha256 mismatch: {digest}")
|
|
stars = _parse(gzip.decompress(packed))
|
|
known = {star["hr"] for star in stars}
|
|
for line in LINES:
|
|
for stroke in line["strokes"]:
|
|
missing = [hr for hr in stroke if hr not in known]
|
|
if missing:
|
|
raise SystemExit(f"{line['id']} needs HR {missing} brighter than {MAX_MAGNITUDE}")
|
|
payload = {
|
|
"source": "Yale Bright Star Catalogue, 5th Revised Ed. (Hoffleit & Warren 1991), VizieR V/50",
|
|
"source_url": SOURCE_URL,
|
|
"source_sha256": SOURCE_SHA256,
|
|
"license": "Public domain (NASA Astronomical Data Center product, redistributed by CDS without restriction)",
|
|
"selection": f"V magnitude <= {MAX_MAGNITUDE}; J2000.0 equinox and epoch; proper motion not applied",
|
|
"fields": ["hr", "ra_deg_j2000", "dec_deg_j2000", "vmag"],
|
|
"generator": "scripts/build_bright_stars.py",
|
|
"lines": LINES,
|
|
"stars": [[s["hr"], s["ra"], s["dec"], s["vmag"]] for s in stars],
|
|
}
|
|
OUT.write_text(json.dumps(payload, ensure_ascii=False, separators=(",", ":")) + "\n", encoding="utf-8")
|
|
print(f"{len(stars)} stars -> {OUT}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.argv[1] if len(sys.argv) > 1 else "/tmp/bsc5_catalog.gz")
|