Svalbard Ground Station Access

Computes all contact windows between a sun-synchronous satellite and the Svalbard Satellite Station (SvalSat) over a 7-day period.

Scenario

  • 550 km sun-synchronous orbit, LTDN 10:30 (descending node)

  • Ground station: SvalSat, 78.229°N / 15.407°E / 500 m altitude

  • Minimum elevation: 5°

  • Analysis window: 2025-03-01 → 2025-03-08 (7 days)

[1]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime

from missiontools import Spacecraft, GroundStation

1. Spacecraft

A 550 km SSO with a 10:30 local time at descending node, propagated with J2.

[2]:
EPOCH = np.datetime64('2025-03-01T00:00:00', 'us')

sc = Spacecraft.sunsync(
    altitude_km    = 550.0,
    node_solar_time = '10:30',
    node_type      = 'descending',
    epoch          = EPOCH,
)

period_s = 2 * np.pi * np.sqrt(sc.a**3 / sc.central_body_mu)

print(f"Semi-major axis : {sc.a / 1e3:.1f} km")
print(f"Altitude        : {(sc.a - sc.central_body_radius) / 1e3:.1f} km")
print(f"Inclination     : {np.degrees(sc.i):.3f}°")
print(f"RAAN at epoch   : {np.degrees(sc.raan):.3f}°")
print(f"Orbital period  : {period_s / 60:.2f} min")
print(f"Propagator      : {sc.propagator_type}")
Semi-major axis : 6928.1 km
Altitude        : 550.0 km
Inclination     : 97.593°
RAAN at epoch   : 139.643°
Orbital period  : 95.65 min
Propagator      : j2

2. Ground Station

SvalSat sits on the island of Spitsbergen at 78.229°N — so far north that it can see every polar-orbiting satellite at least twice per day.

[3]:
gs = GroundStation(lat=78.229, lon=15.407, alt=500.0)

print(f"Ground station  : SvalSat")
print(f"Latitude        : {gs.lat:.3f}°N")
print(f"Longitude       : {gs.lon:.3f}°E")
print(f"Altitude        : {gs.alt:.0f} m a.s.l.")
Ground station  : SvalSat
Latitude        : 78.229°N
Longitude       : 15.407°E
Altitude        : 500 m a.s.l.

3. Compute Access Windows

GroundStation.access() scans the analysis window at the requested maximum time step and returns a list of (AOS, LOS) pairs as datetime64[us] tuples. A 20 s step is fine for detecting passes whose shortest duration exceeds 5 min.

[4]:
T_START = np.datetime64('2025-03-01T00:00:00', 'us')
T_END   = np.datetime64('2025-03-08T00:00:00', 'us')

passes = gs.access(
    sc,
    T_START, T_END,
    el_min = 5.0,                      # degrees
    max_step = np.timedelta64(20, 's'),
)

print(f"Total passes found: {len(passes)}")
Total passes found: 96

4. Access Report

[5]:
print(f"  {'#':>3}  {'AOS (UTC)':^26}  {'LOS (UTC)':^26}  {'Duration':>9}")
print(f"  {'─'*3}  {'─'*26}  {'─'*26}  {'─'*9}")

total_s = 0
for idx, (aos, los) in enumerate(passes, 1):
    dur_s = int((los - aos) / np.timedelta64(1, 's'))
    total_s += dur_s
    print(f"  {idx:>3}  {str(aos):^26}  {str(los):^26}  "
          f"{dur_s // 60:3d}m {dur_s % 60:02d}s")

window_s = int((T_END - T_START) / np.timedelta64(1, 's'))
print()
print(f"  Total passes       : {len(passes)}")
print(f"  Total contact time : {total_s // 3600}h "
      f"{(total_s % 3600) // 60}m {total_s % 60:02d}s")
