Satellite link

Overview

Satellite link is OscarWatch’s replacement for the legacy SatPC32 DDE interface. When enabled, OscarWatch runs a small WebSocket server on your PC that sends JSON messages about the focused satellite: name, NORAD ID, uplink and downlink frequencies and modes, ADIF bands, azimuth, elevation, range, and more. Logging applications can subscribe and pre-fill QSO fields automatically. When you log a QSO in OscarWatch Logbook, OscarWatch also broadcasts a qsoLogged event (and qsoUpdated / qsoDeleted when you edit or remove entries) so external tools can mirror your log in real time. When a scheduled sidebar pass reaches its reminder, OscarWatch broadcasts a passAlert event so a second screen or logging app can warn you before AOS.

Each satellite status message also includes a wispDde string in the old Wisp32/GSC format for adapters that still expect SatPC32-style text. QSO and pass-alert messages do not.

Settings

Open Settings → Integrations → Satellite link. Press Save when finished. See also Settings (Integrations).

  • Enable Satellite link WebSocket server — master switch.
  • Port — default 7373 (1024–65535).
  • Allow connections from local network — when off, only this PC (127.0.0.1). When on, OscarWatch listens on your LAN IP addresses (shown in the connection URL box). On Windows, allow OscarWatch when the system firewall prompt appears.
  • Only broadcast when satellite is above horizon — when on, payloads below 0° elevation send ** NO SATELLITE ** (SatPC32-style).
  • Update interval (ms) — minimum time between identical payloads (default 1000). Focus and mode changes are sent immediately.
  • Test port — checks that OscarWatch can bind to the port (no client required).
  • Open help — opens this page.

Status bar

When Satellite link is enabled, the main window status bar shows a Sat link label to the left of the clock:

  • amber — waiting for clients (port open, none connected yet)
  • green — one or more clients connected (hover for port and client count)
  • red — could not start (port in use, permissions, etc.)

The indicator is hidden when Satellite link is disabled in Settings.

For developers

Protocol version 1. Connect with a standard WebSocket client to:

ws://127.0.0.1:<port>/ (this PC only)

or, when LAN access is enabled:

ws://<this PC IP>:<port>/

There is no authentication in v1 — use localhost or a trusted LAN only. On connect, OscarWatch sends the latest satellite snapshot immediately, then broadcasts updates when focus, mode, frequencies, or tracking data change (throttled by the update interval). QSO events are sent immediately when you add, edit, or delete a contact in OscarWatch Logbook (not replayed to new clients on connect).

Satellite status messages

Live tracking payloads use type: "satelliteStatus":

{
  "type": "satelliteStatus",
  "version": 1,
  "timestampUtc": "2026-07-07T11:04:00.000Z",
  "inRange": true,
  "satellite": {
    "name": "SO-50",
    "noradId": "27607",
    "modeType": "FM VOICE"
  },
  "frequencies": {
    "uplinkHz": 435300000,
    "downlinkHz": 145850000,
    "uplinkMode": "FM",
    "downlinkMode": "FM",
    "nominalUplinkKHz": 435300,
    "nominalDownlinkKHz": 145850,
    "isBeaconOnly": false
  },
  "bands": { "tx": "70cm", "rx": "2m" },
  "tracking": {
    "azimuthDeg": 91.7,
    "elevationDeg": 1.9,
    "rangeKm": 2100.5,
    "rangeRateKmPerSec": -4.92,
    "isSunlit": true
  },
  "dopplerStrategy": "full",
  "wispDde": "SO-50 AZ91,7 EL1,9 UP435300000 UFM DN145850000 DFM MA0,0 RR-4,92"
}

When no satellite is in range (or none focused):

{
  "type": "satelliteStatus",
  "version": 1,
  "timestampUtc": "...",
  "inRange": false,
  "satellite": null,
  "wispDde": "** NO SATELLITE **"
}

Scheduled pass alerts

