#!/usr/bin/env python3 """ISS live position -> control signals. The space station's motion drifts the timbre. It circles Earth every ~90 min at ~7.66 km/s; lat/lon sweep continuously while the computed ground speed wobbles gently around orbital velocity -> a slow, living modulator. """ import argparse import json import math import time import urllib.request import urllib.error from pythonosc.udp_client import SimpleUDPClient URL = "http://api.open-notify.org/iss-now.json" USER_AGENT = "godstrument-iss/1.0" EARTH_RADIUS_M = 6371000.0 def haversine_m(lat1, lon1, lat2, lon2): """Great-circle ground distance in meters between two lat/lon points.""" p1 = math.radians(lat1) p2 = math.radians(lat2) dphi = math.radians(lat2 - lat1) dlam = math.radians(lon2 - lon1) a = math.sin(dphi / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dlam / 2) ** 2 return 2 * EARTH_RADIUS_M * math.asin(math.sqrt(a)) def fetch(): req = urllib.request.Request(URL, headers={"User-Agent": USER_AGENT}) with urllib.request.urlopen(req, timeout=10) as resp: data = json.loads(resp.read().decode("utf-8")) pos = data["iss_position"] return float(pos["latitude"]), float(pos["longitude"]) def main(): ap = argparse.ArgumentParser() ap.add_argument("--host", default="127.0.0.1") ap.add_argument("--port", type=int, default=9000) ap.add_argument("--interval", type=float, default=5.0) args = ap.parse_args() client = SimpleUDPClient(args.host, args.port) print(f"[world_iss] emitting /gs/iss/lat,lon,vel every {args.interval:g}s") prev = None # (lat, lon) prev_t = None # monotonic timestamp of prev fix smooth_vel = None # exponentially smoothed ground speed (m/s) backoff = args.interval while True: try: lat, lon = fetch() now = time.monotonic() client.send_message("/gs/iss/lat", float(lat)) client.send_message("/gs/iss/lon", float(lon)) if prev is not None and prev_t is not None: dt = now - prev_t if dt > 0: dist = haversine_m(prev[0], prev[1], lat, lon) vel = dist / dt # m/s ground speed if smooth_vel is None: smooth_vel = vel else: smooth_vel = 0.6 * smooth_vel + 0.4 * vel client.send_message("/gs/iss/vel", float(smooth_vel)) prev = (lat, lon) prev_t = now backoff = args.interval except (urllib.error.URLError, OSError, ValueError, KeyError, json.JSONDecodeError) as e: print(f"[world_iss] warning: {e}; retrying in {backoff:g}s") time.sleep(backoff) backoff = min(backoff * 2, 60.0) continue time.sleep(args.interval) if __name__ == "__main__": main()