#!/usr/bin/env python3 """The actual solar wind hitting Earth right now, opening the master filter — playing the sun. NOAA SWPC reports live proton speed (km/s) of the solar wind stream; a faster gust is a brighter, more open sound. The star's mood becomes the master filter cutoff.""" import argparse import json import time import urllib.request from pythonosc.udp_client import SimpleUDPClient NAME = "world_sun" URL = "https://services.swpc.noaa.gov/products/summary/solar-wind-speed.json" POLL_INTERVAL = 60.0 # seconds between API polls (be kind to NOAA) REEMIT_INTERVAL = 5.0 # re-fire last known value this often UA = "Godstrument/1.0 (world_sun worker; +https://github.com/godstrument)" def fetch_speed(): """Fetch current solar-wind proton speed (km/s). Returns float or None on failure.""" req = urllib.request.Request(URL, headers={"User-Agent": UA}) with urllib.request.urlopen(req, timeout=15) as resp: data = json.load(resp) # Response is a JSON list; take [0]["proton_speed"]. return float(data[0]["proton_speed"]) def main(): ap = argparse.ArgumentParser(description="Emit live solar-wind speed as OSC.") ap.add_argument("--host", default="127.0.0.1") ap.add_argument("--port", type=int, default=9000) args = ap.parse_args() client = SimpleUDPClient(args.host, args.port) print(f"[{NAME}] emitting /gs/sun/speed every {int(REEMIT_INTERVAL)}s") last_value = None last_poll = 0.0 backoff = 5.0 while True: now = time.monotonic() # Poll the API at most once per POLL_INTERVAL. if now - last_poll >= POLL_INTERVAL or last_value is None: try: last_value = fetch_speed() last_poll = now backoff = 5.0 # reset backoff on success except Exception as e: print(f"[{NAME}] fetch failed: {e}; retrying in {backoff:.0f}s") time.sleep(backoff) backoff = min(backoff * 2, 120.0) continue # Re-emit last known value. try: client.send_message("/gs/sun/speed", float(last_value)) except Exception as e: print(f"[{NAME}] send failed: {e}") time.sleep(REEMIT_INTERVAL) if __name__ == "__main__": main()