print(f"  Duty cycle         : {total_s / window_s * 100:.2f}%")
print(f"  Mean pass duration : {total_s / len(passes) / 60:.1f} min")
    #          AOS (UTC)                   LOS (UTC)            Duration
  ───  ──────────────────────────  ──────────────────────────  ─────────
    1  2025-03-01T01:10:53.750000  2025-03-01T01:18:22.500000    7m 28s
    2  2025-03-01T02:46:01.250000  2025-03-01T02:55:08.125000    9m 06s
    3  2025-03-01T04:21:03.750000  2025-03-01T04:30:53.750000    9m 50s
    4  2025-03-01T05:55:56.250000  2025-03-01T06:05:52.500000    9m 56s
    5  2025-03-01T07:30:36.250000  2025-03-01T07:40:21.250000    9m 45s
    6  2025-03-01T09:05:02.500000  2025-03-01T09:14:42.500000    9m 40s
    7  2025-03-01T10:39:23.750000  2025-03-01T10:49:11.250000    9m 47s
    8  2025-03-01T12:13:56.875000  2025-03-01T12:23:53.750000    9m 56s
    9  2025-03-01T13:49:03.125000  2025-03-01T13:58:48.750000    9m 45s
   10  2025-03-01T15:25:00.000000  2025-03-01T15:33:52.500000    8m 52s
   11  2025-03-01T17:01:57.500000  2025-03-01T17:09:00.625000    7m 03s
   12  2025-03-01T18:39:59.375000  2025-03-01T18:44:07.500000    4m 08s
   13  2025-03-01T23:30:31.250000  2025-03-01T23:35:25.000000    4m 53s
   14  2025-03-02T01:05:40.000000  2025-03-02T01:13:12.500000    7m 32s
   15  2025-03-02T02:40:47.500000  2025-03-02T02:49:55.625000    9m 08s
   16  2025-03-02T04:15:49.375000  2025-03-02T04:25:40.625000    9m 51s
   17  2025-03-02T05:50:41.875000  2025-03-02T06:00:37.500000    9m 55s
   18  2025-03-02T07:25:21.250000  2025-03-02T07:35:06.250000    9m 45s
   19  2025-03-02T08:59:47.500000  2025-03-02T09:09:26.875000    9m 39s
   20  2025-03-02T10:34:08.125000  2025-03-02T10:43:56.250000    9m 48s
   21  2025-03-02T12:08:42.500000  2025-03-02T12:18:39.375000    9m 56s
   22  2025-03-02T13:43:50.000000  2025-03-02T13:53:34.375000    9m 44s
   23  2025-03-02T15:19:48.125000  2025-03-02T15:28:38.125000    8m 50s
   24  2025-03-02T16:56:48.125000  2025-03-02T17:03:46.875000    6m 58s
   25  2025-03-02T18:34:51.250000  2025-03-02T18:38:53.750000    4m 02s
   26  2025-03-02T23:25:17.500000  2025-03-02T23:30:16.250000    4m 58s
   27  2025-03-03T01:00:25.625000  2025-03-03T01:08:01.875000    7m 36s
   28  2025-03-03T02:35:33.125000  2025-03-03T02:44:43.750000    9m 10s
   29  2025-03-03T04:10:35.625000  2025-03-03T04:20:26.875000    9m 51s
   30  2025-03-03T05:45:27.500000  2025-03-03T05:55:23.125000    9m 55s
   31  2025-03-03T07:20:06.250000  2025-03-03T07:29:51.250000    9m 45s
   32  2025-03-03T08:54:32.500000  2025-03-03T09:04:11.875000    9m 39s
   33  2025-03-03T10:28:53.125000  2025-03-03T10:38:41.250000    9m 48s
   34  2025-03-03T12:03:28.125000  2025-03-03T12:13:25.000000    9m 56s
   35  2025-03-03T13:38:36.875000  2025-03-03T13:48:20.000000    9m 43s
   36  2025-03-03T15:14:36.875000  2025-03-03T15:23:24.375000    8m 47s
   37  2025-03-03T16:51:38.125000  2025-03-03T16:58:32.500000    6m 54s
   38  2025-03-03T18:29:43.125000  2025-03-03T18:33:39.375000    3m 56s
   39  2025-03-03T21:45:31.250000  2025-03-03T21:45:55.625000    0m 24s
   40  2025-03-03T23:20:03.750000  2025-03-03T23:25:07.500000    5m 03s
   41  2025-03-04T00:55:11.875000  2025-03-04T01:02:51.250000    7m 39s
   42  2025-03-04T02:30:19.375000  2025-03-04T02:39:31.875000    9m 12s
   43  2025-03-04T04:05:21.250000  2025-03-04T04:15:13.125000    9m 51s
   44  2025-03-04T05:40:13.125000  2025-03-04T05:50:08.125000    9m 55s
   45  2025-03-04T07:14:51.875000  2025-03-04T07:24:36.250000    9m 44s
   46  2025-03-04T08:49:16.875000  2025-03-04T08:58:56.875000    9m 40s
   47  2025-03-04T10:23:38.125000  2025-03-04T10:33:26.875000    9m 48s
   48  2025-03-04T11:58:13.125000  2025-03-04T12:08:10.000000    9m 56s
   49  2025-03-04T13:33:23.125000  2025-03-04T13:43:06.250000    9m 43s
   50  2025-03-04T15:09:25.000000  2025-03-04T15:18:10.625000    8m 45s
   51  2025-03-04T16:46:28.125000  2025-03-04T16:53:18.750000    6m 50s
   52  2025-03-04T18:24:35.000000  2025-03-04T18:28:25.625000    3m 50s
   53  2025-03-04T21:40:09.375000  2025-03-04T21:40:56.250000    0m 46s
   54  2025-03-04T23:14:49.375000  2025-03-04T23:19:58.750000    5m 09s
   55  2025-03-05T00:49:58.125000  2025-03-05T00:57:40.625000    7m 42s
   56  2025-03-05T02:25:05.625000  2025-03-05T02:34:19.375000    9m 13s
   57  2025-03-05T04:00:06.875000  2025-03-05T04:09:59.375000    9m 52s
   58  2025-03-05T05:34:58.750000  2025-03-05T05:44:53.750000    9m 55s
   59  2025-03-05T07:09:36.875000  2025-03-05T07:19:20.625000    9m 43s
   60  2025-03-05T08:44:01.875000  2025-03-05T08:53:41.875000    9m 40s
   61  2025-03-05T10:18:23.125000  2025-03-05T10:28:11.875000    9m 48s
   62  2025-03-05T11:52:58.750000  2025-03-05T12:02:55.625000    9m 56s
   63  2025-03-05T13:28:10.000000  2025-03-05T13:37:51.875000    9m 41s
   64  2025-03-05T15:04:13.750000  2025-03-05T15:12:56.250000    8m 42s
   65  2025-03-05T16:41:18.125000  2025-03-05T16:48:05.000000    6m 46s
   66  2025-03-05T18:19:26.875000  2025-03-05T18:23:11.250000    3m 44s
   67  2025-03-05T21:34:50.625000  2025-03-05T21:35:53.125000    1m 02s
   68  2025-03-05T23:09:35.625000  2025-03-05T23:14:50.000000    5m 14s
   69  2025-03-06T00:44:44.375000  2025-03-06T00:52:30.000000    7m 45s
   70  2025-03-06T02:19:51.250000  2025-03-06T02:29:06.875000    9m 15s
   71  2025-03-06T03:54:52.500000  2025-03-06T04:04:45.625000    9m 53s
   72  2025-03-06T05:29:43.750000  2025-03-06T05:39:38.750000    9m 55s
   73  2025-03-06T07:04:21.875000  2025-03-06T07:14:05.625000    9m 43s
   74  2025-03-06T08:38:46.875000  2025-03-06T08:48:26.875000    9m 40s
   75  2025-03-06T10:13:08.125000  2025-03-06T10:22:56.875000    9m 48s
   76  2025-03-06T11:47:44.375000  2025-03-06T11:57:41.875000    9m 57s
   77  2025-03-06T13:22:56.875000  2025-03-06T13:32:38.125000    9m 41s
   78  2025-03-06T14:59:01.875000  2025-03-06T15:07:42.500000    8m 40s
   79  2025-03-06T16:36:08.125000  2025-03-06T16:42:51.250000    6m 43s
   80  2025-03-06T18:14:18.750000  2025-03-06T18:17:56.875000    3m 38s
   81  2025-03-06T21:29:33.125000  2025-03-06T21:30:48.750000    1m 15s
   82  2025-03-06T23:04:21.875000  2025-03-06T23:09:41.250000    5m 19s
   83  2025-03-07T00:39:30.625000  2025-03-07T00:47:19.375000    7m 48s
   84  2025-03-07T02:14:37.500000  2025-03-07T02:23:55.000000    9m 17s
   85  2025-03-07T03:49:38.750000  2025-03-07T03:59:31.875000    9m 53s
   86  2025-03-07T05:24:29.375000  2025-03-07T05:34:23.750000    9m 54s
   87  2025-03-07T06:59:06.875000  2025-03-07T07:08:50.625000    9m 43s
   88  2025-03-07T08:33:31.250000  2025-03-07T08:43:11.875000    9m 40s
   89  2025-03-07T10:07:53.125000  2025-03-07T10:17:42.500000    9m 49s
   90  2025-03-07T11:42:30.625000  2025-03-07T11:52:27.500000    9m 56s
   91  2025-03-07T13:17:43.750000  2025-03-07T13:27:23.750000    9m 40s
   92  2025-03-07T14:53:50.625000  2025-03-07T15:02:28.750000    8m 38s
   93  2025-03-07T16:30:58.750000  2025-03-07T16:37:37.500000    6m 38s
   94  2025-03-07T18:09:10.625000  2025-03-07T18:12:43.125000    3m 32s
   95  2025-03-07T21:24:16.875000  2025-03-07T21:25:43.750000    1m 26s
   96  2025-03-07T22:59:08.125000  2025-03-07T23:04:32.500000    5m 24s

  Total passes       : 96
  Total contact time : 12h 54m 58s
  Duty cycle         : 7.69%
  Mean pass duration : 8.1 min

