Web publishing: SatMe-PC pushes to your site
SatMe-PC can push items to an outside web site: SSTV and SSDV pictures, planches, event reports, contacts, the journal's videos, FT8 / FT4 passes, and as an option live data (auto-pilot, QO-100 spectrum, DATV picture, station, FT8 / FT4), your favourite satellites and your awards. Your site receives them as it likes — a PHP file on shared hosting, a WordPress plugin, a small Python or Node server, anything that can answer an HTTP request. This page covers the setting in SatMe-PC and the wire format (API v1), stable and versioned, with ready-to-use receivers.
With no server at all, SatMe-PC can also write into a folder (see No server: a folder).
SatMe-PC always does the sending: it works from behind a home router, and nothing is opened on the station (the site never connects to the PC).
Contents: Setup · API v1 · Types · Responses · Queue and retries · Live data · Security · Examples · Displaying · Folder
The settings' names below are the French ones shown by SatMe-PC.
Setup in SatMe-PC#
- Réglages › Modules: tick Publication web. The module is off by default and no profile switches it on: while it is unticked, SatMe-PC sends nothing to anyone (no queue, no test, no live data) and shows no 🌐 button.
- Réglages › Services en ligne › Envoyer vers un site web:
- Où envoyer (where): Adresse web (HTTPS) or Dossier sur ce PC (a folder).
- The address that receives. Two ways:
- with
{type}in it, replaced by each request's type:https://example.org/api/satme/{type}→…/api/satme/sstv-image,…/api/satme/ping; - without
{type}: everything goes to that one address (webhook style), the type in theX-SatMe-Typeheader (and the form'stypefield):https://example.org/satme/receive.php.
- with
- The token (optional, advised): a secret you make up (for instance
openssl rand -hex 32) and your site checks; sent asAuthorization: Bearer <token>. It stays on this PC and is never shown again. - Tester (test): SatMe-PC sends a
ping(nothing is published). Sending is ready only after a test that passes; another address, token or folder is tested again. - Quoi envoyer (what to send): each type Sur clic (at a click: the 🌐 button of each item), Automatique (as it comes) or Jamais (never). Everything starts at « Sur clic »: nothing goes by itself until you choose « Automatique ».
- The 🌐 buttons appear: SSTV viewer, log lines, Planche page (planche and report), videos made by the journal's editor, SSDV pictures (Télémesure › Images SSDV), the passes of the « Passages FT8/FT4 sur le site » card (FT8/FT4 tab), and Envoyer le diplôme reçu (send the award received; Agenda › Diplômes).
http:// (no S) is accepted only towards this machine or the local network (127.0.0.1, 192.168.…, 10.…, 172.16–31.…, ….local, a name without a dot); elsewhere https:// is required. Redirects are not followed: give the final address.
API v1#
Every request is one POST to the address set.
Headers, always#
| Header | Example | Meaning |
|---|---|---|
User-Agent | SatMe-PC/2026.10.34 (+https://github.com/f4ioz/SatMe) | the software and its version |
X-SatMe-Version | 1 | the format's version; a receiver may refuse a version it does not know |
X-SatMe-Type | sstv-image | the request's type (below) |
X-SatMe-Id | 0b6f7c1e-3d0a-4c9e-9a51-6f4f0c2b8e21 | unique id of this delivery: the same from one try to the next (see idempotency) |
Authorization | Bearer 3f9a… | only when a token is set |
Content-Type | multipart/form-data; boundary=… or application/json; charset=utf-8 | depending on the type |
The version changes only if a receiver written for v1 would stop working: adding a type or a field does not change it. Ignore the fields you do not know.
Media: multipart/form-data#
The types sstv-image, ssdv-image, planche, rapport, qso, journal-video (and the live pictures) arrive like an HTML file-upload form, three fields in this order:
| Field | Content |
|---|---|
type | the type, as text (sstv-image) — the same as X-SatMe-Type |
meta | a JSON (application/json): the words that go with it (below) |
fichier | the file, with its name (filename) and its Content-Type |
In PHP: $_POST['type'], json_decode($_POST['meta'], true), $_FILES['fichier']. In Python: any multipart reader.
The meta JSON#
Always there:
| Field | Type | Example | Meaning |
|---|---|---|---|
version | number | 1 | format version |
type | string | "sstv-image" | the type |
id | string | "0b6f7c1e-…" | the same as X-SatMe-Id |
cle | string | "sstv:ISS_20261003_135119.png" | the item on SatMe-PC's side: a new version of the same item (a planche redone) has the same cle and replaces the old one |
titre | string | "ISS · PD120" | a title to show |
fichier.nom | string | "ISS_20261003_135119.png" | suggested name (never use it as is as a file name on the server) |
fichier.type | string | "image/png" | MIME type |
fichier.taille | number | 48213 | bytes |
fichier.sha256 | string | "9f86d0…" | SHA-256 (hex) of the file |
Depending on the type, when SatMe-PC knows them:
| Field | Type | Example | For |
|---|---|---|---|
satellite | string | "ISS" | sstv-image, qso, journal-video |
utc | ISO 8601 string | "2026-10-03T13:51:19Z" | all (for a planche or a report: the time it was sent) |
mode | string | "PD120", "FM" | sstv-image, qso |
station | string | "F4XYZ · JN18FS" | your call sign and locator |
evenement | string | "ARISS octobre 2026" | sstv-image (campaign), planche, rapport |
description | string | "Diapo 3/12" | sstv-image |
incomplete | boolean | true | sstv-image partly received |
indicatif | string | "EA4ABC" | qso: the station worked |
locator | string | "IN80DD" | qso: its locator |
SSTV: passes, auto-pilot, planches (optional)#
All optional: an older SatMe-PC does not send them, and a receiver may ignore them. Times are ISO 8601 UTC strings. A pass id is the satellite's NORAD number and its AOS to the minute ("25544-20261003T1349Z"): the same pass has the same id in every item that names it, as long as it is worked out from the same elements (when in doubt, compare the satellite and the times). A key ("sstv:…", "journal:…", "rapport:…", "planche:…") is another item's cle: it may name an item not published (yet) on your site.
sstv-image and journal-video:
| Field | Type | Example | Meaning |
|---|---|---|---|
catnum | number | 25544 | the satellite's NORAD number |
passage | string | "25544-20261003T1349Z" | the pass it came on (pass id) |
aos, los | string | "2026-10-03T13:49:30Z" | that pass's AOS and LOS |
elMax | number | 62.4 | its highest elevation (degrees) |
autopilote | boolean | true | received by the SSTV auto-pilot (Agenda › SSTV) |
campagne | string | "ARISS série 34" | the auto-pilot's campaign that received it |
diapo, diapos | number | 3, 12 | sstv-image: its slide, set by hand (3 of 12) |
planches | list of keys | ["planche:serie34"] | journal-video: the planches of the event it belongs to |
planche:
| Field | Type | Example | Meaning |
|---|---|---|---|
satellite, catnum | string, number | "ISS", 25544 | the satellite of its event (else of most of its pictures) |
evenementId | string | "c:ariss-34" | its event's id (stable) |
evenementGenre | string | "campagne" | campagne (an auto-pilot campaign), activite (an activity announced), images (a run of pictures) |
evenementDebut, evenementFin | string | the event's period | |
campagne, campagneId | string | "ARISS série 34", "ariss-34" | when the event is an auto-pilot campaign |
mode | string | "PD120" | the most frequent SSTV mode of its pictures |
premiere, derniere | string | the first and the last picture | |
images | number | 23 | pictures of the event |
diapos | number | 12 | slides expected |
diaposRecues | number | 10 | slides received |
diaposManquantes | list of numbers | [5, 6] | the slides missing |
meilleures | list | see below | the best picture of each slide received |
passages | list | see below | the passes that brought pictures, oldest first |
autopilote | boolean | true | at least one of those passes was received by the auto-pilot |
videos | list of keys | ["journal:SatMe_ISS_…mp4"] | the journal's videos of those passes |
rapport | key | "rapport:serie34" | its event report |
meilleures[]: {"diapo": 3, "image": "sstv:<file>.png", "utc": "…", "note": 82, "copies": 4, "estimee": true} (note: SatMe's mark 0–100 when known; estimee: the slide number was estimated from the timetable). passages[]: {"id": "25544-20261003T1349Z", "aos": "…", "los": "…", "elMax": 62.4, "images": 5, "diapos": [3, 4], "autopilote": true, "campagne": "ARISS série 34", "video": "journal:<file>.mp4"}.
ssdv-image (an SSDV picture: numbered packets of a JPEG, received over one pass or several — SO-127 and other satellites):
| Field | Type | Example | Meaning |
|---|---|---|---|
satellite, catnum | string, number | "HADES-SA", 68446 | the satellite heard, its NORAD number |
utc, premier | string | "2026-10-30T09:12:04Z" | its first packet received (both the same) |
dernier | string | its last packet received so far | |
image | number | 17 | the picture's number on board (0–255) |
indicatif | string | "Q0SAT" | the SSDV callsign field, as decoded (some satellites put fixed bytes there) |
paquetsRecus | number | 38 | packets received |
paquetsAttendus | number | 40 | packets in the picture |
attendusEstime | boolean | true | paquetsAttendus is an estimate (the last packet has not come yet) |
manquants | number | 2 | packets missing |
complete | boolean | false | every packet received |
largeur, hauteur | number | 320, 240 | pixels |
passages | list | see below | each pass (or recording, or import) that brought packets, oldest first |
autopilote | boolean | true | at least one of those passes was received by the auto-pilot |
campagne | string | "SO-127 octobre" | the auto-pilot's campaign (the latest) |
passages[]: {"id": "68446-20261030T0910Z", "aos": "…", "los": "…", "elMax": 48.2, "paquets": 21, "premier": "…", "dernier": "…", "source": "radio", "autopilote": true, "campagne": "SO-127 octobre"} (id, aos, los, elMax when the pass is known; source: radio, or the recording or file the packets were read from).
An SSDV picture grows: missing blocks are grey in the JPEG sent. When more packets arrive (later in the pass, or on another pass), SatMe-PC sends it again with the same cle ("ssdv:<key>"), a minute after its last packet or as soon as it is complete — in Automatique, and in Sur clic once the owner has sent it by hand. Replace the earlier version.
rapport: planche (the planche's key), satellite, catnum, evenementId, evenementGenre, campagne, campagneId, premiere, derniere, as above.
Lists and objects are small (a series has a few dozen passes at most); a receiver that keeps only flat fields loses nothing else.
A whole example (sstv-image):
POST /api/satme/sstv-image HTTP/1.1
Host: example.org
User-Agent: SatMe-PC/2026.10.34 (+https://github.com/f4ioz/SatMe)
X-SatMe-Version: 1
X-SatMe-Type: sstv-image
X-SatMe-Id: 0b6f7c1e-3d0a-4c9e-9a51-6f4f0c2b8e21
Authorization: Bearer 3f9a…
Content-Type: multipart/form-data; boundary=SatMe-5c1e…
--SatMe-5c1e…
Content-Disposition: form-data; name="type"
Content-Type: text/plain; charset=utf-8
sstv-image
--SatMe-5c1e…
Content-Disposition: form-data; name="meta"
Content-Type: application/json; charset=utf-8
{"satellite":"ISS","utc":"2026-10-03T13:51:19Z","mode":"PD120","station":"F4XYZ · JN18FS","evenement":"ARISS octobre 2026","titre":"ISS · PD120","version":1,"type":"sstv-image","id":"0b6f7c1e-3d0a-4c9e-9a51-6f4f0c2b8e21","cle":"sstv:ISS_20261003_135119.png","fichier":{"nom":"ISS_20261003_135119.png","type":"image/png","taille":48213,"sha256":"9f86d0…"}}
--SatMe-5c1e…
Content-Disposition: form-data; name="fichier"; filename="ISS_20261003_135119.png"
Content-Type: image/png
<the PNG's bytes>
--SatMe-5c1e…--Data only: application/json#
ping, retrait and the live data are a JSON, which carries type and version too.
Types#
| Type | Body | File | Sent |
|---|---|---|---|
ping | JSON | — | by Tester |
sstv-image | multipart | PNG | an SSTV picture of the gallery (at a click, or each picture received live in Automatique) |
ssdv-image | multipart | JPEG | an SSDV picture as it stands (at a click, or in Automatique), sent again as more packets come (same cle) |
planche | multipart | JPEG (1600 px) | a planche; in Automatique, those with pictures, 2 min after the last picture received |
rapport | multipart | a planche's event report | |
qso | multipart | ADIF (.adi, one record) | a contact of the log; in Automatique, each contact added |
journal-video | multipart | MP4 | a video made by the journal's editor (can be hundreds of MB) |
retrait | JSON | — | withdraw an item (no button in SatMe-PC yet; part of the API) |
direct, direct-spectre, direct-datv-image, direct-sstv-image, direct-station, direct-efface | JSON or multipart | JPEG | live data, only when ticked |
favoris | JSON | — | the favourite satellites, only when ticked |
diplomes | JSON | — | the awards followed and your progress, only when ticked |
diplome-image | multipart | PNG, JPEG, PDF | the picture of an award received, only on a click |
direct-ft8 | JSON | — | each FT8 / FT4 slot decoded, only when ticked |
ft8-passage | multipart | JSON | a journal's pass with its FT8 / FT4 messages (at a click, or each pass in Automatique) |
ping: {"type":"ping","version":1,"logiciel":"SatMe-PC/…","types":["sstv-image","planche",…]} (types: those not set to « Jamais »). The receiver answers any 2xx. If it answers a JSON, SatMe-PC optionally reads nom or indicatif and site (shown: « ok : site de F4XYZ (https://…) »), and refuses (a list of types the site does not take, named on the card).
retrait: {"type":"retrait","version":1,"typeRetire":"sstv-image","cle":"sstv:…","id":"<the id the site gave back when publishing, or empty>"}. 2xx or 404: withdrawn.
Idempotency#
X-SatMe-Ididentifies a delivery: if it comes again (the network dropped after you stored it, before SatMe-PC read the answer), it is the same one. Simplest: if you already have an item with this id, answer200without storing it again.cleidentifies the item: the same item sent again later (a planche redone) has a newX-SatMe-Idbut the samecle; replace the old one.
Responses#
| Response | SatMe-PC does |
|---|---|
| 2xx | accepted. Optionally a JSON {"id": "…", "url": "…", "deja": true}: the id is sent back in a retrait; deja shows « déjà en ligne » (already online) |
| 401 | token refused: everything stops, the item stays queued, the card asks to test the connection again |
| 403 | refused (this type is not taken by the site): the item leaves the queue, the refusal is noted on the card (with {"erreur": "…"} if given) |
| 413 | too large: leaves the queue, noted |
| 3xx and other 4xx (400, 404, 415…) | final refusal: leaves the queue, noted |
| 408, 429, 5xx, no answer | tried again later (see the queue) |
An {"erreur": "…"} in the response is shown as is on the card: write it for a human.
Queue and retries#
Nothing goes from the page itself: each item is first copied into a queue on disk (SatMe-PC's data folder, site/: file.json and envois/; a video is read where it is). A sender thread wakes every 5 seconds and sends what is due, one at a time, in order.
- No answer, 408, 429 or 5xx: tried again after 30 s, 1 min, 2 min, 4 min, 8 min, 16 min, 32 min, then every hour, with the same
X-SatMe-Id; each item keeps its own pace. - The queue survives a restart; 300 items at most (the oldest go).
- The same item asked again while it waits: only the new version stays.
- On the card: what waits, the last try, the last answers, Réessayer maintenant (retry now), Vider la file (empty the queue).
Live data (optional)#
Under En direct, each part is ticked on its own (nothing by default). No queue: what the site does not take is dropped, and the next try waits (30 s, 1 min… 5 min; 2 min after a 403, 10 min after a 401). Only names, times, measurements, a spectrum and pictures — never an address, a pairing code, a password or a key.
| Type | Body | Pace |
|---|---|---|
direct | JSON {"type","version","t": <UTC ms>, "pilote": {…} \| null} or {…, "datv": {…} \| null}: each part present replaces the previous one, null clears it | Auto-pilot: 10 s (once a minute when unchanged); DATV: 3 to 30 s |
direct-spectre | JSON {"type","version","centreHz","largeurHz","minDb","maxDb","bins"}: bins = 1024 bytes in base64, 0 = minDb, 255 = maxDb | 1 to 10 s, while the SDR is on QO-100 |
direct-datv-image | multipart, fichier = JPEG ≤ 190 KB, 640 px | while the DATV receiver is locked |
direct-sstv-image | multipart, fichier = JPEG | every 5 s during an auto-pilot pass, when the picture changed |
direct-efface | JSON {"type","version","t"} | the Effacer le direct button: forget all live data |
pilote: {"maitre": bool, "prochain": {"campagne","sat","aos","los","el"}, "enCours": {…, "phase","mode","images","elMaintenant","azMaintenant","image": {"phase","progression","mode","prochaine"}}, "derniere": {"sat","utc","mode"}} (times in UTC ms). datv: {"verrouille","cielKhz","srKs","service","fournisseur","modcod","mer","marge"}.
A receiver that does not want live data answers 403 (or 404) to these types.
Favourite satellites#
Under En direct, the Satellites favoris line (unticked by default) sends the list of the satellites marked ★ in SatMe-PC, for instance for a home page to feature them. It goes as soon as the list changes, otherwise at most once an hour, and only while the module is on and the target tested.
{"type":"favoris","version":1,"satellites":[{"nom":"ISS","norad":25544},{"nom":"SO-50","norad":27607}],"t":1798000000000}satellites is the whole list (sorted by name; empty when there is no favourite left): it replaces the previous one. t: when it was sent, UTC ms. To a folder: favoris.json at the root, same content.
A receiver that does not know this type answers 400, 404 or 415: SatMe-PC shows « non pris en charge par le site », without an error, and tries again only the next day (or when the line is ticked again).
Awards#
Under En direct, the Diplômes line (unticked by default) sends the awards followed in Agenda › Diplômes (which must be active), for instance for an « Awards » page on your site: as soon as they change (looked at once a minute), otherwise once a day, only when the module is on and the target tested.
{"type":"diplomes","version":1,"t":1798000000000,"diplomes":[
{"id":"cat:vucc","nom":"VUCC Satellite","emetteur":"ARRL","sorte":"permanent","statut":"en_cours",
"libelle":"En cours : 37/100 locators","progression":{"actuel":37,"objectif":100,"unite":"locators"},
"reglement":"https://www.arrl.org/vucc"},
{"id":"cat:amsat-scc","nom":"AMSAT Satellite Communicators Club","emetteur":"AMSAT","sorte":"permanent",
"statut":"recu","recu":1790000000000,"progression":{"actuel":1,"objectif":1,"unite":"contacts"},"image":true},
{"id":"pzk:75-2","nom":"ARISS SSTV …","emetteur":"ARISS (géré par le PZK)","sorte":"evenement","statut":"a_venir",
"debut":1798100000000,"fin":1798300000000,"limite":1800500000000,"satellite":"ISS","mode":"PD120",
"reglement":"https://ariss.pzk.org.pl/sstv/"}]}diplomes is the whole list and replaces the previous one. Per award: id (stable), nom (name), emetteur (issuer), sorte (permanent: counted from the log; evenement: pictures received during a window), statut (en_cours in progress, obtenable goal reached, to ask for, demande asked, recu received, a_venir to come, pendant the event is on), libelle (SatMe's sentence, in French), progression (actuel, objectif, unite; standing awards), debut, fin, limite (deadline to ask), demande, recu (UTC ms), reglement (rules, https), satellite and mode (events), image (true once the received award's picture was sent). Ignore an unknown field; a missing one has no value.
Never sent: what was counted (locators, entities, states, callsigns worked), the pictures' names, the notes, the name and e-mail of « Pour les demandes », the forms, and the awards hidden, missed, expired or « to check ».
The picture of an award received (diplome-image) goes only on a click: in Agenda › Diplômes, Envoyer le diplôme reçu on an award whose picture is attached (the button shows only when web publishing is ready). Multipart like the media, meta: {"diplome": "<id>", …}, the file: the picture as kept (PNG or JPEG; made a JPEG of 3000 px at most when over 8 MB) or a PDF of 8 MB at most. No queue: when the site does not answer, the button says so; click again.
To a folder: diplomes.json at the root (same content) and diplomes/<id>.png|jpg|pdf. A receiver that does not know diplomes answers 400, 404 or 415: left alone until the next day, without an error.
The station#
Under En direct, the Station line (unticked by default) sends the station as it is now, for a live page to draw the satellite, the antennas and the receiving chain beside the pictures: type direct-station, JSON, about 1 KB. It goes when something changed, at most every 2 s (2 to 10 s, chosen on the line), and every 30 s when nothing changed — so a receiver can take a silence of a minute or two as « station off line ».
{"type":"direct-station","version":1,"t":1798000000000,
"satellite":{"nom":"ISS","norad":25544,"geo":false,"az":212.4,"el":31.6,"distanceKm":812,"vitesseKmS":-5.84,
"aos":1797999700000,"los":1798000300000,"elMax":61.3,"choisi":"Mode V/U FM",
"emetteurs":[{"description":"Mode V/U FM","mode":"FM","descenteHz":437800000,"monteeHz":145990000}]},
"antennes":{"rotor":{"connecte":true,"az":209.8,"el":30.9,"cibleAz":212.4,"cibleEl":31.6,"enMouvement":true,"horsPortee":false,"azSeul":false},
"parabole":{"az":150.0,"el":29.7}},
"reception":{"sdr":{"actif":true,"type":"RTL-SDR","frequenceHz":437800000},
"datv":{"actif":true,"verrouille":true,"mer":7.4,"marge":2.1,"service":"F4XYZ","cielKhz":10494750},
"poste":{"modele":"IC-9700","connecte":true,"descenteHz":437800000,"monteeHz":145990000,"mode":"FM"}},
"enEmission":false,"mode":"phonie"}| Key | Meaning |
|---|---|
satellite | the satellite followed, or QO-100 while the station works it (SDR or DATV receiver on it) and the one followed is below the horizon; geo: geostationary (then no aos/los); az/el (°), distanceKm, vitesseKmS (+ moving away) from the station; aos, los, elMax: the pass under way or the next one; emetteurs: up to 6 of its transmitters (SatNOGS' public list), choisi the one in use |
antennes.rotor | only when a rotor is set: az/el as the controller reads them back, cibleAz/cibleEl where it is sent, enMouvement, horsPortee (beyond its range), azSeul (no elevation) |
antennes.parabole | a geostationary satellite: the fixed dish's pointing from the station's locator |
reception.sdr | the SDR: on or off, its kind (RTL-SDR, ADALM-PLUTO, bladeRF, SatMe-Node, FI du poste, …), the sky frequency heard |
reception.datv | the DATV receiver: on, locked; while locked, mer and marge (dB), service |
reception.poste | only while the rig is connected: its model's name, its sky frequencies and mode as read back |
enEmission | something transmits now (yes or no, nothing more) |
mode | what the station does, when known: DATV, FT8, FT4, SSTV, CW, phonie, numérique |
Every key is optional except t and enEmission. Never in it: an address, a port, a serial device, a file path, a pairing code, a token, another setting. To a folder: direct/station.json, same content. A receiver that does not know the type answers 400, 404 or 415: SatMe-PC shows « non pris en charge par le site », without an error, and tries again ten minutes later.
The settings call, for a script (POST /api/site/direct, the same as the line's box), parameters in the query string: POST /api/site/direct?on_station=1 (or 0), &periode_station=2 (2 to 10 s).
FT8 / FT4#
Under En direct, the FT8/FT4 line (unticked by default) sends, while FT8 / FT4 listening is on, each slot once decoded (FT8 15 s, FT4 7.5 s; about 9 s, resp. 4.5 s, after its end, the decoder's time): type direct-ft8, JSON, 1 to 10 KB. Each slot goes once, empty ones too (so a live waterfall goes on); what the site does not take is dropped.
{"type":"direct-ft8","version":1,"t":1798000005000,"mode":"FT8","duree":15000,
"satellite":{"nom":"RS-44","norad":44909,"geo":false,"aos":1797999700000,"los":1798000600000,"elMax":54.4},
"dialHz":435640000,"transpondeur":"Mode V/U linear",
"decodes":[{"df":1210,"dt":0.3,"snr":-8,"msg":"CQ EA4XYZ IM79","de":"EA4XYZ","grid":"IM79","cq":true,"toMe":false},
{"df":1530,"dt":0.2,"snr":-12,"msg":"F4IOZ DL1ABC JO62","de":"DL1ABC","grid":"JO62","cq":false,"toMe":true}],
"n":2,
"cascade":{"basHz":200,"hautHz":3000,"bins":256,"lignes":8,"data":"<base64, 2048 bytes>"}}| Key | Meaning |
|---|---|
t, mode, duree | the slot's start (UTC ms), FT8 or FT4, its length (ms) |
satellite | the pass under way (from a minute before AOS to a minute after LOS), or QO-100 at the rig (geo, no aos/los); absent between passes |
bande | without a satellite, the band of the dial (2 m, 70 cm, 20 m…), when known |
dialHz, transpondeur | the dial (sky Hz) as read from the rig; the satellite's transmitter chosen |
decodes | at most 60, the strongest, sorted by audio frequency: df audio offset (Hz), dt time offset (s), snr report (dB), msg the message (FT8's characters, 40 at most), de the callsign that sent it (when it can be told), grid the locator it gives (4 characters), cq a CQ, toMe addressed to the station's callsign |
n | how many messages the slot had (before the cap of 60) |
cascade | when the waterfall ran: lignes rows of bins bytes (0 quiet … 255 loud), oldest row first, from basHz to hautHz across |
Only messages heard over the air: no setting, address, path, sound card or anything else of the station. To a folder: direct/ft8.json (the last slot). A receiver that does not know the type answers 400, 404 or 415: left alone ten minutes, without an error. The settings call: POST /api/site/direct?on_ft8=1 (or 0).
A whole pass (ft8-passage, a kind of the queue: Sur clic by default, Automatique sends each pass of the journal when it ends, if it has messages): the 🌐 of the FT8 tab's card « Passages FT8/FT4 sur le site » (SatMe-PC lists the journal's last passes with messages). Multipart like the media: meta (titre, satellite, utc = AOS, mode, stations, carres, cle = ft8:<catnum>_<AOS ms>) and fichier, a JSON (application/json, up to a few hundred KB):
{"version":1,"satellite":{"nom":"RS-44","norad":44909},"aos":1797999700000,"los":1798000600000,"elMax":54.4,
"transpondeur":"Mode V/U linear","dialHz":435640000,"modes":["FT4"],
"tranches":[{"t":1797999707500,"mode":"FT4","decodes":[…],"n":3,"cascade":{…}}, …],
"decodes":148,"stations":23,"carres":17,"dxcc":9}tranches: each slot with messages (480 at most), its decodes as above (60 at most), cascade only for the slots SatMe-PC still had in memory (the last three hours). stations, carres, dxcc: how many callsigns (not the station's own), locators and DXCC entities were heard.
Security#
- HTTPS on the Internet (SatMe-PC refuses
http://outside the local network). - A token, long and random, compared in constant time (
hash_equalsin PHP,hmac.compare_digestin Python). Never put it in a public page or a repository. - Check the type and the size: an allow-list of types and extensions, a maximum size per type; refuse the rest (
403,413,415). - Never trust the file name: name files yourself (for instance after
X-SatMe-Id), with no..or/. - Store outside the web root when you can, and serve the files through a script or a rule that does not execute them. Otherwise, forbid execution in the receiving folder (Apache:
php_flag engine off/RemoveHandler .php .phtml/Options -ExecCGIin an.htaccess, or betterRequire all deniedand a script serving the files). Never execute an uploaded file. - Escape every text of
metawhen displaying it (it is free text). - Rate-limit if your host allows it.
Ready-to-use receivers#
In docs/exemples/publication-web/ (GPL-2.0-or-later, like the rest of the repository: copy them, adapt them):
recevoir.php— a single PHP file (7.4+) for shared hosting: checks the token and the type, stores the media and its JSON card in a folder, keepsindex.jsonup to date, answers JSON. SetTOKENandDIRat the top; in SatMe-PC, the addresshttps://your-site/recevoir.php(no{type}). For videos, PHP'supload_max_filesizeandpost_max_sizemust follow.recevoir.py— Python 3, standard library only: the same, plusretraitand the live data (direct/direct.json, the pictures). It also serves the folder read-only (GET):
Address in SatMe-PC:SATME_JETON=$(openssl rand -hex 32) python3 recevoir.py --dossier ./satme-donnees --port 8765http://127.0.0.1:8765/satme/{type}on the same machine (--ecoute 0.0.0.0for the local network). On the Internet, put it behind an HTTPS server (nginx, Caddy).index.html— a plain HTML and JavaScript page that readsindex.jsonand shows the pictures, videos, documents and contacts.
All three share the layout of the folder target: a page made for one works with the others.
Displaying what was received#
Everything is in the receiving folder:
satme-donnees/
├── index.json the latest items, newest first
├── sstv-image/2026/0b6f7c1e-….png the file
├── sstv-image/2026/0b6f7c1e-….json its card: the meta JSON + "chemin" + "ajoute"
├── qso/2026/….adi + ….json
├── favoris.json the favourite satellites (when ticked)
├── diplomes.json, diplomes/… the awards (when ticked) and the pictures sent with a click
└── direct/direct.json, station.json, datv.jpg, sstv.jpg (live data, rewritten in place)index.json:
{
"version": 1,
"maj": "2026-10-03T13:52:02Z",
"elements": [
{ "type": "sstv-image", "id": "0b6f7c1e-…", "cle": "sstv:ISS_20261003_135119.png",
"titre": "ISS · PD120", "satellite": "ISS", "utc": "2026-10-03T13:51:19Z", "mode": "PD120",
"chemin": "sstv-image/2026/0b6f7c1e-….png", "ajoute": "2026-10-03T13:52:02Z",
"fichier": { "nom": "ISS_20261003_135119.png", "type": "image/png", "taille": 48213, "sha256": "9f86…" } }
]
}- A static page: copy
index.htmlbesideindex.json, or write your own (onefetch('index.json'), one loop). - WordPress, a CMS: read
index.json(or the cards) from a template or a plugin. - A static site generator: Hugo reads JSON as data (
resources.Get/transform.Unmarshal), Jekyll through_data/.
No server: a folder#
Choose Dossier sur ce PC at step 1 and give a full path (/home/me/Nextcloud/satme, C:\Users\me\Sync\satme). Tester checks it can be written. SatMe-PC then writes each item sent there, with the same queue, the same Sur clic / Automatique / Jamais choices and the same cards as the API:
<folder>/<type>/<YYYY>/<id>.<ext>and<id>.json(the API'smeta, pluscheminandajoute); the year comes fromutc;index.json: the last 500, newest first (a new version of the same item replaces the old one, files included);flux.xml: an Atom feed of the last 50 (links relative to the folder);direct/direct.json(rewritten in place),direct/station.json,direct/datv.jpg,direct/sstv.jpgwhen live data is ticked — no spectrum to a folder;direct/ft8.json(the last slot) when FT8/FT4 is ticked; a pass (ft8-passage) like any item,ft8-passage/<year>/<id>.data.json(its card<id>.json);favoris.jsonwhen Satellites favoris is ticked;diplomes.jsonwhen Diplômes is ticked,diplomes/<id>.<ext>for each picture sent with Envoyer le diplôme reçu.
Each file is written beside its place then renamed (atomic where the system allows): a reader never sees half a file, and the index never names a file not yet there.
What for:
- a static site (Hugo, Jekyll, HTML): the folder inside the site, then your usual publishing;
- a sync tool you already have: Nextcloud, Syncthing, an rsync or FTP mirror (
lftp mirror -R), a shared folder. SatMe-PC never does FTP itself.
The folder is not available in the demonstration.