Daily close-approach brief for near-Earth objects (with an optional AI summary)
Tested by BSVPull NASA/JPL close-approach data for the coming week and write a clean Markdown table you can read in a minute or hand to an AI agent.
- Input
- Start date (default today, UTC), days ahead (default 7), distance limit in au (default 0.05). No API key.開始日(既定は今日・UTC)、何日先まで(既定7日)、距離の上限(au、既定0.05)。APIキーは不要です。
- Output
- neo_brief_<date>.md (closest first: distance in km and lunar distances, speed, absolute magnitude H) and neo_brief_<date>.json.neo_brief_<日付>.md(近い順。距離はkmと月までの距離の何倍か、速度、絶対等級H)と neo_brief_<日付>.json。
- Prerequisites
- Python 3.10 or later; standard library only.Python 3.10以上。標準ライブラリだけで動きます。
- Steps
-
- Save the code below as neo_brief.py.
- Run
python neo_brief.py(orpython neo_brief.py 2026-10-10 14for another window). - Open the .md file; check one object on JPL's Small-Body Database before quoting it anywhere.
- Optional AI step: pass the Markdown to a local model, for example
ollama run llama3.2 "Summarise this table in three plain sentences. Do not add facts." < neo_brief_<date>.md. - Schedule it (cron or Task Scheduler) to get a fresh brief every morning.
- 下のコードを neo_brief.py として保存します。
python neo_brief.pyを実行します(期間を変えるならpython neo_brief.py 2026-10-10 14)。- .md ファイルを開きます。どこかで引用する前に、少なくとも1天体はJPLのSmall-Body Databaseで確認してください。
- AIで要約する場合(任意):Markdownをローカルモデルに渡します。例:
ollama run llama3.2 "この表を平易な3文で要約してください。事実を付け足さないでください。" < neo_brief_<日付>.md - cronやタスクスケジューラに登録すると、毎朝新しいブリーフが届きます。
- Expected result
- A table of every approach inside the distance limit, closest first. Size is not published for most small objects, so the brief says so instead of guessing.距離の上限内に入る接近がすべて、近い順に並んだ表になります。小さな天体の多くは大きさが公表されていないため、推測で埋めずに「未公表」と書きます。
- Next step
- Add a check against JPL's Sentry impact-risk table for any object in your brief, or post the brief to your own channel with an AI agent from BSV's toolkit list.ブリーフに載った天体についてJPLのSentry(衝突リスク表)も確認する処理を足すか、BSVのツールキット一覧にあるAIエージェントで、ご自身のチャンネルに投稿する仕組みにしてみてください。 Open
The optional AI-summary step was not run by BSV.
任意のAI要約の手順は、BSVでは実行していません。
Code (BSV original, MIT licence)
Download .py · neo_brief.py
#!/usr/bin/env python3
"""BSV recipe: near-Earth-object close-approach brief (NASA/JPL SBDB Close-Approach Data API).
Input : start date (default today UTC), days ahead (default 7), max distance in au (default 0.05).
Output: neo_brief_<date>.md (closest first) and neo_brief_<date>.json (the rows used). No API key needed.
Original BSV code, MIT. Data: NASA/JPL Center for Near Earth Object Studies (ssd-api.jpl.nasa.gov/cad.api).
"""
import datetime as dt, json, sys, urllib.parse, urllib.request
AU_KM = 149_597_870.7 # IAU 2012 definition of the astronomical unit
LD_KM = 384_400 # mean Earth-Moon distance used here for "lunar distances"
def fetch(start: str, days: int, dist_max: str) -> list[dict]:
end = (dt.date.fromisoformat(start) + dt.timedelta(days=days)).isoformat()
q = urllib.parse.urlencode({"date-min": start, "date-max": end, "dist-max": dist_max, "body": "Earth",
"sort": "dist", "fullname": "true", "diameter": "true"})
with urllib.request.urlopen(f"https://ssd-api.jpl.nasa.gov/cad.api?{q}", timeout=30) as r:
data = json.load(r)
f = data.get("fields", [])
rows = []
for x in data.get("data", []):
d = dict(zip(f, x))
au = float(d["dist"])
rows.append({"object": (d.get("fullname") or d["des"]).strip(), "time_tdb": d["cd"],
"dist_au": au, "dist_km": au * AU_KM, "dist_ld": au * AU_KM / LD_KM,
"v_rel_km_s": float(d["v_rel"]), "h_mag": d.get("h"),
"diameter_km": d.get("diameter"), "uncertainty": d.get("t_sigma_f")})
return rows
def brief(start: str, days: int, rows: list[dict]) -> str:
out = [f"# Close approaches to Earth, {start} + {days} days", "",
f"{len(rows)} approaches within the distance limit (NASA/JPL CNEOS). Closest first.", "",
"| Object | Time (TDB) | Distance | Lunar distances | Speed | H (mag) | Diameter |",
"|---|---|---|---|---|---|---|"]
for r in rows:
dia = f"{r['diameter_km']} km" if r["diameter_km"] else "not published"
out.append(f"| {r['object']} | {r['time_tdb']} ± {r['uncertainty']} | {r['dist_km']:,.0f} km | {r['dist_ld']:.2f} | "
f"{r['v_rel_km_s']:.1f} km/s | {r['h_mag']} | {dia} |")
out += ["", "H is absolute magnitude (smaller = larger object). Size is unknown for most objects; do not guess it.",
"Source: https://ssd-api.jpl.nasa.gov/cad.api — verify an object on JPL's Small-Body Database before quoting it."]
return "\n".join(out) + "\n"
if __name__ == "__main__":
start = sys.argv[1] if len(sys.argv) > 1 else dt.datetime.now(dt.timezone.utc).date().isoformat()
days = int(sys.argv[2]) if len(sys.argv) > 2 else 7
rows = fetch(start, days, sys.argv[3] if len(sys.argv) > 3 else "0.05")
open(f"neo_brief_{start}.md", "w").write(brief(start, days, rows))
json.dump(rows, open(f"neo_brief_{start}.json", "w"), indent=1)
print(f"wrote neo_brief_{start}.md with {len(rows)} approaches")
Output of BSV's own test run
Run on 2026-10-06 23:37 JST · Python 3.13.5
wrote neo_brief_2026-10-06.md with 14 approaches