5. Pass Timeline

Each bar represents one contact window. The bar length is proportional to contact duration.

[6]:
# Convert datetime64 to Python datetime for matplotlib
def to_dt(t64):
    return t64.astype('datetime64[ms]').astype(datetime)

fig, ax = plt.subplots(figsize=(12, 4))

for idx, (aos, los) in enumerate(passes):
    dur_s = int((los - aos) / np.timedelta64(1, 's'))
    ax.barh(
        0,
        left  = to_dt(aos),
        width = to_dt(los) - to_dt(aos),
        height = 0.5,
        color  = 'tab:blue',
        alpha  = 0.7,
    )
    # Annotate short passes with their duration (min:ss)
    mid = to_dt(aos + (los - aos) // 2)
    ax.text(mid, 0, f"{dur_s // 60}m{dur_s % 60:02d}s",
            ha='center', va='center', fontsize=7, color='white', fontweight='bold')

ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
ax.xaxis.set_major_locator(mdates.DayLocator())
ax.set_yticks([])
ax.set_xlim(to_dt(T_START), to_dt(T_END))
ax.set_xlabel('Date (UTC)')
ax.set_title(
    f'SvalSat contact windows — 550 km SSO LTDN 10:30, el ≥ 5°\n'
    f'{len(passes)} passes, {total_s / 3600:.1f} h total contact time'
)
ax.grid(True, axis='x', alpha=0.3)
plt.tight_layout()
plt.show()
../_images/examples_svalbard_access_11_0.png

6. Passes per Day

[7]:
from collections import defaultdict

passes_per_day = defaultdict(list)
for aos, los in passes:
    day = str(aos)[:10]   # 'YYYY-MM-DD'
    dur_s = int((los - aos) / np.timedelta64(1, 's'))
    passes_per_day[day].append(dur_s)

print(f"  {'Date':>12}  {'Passes':>7}  {'Contact':>12}")
print(f"  {'─'*12}  {'─'*7}  {'─'*12}")
for day in sorted(passes_per_day):
    n = len(passes_per_day[day])
    t = sum(passes_per_day[day])
    print(f"  {day:>12}  {n:>7}  {t // 60:3d}m {t % 60:02d}s")
          Date   Passes       Contact
  ────────────  ───────  ────────────
    2025-03-01       13  110m 09s
    2025-03-02       13  110m 06s
    2025-03-03       14  110m 27s
    2025-03-04       14  110m 48s
    2025-03-05       14  110m 58s
    2025-03-06       14  111m 12s
    2025-03-07       14  111m 18s