When a pass you marked in the sidebar enters its reminder window (the same moment as the on-screen alert), OscarWatch broadcasts one passAlert frame. It is not repeated, and it is not part of satelliteStatus, so a listener will not retune the radio. The message has no frequencies. Clients that connect later do not receive a backlog.

{
  "type": "passAlert",
  "version": 1,
  "timestampUtc": "2026-10-07T18:25:00.000Z",
  "pass": {
    "satellite": {
      "name": "AO-07",
      "noradId": "07530"
    },
    "aosUtc": "2026-10-07T18:30:00.000Z",
    "losUtc": "2026-10-07T18:42:00.000Z",
    "maxElevationDeg": 24.6,
    "secondsUntilAos": 300
  }
}
  • pass.satellite.name and noradId identify the scheduled spacecraft.
  • aosUtc and losUtc are UTC (ISO-8601). Use them to ignore a duplicate if you reconnect and the reminder has already fired.
  • maxElevationDeg is the pass maximum elevation in degrees.
  • secondsUntilAos is whole seconds until AOS at the moment the alert was sent, never below zero.

QSO logbook messages

When a contact is saved in OscarWatch Logbook, OscarWatch broadcasts a separate JSON frame to all connected clients. These events are independent of satellite status updates and are always sent immediately (not throttled). Clients that connect later do not receive a backlog — only events that occur while they are connected.

qsoLogged

Emitted when a new QSO is added:

{
  "type": "qsoLogged",
  "version": 1,
  "timestampUtc": "2026-07-11T14:30:05.000Z",
  "logbook": {
    "id": 3,
    "name": "Field day",
    "myCallsign": "G0ABC",
    "myGridSquare": "IO91"
  },
  "qso": {
    "id": 42,
    "qsoUtc": "2026-07-11T14:30:00.000Z",
    "call": "DL1ABC",
    "rstSent": "59",
    "rstRcvd": "59",
    "gridSquare": "JO62",
    "name": "",
    "comment": "",
    "satellite": {
      "name": "SO-50",
      "noradId": "27607"
    },
    "frequencies": {
      "uplinkHz": 435300000,
      "downlinkHz": 145850000,
      "uplinkMode": "FM",
      "downlinkMode": "FM"
    },
    "bands": { "tx": "70cm", "rx": "2m" },
    "propMode": "SAT"
  },
  "adif": "<CALL:6>DL1ABC<QSO_DATE:8>20260711<TIME_ON:4>1430<RST_SENT:2>59<RST_RCVD:2>59<GRIDSQUARE:4>JO62<STATION_CALLSIGN:5>G0ABC<MY_GRIDSQUARE:4>IO91<SAT_NAME:5>SO-50<PROP_MODE:3>SAT<FREQ:10>435.300000<BAND:4>70cm<MODE:2>FM<FREQ_RX:10>145.850000<BAND_RX:2>2m<EOR>"
}

qsoUpdated

Same shape as qsoLogged, emitted when an existing QSO is saved after editing.

qsoDeleted

Emitted when a QSO is removed from the logbook:

{
  "type": "qsoDeleted",
  "version": 1,
  "timestampUtc": "2026-07-11T14:35:00.000Z",
  "logbook": {
    "id": 3,
    "name": "Field day",
    "myCallsign": "G0ABC",
    "myGridSquare": "IO91"
  },
  "qso": {
    "id": 42,
    "call": "DL1ABC"
  }
}

QSO field notes

  • logbook, the active logbook at the time of the event.
  • qso.id, stable OscarWatch database ID; use for deduplication across qsoUpdated / qsoDeleted.
  • qso.qsoUtc, contact time in UTC (ISO-8601), matching ADIF QSO_DATE + TIME_ON.
  • qso.satellite.name, LoTW-style name captured at log time (same as ADIF SAT_NAME).
  • qso.satellite.noradId, focused satellite NORAD ID when logged; omitted on edit/delete if unknown.
  • qso.frequencies.uplinkHz / downlinkHz, TX/RX at log time (Hz). Omitted when zero.
  • qso.bands.tx / rx, ADIF band labels (e.g. 70cm, 2m).
  • qso.propMode, always SAT for satellite QSOs in OscarWatch.
  • adif, a single ADIF 3.1 record for the contact (same fields as file export and Cloudlog upload). Record only, no PROGRAMID / <EOH> header; ends at <EOR> with no trailing newline. Present on qsoLogged and qsoUpdated; omitted on qsoDeleted.
  • Empty optional strings are omitted (null absent from JSON) rather than sent as "".

