● Zion Boggan
repos/Sightline/backend/live_satellites.py
zionboggan.com ↗
68 lines · python
History for this file →
1
"""Live satellite-camera tracker. Re-propagates imaging-satellite positions to 'now' every
2
INTERVAL seconds (locally, from cached orbital elements - no network spam), upserts the latest
3
position into live_entities, and appends to entity_positions so each satellite has a moving
4
ground track. Prunes position history beyond RETAIN_MINUTES.
5
"""
6
from __future__ import annotations
7
 
8
import signal
9
import time
10
from datetime import datetime, timedelta, timezone
11
 
12
from sqlalchemy import delete, select
13
from sqlalchemy.dialects.postgresql import insert as pg_insert
14
 
15
from app.db.session import SessionLocal
16
from app.models import EntityPosition, LiveEntity
17
from app.services.realsources import imaging_satellites
18
 
19
INTERVAL = 30
20
RETAIN_MINUTES = 180
21
_running = True
22
 
23
 
24
def _stop(*_):
25
    global _running
26
    _running = False
27
 
28
 
29
def main():
30
    signal.signal(signal.SIGTERM, _stop)
31
    signal.signal(signal.SIGINT, _stop)
32
    db = SessionLocal()
33
    print(f"satellite tracker started (every {INTERVAL}s)", flush=True)
34
    while _running:
35
        try:
36
            sats = imaging_satellites()
37
            now = datetime.now(timezone.utc)
38
            for s in sats:
39
                stmt = pg_insert(LiveEntity).values(
40
                    kind="satellite_camera", source=s["source"], identifier=s["identifier"],
41
                    label=s["label"], lat=s["lat"], lon=s["lon"], altitude_m=s["altitude_m"],
42
                    origin_country=None, attributes=s["attributes"], observed_at=now)
43
                stmt = stmt.on_conflict_do_update(
44
                    index_elements=["source", "identifier"],
45
                    set_={"lat": s["lat"], "lon": s["lon"], "altitude_m": s["altitude_m"],
46
                          "observed_at": now})
47
                db.execute(stmt)
48
            if sats:
49
                db.execute(EntityPosition.__table__.insert(),
50
                           [{"identifier": s["identifier"], "kind": "satellite_camera",
51
                             "lat": s["lat"], "lon": s["lon"], "altitude_m": s["altitude_m"],
52
                             "observed_at": now} for s in sats])
53
            db.execute(delete(EntityPosition).where(
54
                EntityPosition.observed_at < now - timedelta(minutes=RETAIN_MINUTES)))
55
            db.commit()
56
            print(f"propagated {len(sats)} satellite cameras @ {now.strftime('%H:%M:%S')}", flush=True)
57
        except Exception as e:
58
            print(f"sat tracker error: {e}", flush=True)
59
            db.rollback()
60
        for _ in range(INTERVAL):
61
            if not _running:
62
                break
63
            time.sleep(1)
64
    db.close()
65
 
66
 
67
if __name__ == "__main__":
68
    main()