home

first contact - orbital mechanics (pass prediction)

scenario

a software update knocked the ground station's automated pass prediction system offline just before a scheduled communications window.
the operations team still has the satellite's latest TLEs and the station's coordinates, but they need you to manually determine when the satellite will be above the horizon and visible from the ground station. calculate the visibility windows so contact can be re-established before the next scheduled transmission is missed.

requirements

read the doc for skyfield and install it.
challenge must be solved dynamically and in one persistent session.

challenge info

Challenge Sat 1
TLE:
DIGITWIN HTB
1 01337U 00000A 26277.58845255+.00000000 00000-0 18941-3 0 189 7
2 01337 97.3224 170.3767 0047165 301.1887 95.3084 15.43215537179265
Station location:
(Lat,Long): 46.57527473265949,-48.605998524385605

When will it be visible next?>

automated solver

#!/usr/bin/python3.11

import socket, re, time
from datetime import timedelta
from skyfield.api import load, EarthSatellite, wgs84

HOST = "154.57.164.72"
PORT = 32211

def calculate(sat, lat, lon):
    station  = wgs84.latlon(lat,lon)
    #print(station)
    t0 = ts.now()
    t1 = ts.from_datetime(t0.utc_datetime() + timedelta(hours=24))
    t, events = sat.find_events(station, t0, t1, altitude_degrees=30.0)
    out, i = [], 0
    while i < len(events) - 2:
        if events[i] == 0 and events[i+1] == 1 and events[i+2] == 2:
            out.append(t[i].utc_strftime("%Y-%m-%dT%H:%M:%SZ"))
            out.append(t[i+2].utc_strftime("%Y-%m-%dT%H:%M:%SZ"))
        i += 1
    return " ".join(out)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    while True:
        buffer = ""
        while "visible next?" not in buffer:
            time.sleep(1)
            data = s.recv(4096)
            if not data:
                break
            buffer += data.decode(errors="replace")
        print(buffer)
        if "visible next?" not in buffer:
                break

        lines = buffer.splitlines()
        found = False
        i = 0
        while i < len(lines) - 2:
            ts = load.timescale()
            line1, line2 = lines[i], lines[i + 1]
            line3 = lines[i + 2]
            if line1.startswith("1 ") and line2.startswith("2 "):
                sat = EarthSatellite(line1, line2, 'DIGITWIN HTB', ts)
                pos = re.search(r"\(Lat,Long\):\s*(-?\d+\.?\d*)\s*,\s*(-?\d+\.?\d*)", buffer)
                #print(sat)
                #print(pos)
                answer = calculate(sat, float(pos.group(1)), float(pos.group(2)))
                #print(answer)
                s.sendall((answer + "\n").encode())
                #print(s.recv(4096).decode(errors="replace"))
                found = True
                break
                i += 2
            else:
                i += 1
        if not found:
            break

whoami – uses – writeups – music – reading