Verified 25-byte Whole-Orbit (WO) record format for Nayif-1 (and JY1SAT/FUNCube-1-family
payloads), reverse-engineered from the official FUNcube Nayif-1 Dashboard
(Nayif-1_Dashboard_1040_Installer.msi, decompiled WholeOrbitSample.cs,
WholeOrbitDataStore.cs, HighResolutionSample.cs, HighResolutionDataStore.cs,
Telemetry.cs, BKSystem.IO/BitStream.cs) and validated against 70,368 real records
(733 complete orbits) from SatNOGS-archived frames.
See also: daniestevez/gr-satellites#845
nayif1.ksy- Kaitai Struct definition (realtime block + whole-orbit record + frame + full orbit). Compiles with kaitai-struct-compiler 0.11; 0 mismatches vs the Python decoder on 32,736 records.nayif1_wo_decoder.py- pure-Python decoder (parse_nayif1_realtime,parse_nayif1_wholeorbit,reassemble_wholeorbit,decode_wholeorbit).wo_sample.csv- first 20 decoded records (cycle 677988) to show the output.
| bits | width | field | conversion |
|---|---|---|---|
| 0-11 | 12 | WOTempMcu | v=raw*0.00078753; v>=0.7012 ? 25-(v-0.7012)/0.001646 : 25-(v-0.7012)/0.001749 |
| 12-23 | 12 | WOTempRf | C = -0.3465*(raw&0x3FF)+266.70646 (top 2 bits const "11") |
| 24-35 | 12 | WOTempPa | piecewise-linear 31-anchor table on (raw&0x3FF) |
| 36-45 | 10 | WOPanelTempXp | C = -0.208*raw+158.792 |
| 46-55 | 10 | WOPanelTempXm | C = -0.208*raw+158.792 |
| 56-65 | 10 | WOPanelTempYp | C = -0.208*raw+158.792 |
| 66-75 | 10 | WOPanelTempYm | C = -0.208*raw+158.792 |
| 76-85 | 10 | WOPanelTempZp | C = -0.208*raw+158.792 |
| 86-95 | 10 | WOPanelTempZm | C = -0.208*raw+158.792 |
| 96-155 | 10x6 | WOSunSensorXp/Xm/Yp/Ym/Zp/Zm | raw |
| 156-163 | 8 | WOBatteryTemp | raw |
| 164-173 | 10 | WOPanelCurrent | raw (= RT totalphotocurrent) |
| 174-187 | 14 | WOBatteryVoltage | raw (mV) |
| 188-199 | 12 | WOSystemCurrent | raw (= RT systemcurrent) |
A cycle is the 12 WO frames (ft 0-11) sharing one 24-bit in-frame sequence number
(bytes 50-52). Each WO frame carries 8 x 25-byte records in its 200-byte payload.
sampleNumber = 96 - frame_id*8 - sample_in_frame; WO1 holds the oldest samples (96..89),
WO12 the freshest (8..1). Dashboard time anchor: sample_time = receive_time - sampleNumber min - frame_id*5 s.
Across 341 complete cycles (all 24 frame types present), the freshest WO record matches the realtime block (bytes 2-56 of every frame) 100% on battery voltage, battery temp, panel current, all 6 sun sensors; (WO temp_rf & 0x3FF)>>2 == RT rftemp; (WO temp_pa & 0x3FF)>>2 == RT paboardtemp; WO system_current == RT systemcurrent.
The official Dashboard decodes 132-bit samples: 6x10-bit sun sensors, then per axis a 22-bit signed magnetometer (x4), plus iMTQ IVA / coil-active / error flags, packed continuously across the five 200-byte HR frames (samples wrap across frame boundaries).