Skip to content
RFrftools.io
Satellite Communications30 de abril de 20265 min de leitura

Construindo um cronograma de passes para a estação terrestre com a Skyfield

Pronto para o pôr do sol do Ansys STK: calcule as previsões de passagem SGP4 para sua estação terrestre de rádio amador ou cubesat usando o Skyfield.

Conteúdo

Por que você precisa de um agendador de passes local

Se você administra uma estação SatNogs, uma cabine de rádio amador que rastreia AO-91/ISS/NOAA ou um segmento terrestre de cubesat em estágio inicial, você precisa de previsões automatizadas de passes. As ferramentas comerciais (STK, NOVA) são um exagero. Os preditores gratuitos da web (AMSAT, Heavens-Above) não funcionam bem. Com o fim do STK Cloud em março de 2026, a alternativa gratuita mais limpa é Skyfield — o moderno sucessor do Python do PyEphem.

Esta postagem mostra um **agendador completo de estações terrestres em cerca de 80 linhas de Python: busque novos TLEs, calcule as próximas 24 horas de passes para uma lista de satélites, filtre por elevação mínima, calcule o Doppler para sua frequência de downlink e emita o iCalendar para que os passes apareçam em seu aplicativo de calendário.

Instalar

pip install skyfield requests icalendar

É isso mesmo. O Skyfield obtém suas próprias efemérides (DE421, IERS) na primeira execução — cerca de 17 MB em cache localmente.

Etapa 1: Buscar os TLEs atuais

Os TLEs derivam — use-os dentro de 7 dias após a época. Extraia o conjunto de elementos atual do 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')

Escolha seus alvos pelo nome (eles diferenciam maiúsculas de minúsculas no arquivo Celestrak):

targets = [
    by_name['AO-91 (FOX-1B)'],
    by_name['ISS (ZARYA)'],
    by_name['NOAA 19'],
    by_name['GreenCube (IO-117)'],
]

Etapa 2: defina sua estação terrestre

from 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 passes

Use o parâmetro elevation_m— ele afeta materialmente a faixa de inclinação em ângulos de elevação baixos.

Etapa 3: Encontre eventos de ascensão/culminação/set

Satellite.find_events() retorna uma lista plana de eventos (0 = aumento, 1 = culminar, 2 = conjunto). Processe-os em triplos:
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')

Ignore os satélites que já estão acima do horizonte em t0(a lista de eventos começa com um par culminante/definido).

Etapa 4: mudança Doppler ao longo de uma passagem

Para UHF amador (435 MHz), um passe pode deslocar sua operadora em ± 10 kHz — seu receptor deve rastrear. Calcule o Doppler instantâneo a partir da taxa de faixa:

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')

Para uma passagem aérea típica de 6 minutos do AO-91, você verá o Doppler varrer de cerca de +10 kHz (aumento) até 0 (zênite) até −10 kHz (conjunto). Acompanhe isso no VFO principal do rádio para não perder o sinal.

Etapa 5: vincular o orçamento de cada passe

Depois de obter o alcance inclinado, insira-o na rftools Free-Space Path Loss Calculator ou calcule em linha:

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 dB

Para obter um orçamento completo de links de Monte Carlo com modelos de propagação ITU-R, use o Satellite Link Budget Analyzer e compartilhe o URL do cenário com o diário de bordo da sua estação.

Etapa 6: emita o iCalendar para que os passes apareçam no seu calendário

É aqui que o agendador se torna útil para 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')

Importe passes.ics para o Apple Calendar/Google Calendar/Outlook e você receberá notificações push 5 minutos antes de cada passagem.

Etapa 7: execute-o no cron

Coloque o script em ~/bin/station-passes.py e faça o cron uma vez por dia:

# Refresh pass schedule nightly at 03:00 local
0 3 * * * /usr/bin/python3 ~/bin/station-passes.py > ~/passes.ics 2>> ~/station.log

Emparelhe-o com o curl --upload-file em um calendário webdav e seu telefone verá cada passe automaticamente.

Substituindo o STK Cloud, peça por peça

Para operações comerciais amadoras/cubesat/pequenas, o kit de ferramentas combinado substitui os recursos de driver diário do STK Cloud:

Recurso STK CloudSubstituição gratuita
Previsão de aprovaçãoSkyfield
Orçamento do linkAnalisador de orçamento do rftools Satellite Link
Curvas Doppler/faixaTaxa de alcance Skyfield
Máscaras de elevação + terrenoSkyfield + DEM local (SRTM)
Visualização 3DCesium.js
Triagem de conjunçãoSOCRATES
Para estudos comerciais de design de missões ou programas institucionais, você ainda desejará o desktop STK ou o NASA GMAT. Para tudo menos isso, incluindo as análises de design da AMSAT, a pilha Skyfield + rftools está completa.

Leitura adicional

Perguntas Frequentes

Para previsão de passes, curvas Doppler, cronogramas de elevação e downloads programados, sim. A Skyfield implementa o SGP4 com efemérides validadas contra o JPL Horizons, corresponde às previsões de passatempo da STK em alguns segundos em horizontes de 30 dias. Ele não substitui a visualização 3D, os relatórios integrados ou a análise de conjunção da STK — use o Cesium.js ou o SOCRATES para isso.
Dentro de 7 dias da época para precisão de posição abaixo de 1 km para satélites LEO. Os TLEs com mais de 14 dias podem flutuar mais de 10 km em uma pista longa, fazendo com que os tempos de passagem mudem em mais de 30 segundos — o suficiente para perder uma janela de passe de 6 minutos. Obtenha novos TLEs do Celestrak antes de cada execução de agendamento.
Três causas prováveis: (1) as coordenadas da sua estação terrestre estão erradas em dezenas de metros — verifique com o GPS, (2) você está usando uma época TLE mais antiga do que o preditor da AMSAT, (3) você não está contabilizando a elevação da estação acima do nível do mar (use elevation_m in wgs84.latlon). Diferenças abaixo de 10 segundos são esperadas e não afetam as operações práticas.
Numericamente: calcule o alcance inclinado no tempo t e t+1s, divida para obter a taxa de alcance em m/s, depois doppler_shift_hz = -frequency_hz * range_rate / c (positivo quando o satélite se aproxima). O Skyfield não tem uma função doppler embutida porque depende da sua frequência de downlink/uplink — calcule você mesmo. Uma passagem LEO típica de 435 MHz varre ± 10 kHz em ~ 6 minutos.
Para GEO (geoestacionário), sim — mas a precisão do SGP4/TLE se degrada acima de ~ 6000 km. Use um solucionador de elementos Keplerianos ou efemérides completas (DE440) para GEO. Para missões no espaço profundo, use as efemérides baseadas no kernel da Skyfield com os kernels SPICE da NASA. Para órbitas lunares, carregue as efemérides DE440 ou jup365 via skyfield.api.load ('de440.bsp').

Artigos Relacionados