Creación de un cronograma de pases para estaciones terrestres con Skyfield
Preparado para el Ansys STK al atardecer: calcula las predicciones de pases del SGP4 para tu estación terrestre de radio amateur o cubesat con Skyfield. Tutorial completo de Python con extracción de TLE, máscaras de elevación, Doppler y exportación de iCalendar.
Contenido
- Por qué necesitas un programador de pases local
- Instalar```bash
- Paso 1: recupera los TLE actuales
- Paso 3: Encuentra eventos de ascenso, culminación o descenso`Satellite.find_events()`devuelve una lista plana de eventos (0 = ascenso, 1 = culminación, 2 = conjunto). Procésalos en triples:```python
- Paso 4: cambio Doppler durante una pasada
- Paso 5: Vincula el presupuesto de cada pase
- Paso 6: Emite iCalendar para que los pases aparezcan en tu calendario
- Paso 7: ejecútalo en cron
- Sustituir a STK Cloud, pieza por pieza
- Lecturas adicionales
Por qué necesitas un programador de pases local
Si diriges una emisora SatNogs, una choza de radioaficionados que rastrean el AO-91/ISS/NOAA o un segmento terrestre con cubesat en las primeras etapas, necesitas hacer predicciones de pases automatizadas. Las herramientas comerciales (STK, NOVA) son exageradas. Los predictores web gratuitos (AMSAT, Heavens-Above) no se escriben bien. Con STK Cloud a finales de marzo de 2026, la alternativa gratuita más limpia es Skyfield, el sucesor moderno de PyEphem en Python.
Esta publicación describe un programador completo de estaciones terrenas en unas 80 líneas de Python: busca TLE nuevos, calcula las próximas 24 horas de pases para una lista de satélites, filtra por elevación mínima, calcula el Doppler para la frecuencia de enlace descendente y emite iCalendar para que los pases aparezcan en tu aplicación de calendario.
Instalarpip install skyfield requests icalendar
Eso es todo. Skyfield extrae sus propias efemérides (DE421, IERS) en la primera ejecución, es decir, unos 17 MB en caché localmente.
pip install skyfield requests icalendarPaso 1: recupera los TLE actuales
Los TLE están a la deriva: úsalos dentro de los 7 días de la época. Extrae el conjunto de elementos actual de Celestrak:
from skyfield.api import load
stations_url = 'https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=tle'
sats = load.tle_file(stations_url)
by_name = {s.name: s for s in sats}
print(f'Loaded {len(sats)} amateur-radio satellites')Elige tus objetivos por su nombre (distinguen mayúsculas de minúsculas en el archivo Celestrak):targets = [
by_name['AO-91 (FOX-1B)'],
by_name['ISS (ZARYA)'],
by_name['NOAA 19'],
by_name['GreenCube (IO-117)'],
]## Paso 2: Defina su estación terrestrefrom skyfield.api import wgs84, load
ts = load.timescale()
my_station = wgs84.latlon(40.0150, -105.2705, elevation_m=1624) # Boulder, CO
min_elevation_deg = 10 # horizon mask to skip low passesUtilice el parámetroelevation_m: afecta considerablemente al rango de inclinación en ángulos de elevación bajos.
Paso 3: Encuentra eventos de ascenso, culminación o descensoSatellite.find_events()devuelve una lista plana de eventos (0 = ascenso, 1 = culminación, 2 = conjunto). Procésalos en triples:from datetime import timedelta
t0 = ts.now()
t1 = ts.from_datetime(t0.utc_datetime() + timedelta(hours=24))
for sat in targets:
t, events = sat.find_events(my_station, t0, t1,
altitude_degrees=min_elevation_deg)
# Group into rise/peak/set triples
triples = zip(t[0::3], t[1::3], t[2::3])
for rise, culm, setting in triples:
# Peak elevation
topo = (sat - my_station).at(culm)
alt, az, dist = topo.altaz()
print(f'{sat.name:30s} {rise.utc_iso():20s} peak {alt.degrees:4.1f}° slant {dist.km:4.0f} km')
Ignora los satélites que ya están por encima del horizonte en elt0(la lista de eventos comienza con un par de puntos culminantes y conjuntos).
from datetime import timedelta
t0 = ts.now()
t1 = ts.from_datetime(t0.utc_datetime() + timedelta(hours=24))
for sat in targets:
t, events = sat.find_events(my_station, t0, t1,
altitude_degrees=min_elevation_deg)
# Group into rise/peak/set triples
triples = zip(t[0::3], t[1::3], t[2::3])
for rise, culm, setting in triples:
# Peak elevation
topo = (sat - my_station).at(culm)
alt, az, dist = topo.altaz()
print(f'{sat.name:30s} {rise.utc_iso():20s} peak {alt.degrees:4.1f}° slant {dist.km:4.0f} km')Paso 4: cambio Doppler durante una pasada
En el caso de los aficionados a la frecuencia UHF (435 MHz), un pase puede desplazar el operador en ± 10 kHz; el receptor debe rastrear. Calcula el Doppler instantáneo a partir de la velocidad de rango:
import numpy as np
FREQ_HZ = 435_800_000 # AO-91 downlink
C = 299_792_458.0 # m/s
def doppler_shift(sat, observer, t):
"""Positive when satellite approaching (received frequency is higher)."""
# Numerical range rate from 1s apart
dt = 1.0 # seconds
t_next = ts.from_datetime(t.utc_datetime() + timedelta(seconds=dt))
r0 = (sat - observer).at(t).distance().m
r1 = (sat - observer).at(t_next).distance().m
range_rate = (r1 - r0) / dt # m/s, positive = moving away
return -FREQ_HZ * range_rate / C # Hz, positive = approaching
# Sample across a pass
for pct in [0, 25, 50, 75, 100]:
t_sample = ts.from_datetime(
rise.utc_datetime() + (setting.utc_datetime() - rise.utc_datetime()) * pct / 100
)
d = doppler_shift(sat, my_station, t_sample)
print(f' {pct:3d}% pass: doppler = {d:+8.0f} Hz')En una pasada aérea típica del AO-91 de 6 minutos, verás un barrido Doppler desde aproximadamente +10 kHz (subida) pasando por 0 (cenit) hasta −10 kHz (ajuste). Síguelo en el VFO principal de la radio para no perder la señal.
Paso 5: Vincula el presupuesto de cada pase
Cuando tengas el rango de inclinación, introdúcelo en la calculadora de pérdidas por trayectoria en espacios libres de rftools o calcula en línea:
def fspl_db(distance_m, freq_hz):
return 20 * np.log10(4 * np.pi * distance_m * freq_hz / C)
# At zenith of an AO-91 pass (~800 km slant at 40° elevation from Boulder)
fspl = fspl_db(800_000, FREQ_HZ)
print(f'FSPL = {fspl:.1f} dB') # ~143 dBPara obtener un presupuesto completo de los enlaces de Montecarlo con los modelos de propagación del UIT-R, utilice el Satellite Link Budget Analyzer y comparta la URL del escenario con el cuaderno de bitácora de la estación.
Paso 6: Emite iCalendar para que los pases aparezcan en tu calendario
Aquí es donde el planificador se vuelve útil para los humanos:
from icalendar import Calendar, Event
from datetime import datetime
import pytz
cal = Calendar()
cal.add('prodid', '-//rftools ground station//')
cal.add('version', '2.0')
for sat in targets:
t, events = sat.find_events(my_station, t0, t1, altitude_degrees=10)
triples = zip(t[0::3], t[1::3], t[2::3])
for rise, culm, setting in triples:
topo = (sat - my_station).at(culm)
alt, az, _ = topo.altaz()
ev = Event()
ev.add('summary', f'{sat.name} (peak {alt.degrees:.0f}°)')
ev.add('dtstart', rise.utc_datetime())
ev.add('dtend', setting.utc_datetime())
ev.add('description', f'Culminate at {culm.utc_iso()}, AZ {az.degrees:.0f}°')
cal.add_component(ev)
with open('passes.ics', 'wb') as f:
f.write(cal.to_ical())
print('Wrote passes.ics — import into Google Calendar / Apple Calendar / Outlook')Importa el artículo 12§ a Apple Calendar, Google Calendar o Outlook y recibirás notificaciones push 5 minutos antes de cada pase.
Paso 7: ejecútalo en cron
Coloca el script en~/bin/station-passes.pyy córtalo una vez al día:
# Refresh pass schedule nightly at 03:00 local
0 3 * * * /usr/bin/python3 ~/bin/station-passes.py > ~/passes.ics 2>> ~/station.logCombínalo concurl --upload-filea un calendario webdav y tu teléfono verá todos los pases automáticamente.
Sustituir a STK Cloud, pieza por pieza
Para pequeñas operaciones comerciales de aficionados, cubesats y pequeñas, el conjunto de herramientas combinado reemplaza las funciones diarias de STK Cloud:
| Función STK Cloud | Sustitución gratuita |
|---|---|
| Predicción de pases | Skyfield |
| Link budget | rftools Satellite Link Budget Analyzer |
| Doppler/curvas de rango | Velocidad de alcance de Skyfield |
| Máscaras de elevación más terreno | Skyfield + DEM local (SRTM) |
| Visualización 3D | Cesium.js |
| Detección conjunta | SÓCRATES |
Lecturas adicionales
- Documentación de Skyfield
- Catálogos Celestrak TLE
- rftools Satellite Pass Predictor: versión alojada de este planificador para los satélites aficionados más comunes
- Migrar desde STK Cloud: alternativas gratuitas: el artículo complementario sobre cómo reemplazar el flujo de trabajo completo de STK
- Cómo calcular el presupuesto de un enlace UHF CubeSat: ejemplo práctico de principio a fin
Artículos Relacionados
Scripting Satellite Link Budgets with ITU-Rpy (Python Examples)
STK-free link-budget automation: sweep frequency, rain availability, and elevation in pure Python using the ITU-Rpy reference implementation of P.618/P.676/P.840. Companion to the rftools Satellite Link Budget Analyzer.
30 abr 2026
Satellite CommunicationsSizing a 9600-baud UHF Downlink for a 3U CubeSat: Full Walkthrough
End-to-end link budget for an amateur-band 3U cubesat: EIRP, ground-station G/T, ITU-R propagation losses, and Monte Carlo availability. Uses the Amateur CubeSat preset.
29 abr 2026
Satellite CommunicationsMigrating from STK Cloud: Free Alternatives for Link Budget and Orbit Analysis
Ansys is sunsetting STK Cloud in March 2026. Here are the free open-source replacements for the two things it did best — ITU-R link budgets and orbital pass prediction.
29 abr 2026