Field notes (satellite status)

  • satellite.name — OscarWatch / LoTW naming (same as Cloudlog sat_name).
  • frequencies.uplinkHz / downlinkHz — radio-corrected Hz (what CAT uses), not raw catalogue nominals.
  • dopplerStrategy — full, downlinkOnly, or uplinkOnly.
  • wispDde — European decimal commas; SatPC32 field order for legacy parsers.
  • Ignore unknown future JSON fields (forward-compatible).

Minimal Python client

import asyncio
import json
import websockets

async def main():
    async with websockets.connect("ws://127.0.0.1:7373/") as ws:
        while True:
            msg = json.loads(await ws.recv())
            kind = msg.get("type")
            if kind == "satelliteStatus":
                if msg.get("inRange"):
                    sat = msg["satellite"]["name"]
                    rx = msg["frequencies"]["downlinkHz"]
                    print(f"{sat} RX {rx} Hz")
                else:
                    print("No satellite")
            elif kind == "passAlert":
                sat = msg["pass"]["satellite"]["name"]
                print(f"{sat} AOS in {msg['pass']['secondsUntilAos']} s")
            elif kind == "qsoLogged":
                qso = msg["qso"]
                print(f"Logged {qso['call']} on {qso['satellite']['name']}")
                if "adif" in msg:
                    print(msg["adif"])
            elif kind == "qsoUpdated":
                print(f"Updated QSO {msg['qso']['id']}")
            elif kind == "qsoDeleted":
                print(f"Deleted QSO {msg['qso']['id']}")

asyncio.run(main())

Minimal C# client

using System.Net.WebSockets;
using System.Text;
using System.Text.Json;

using var ws = new ClientWebSocket();
await ws.ConnectAsync(new Uri("ws://127.0.0.1:7373/"), CancellationToken.None);
var buffer = new byte[8192];
while (ws.State == WebSocketState.Open)
{
    var result = await ws.ReceiveAsync(buffer, CancellationToken.None);
    var json = Encoding.UTF8.GetString(buffer, 0, result.Count);
    using var doc = JsonDocument.Parse(json);
    var root = doc.RootElement;
    switch (root.GetProperty("type").GetString())
    {
        case "satelliteStatus" when root.GetProperty("inRange").GetBoolean():
            Console.WriteLine(root.GetProperty("satellite").GetProperty("name").GetString());
            break;
        case "passAlert":
            Console.WriteLine(root.GetProperty("pass").GetProperty("satellite").GetProperty("name").GetString());
            break;
        case "qsoLogged":
            Console.WriteLine($"Logged {root.GetProperty("qso").GetProperty("call").GetString()}");
            break;
    }
}

Troubleshooting

  • Windows Firewall — when LAN access is first enabled, Windows should prompt to allow OscarWatch. Choose Allow access on private networks. If you previously blocked it, open Windows Security → Firewall & network protection → Allow an app through firewall and enable OscarWatch for Private.
  • Port in use — another program (or a second OscarWatch instance) may already use the port. Change the port or stop the other program. Use Test port in Settings.
  • LAN clients cannot connect — enable Allow connections from local network, allow the port in Windows Firewall, and use this PC’s LAN IP address.
  • Always ** NO SATELLITE ** — check Only broadcast when satellite is above horizon, focus a satellite on the map, and ensure a transponder mode is selected in the frequency panel.
  • No clients shown in status bar — the server can be waiting (amber) with zero clients; connect your logging app and the label should turn green.
OscarWatch
OscarWatch
Toggle sidebar