Skip to content
RFrftools.io
Satellite Communications2026년 4월 30일10분 읽기

스카이필드와 함께 지상역 패스 스케줄 작성하기

Ansys STK 일몰 준비 완료: 스카이필드를 사용하여 아마추어 라디오 또는 큐브샛 지상국의 SGP4 통과 예측을 계산합니다.TLE 페치, 엘레베이션 마스크, 도플러 및 iCalendar 익스포트를 포함한 전체 Python 워크스루.

목차

로컬 패스 스케줄러가 필요한 이유

SatNOGS 방송국, AO-91/ISS/NOAA를 추적하는 아마추어 라디오 쉑 또는 초기 단계의 큐브샛 그라운드 구간을 운영하는 경우 자동 패스 예측이 필요합니다.상용 도구 (STK, NOVA) 는 무용지물입니다.무료 웹 예측 변수 (AMSAT, Heavens-Above) 는 스크립트를 제대로 작성하지 못합니다.STK 클라우드가 2026년 3월에 출시되는 지금, 가장 깨끗한 무료 대안은 파이펫의 최신 파이썬 후속 버전인 스카이필드 입니다.

이 게시물에서는 새 TLE를 가져오고, 위성 목록의 다음 24시간 패스를 계산하고, 최소 고도를 기준으로 필터링하고, 다운링크 주파수에 대해 도플러를 계산하고, 캘린더 앱에 패스가 표시되도록 iCalendar를 내보내는 등 약 80줄의 Python으로 완전한 지상국 스케줄러를 살펴봅니다.

설치
pip install skyfield requests icalendar
그게 다예요.스카이필드는 최초 실행 시 자체 에페메리드 (DE421, IERS) 를 가져옵니다. 약 17MB가 로컬에 캐시됩니다.

1단계: 현재 TLE 가져오기

TLE가 드리프트 (drift) — 에포크 후 7일 이내에 사용할 수 있습니다.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')
이름을 기준으로 대상을 선택하세요 (Celestrak 파일에서는 대소문자를 구분합니다).
targets = [
    by_name['AO-91 (FOX-1B)'],
    by_name['ISS (ZARYA)'],
    by_name['NOAA 19'],
    by_name['GreenCube (IO-117)'],
]
## 2단계: 그라운드 스테이션 정의
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
elevation_m파라미터를 사용하세요. 이 파라미터는 낮은 고도각에서의 경사 범위에 중대한 영향을 미칩니다.

3단계: 상승/절정/설정 이벤트 찾기Satellite.find_events()함수는 이벤트의 단순 목록 (0 = 상승, 1 = 절정, 2 = 세트) 을 반환합니다.세 배로 처리하세요.
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')
t0지평선 위에 이미 있는 위성은 무시하십시오 (이벤트 목록은 절정/설정 쌍으로 시작됨).

4단계: 패스 상에서의 도플러 시프트

아마추어 UHF (435MHz) 의 경우 패스 한 번으로 캐리어가 ±10kHz 정도 이동할 수 있으므로 수신기가 추적해야 합니다.레인지레이트로 순시 도플러 계산하기:

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')
일반적인 6분 AO-91 오버헤드 패스의 경우 약 +10kHz (상승) 에서 0 (제니스) 을 거쳐 -10kHz (세트) 까지의 도플러 스위프를 볼 수 있습니다.라디오의 메인 VFO에서 이 내용을 추적하면 신호가 손실되지 않습니다.

5단계: 각 패스의 연결 예산

범위가 기울어지면 rftools 여유 공간 경로 손실 계산기 에 입력하거나 인라인으로 계산하세요.

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
ITU-R 전파 모델을 사용한 전체 몬테카를로 링크 예산을 확인하려면 위성 링크 버짓 분석기 를 사용하고 시나리오 URL을 스테이션 로그북과 공유하십시오.

6단계: 패스가 캘린더에 표시되도록 iCalendar를 내보냅니다.

이렇게 하면 스케줄러가 사람에게 유용하게 쓰일 수 있습니다.

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')
passes.ics버전을 애플 캘린더/구글 캘린더/아웃룩으로 가져오면 모든 패스 5분 전에 푸시 알림을 받을 수 있습니다.

7단계: 크론에서 실행~/bin/station-passes.py안에 스크립트를 드롭하고 하루에 한 번 크론하십시오:
# Refresh pass schedule nightly at 03:00 local
0 3 * * * /usr/bin/python3 ~/bin/station-passes.py > ~/passes.ics 2>> ~/station.log
curl --upload-file과 함께 webdav 캘린더에 연결하면 휴대폰이 모든 패스를 자동으로 볼 수 있습니다.

STK Cloud를 하나씩 교체하기

아마추어/Cubesat/소규모 커머셜 운영의 경우 통합된 툴킷이 STK Cloud의 일상적인 드라이버 기능을 대체합니다.

STK 클라우드 기능무료 교체
패스 예측스카이필드
링크 버짓rftools 위성 링크 버짓 애널라이저
도플러/레인지 커브스카이필드 레인지-레이트
엘레베이션 마스크 + 터레인스카이필드+로컬 DEM (SRTM)
3D 시각화Cesium.js
결합 스크리닝소크라테스
미션 디자인 무역 연구 또는 기관 프로그램의 경우 여전히 데스크톱 STK 또는 NASA GMAT이 필요합니다.AMSAT 설계 검토를 포함하여 이를 제외한 모든 사항에 대해서는 Skyfield+ rftools 스택이 완성되었습니다.

더 읽어보기

관련 기사