ENTROPY is a real-time Hardware-in-the-Loop and Software-in-the-Loop telemetry injector for orbital-environment testing.
It generates a deterministic low Earth orbit telemetry stream at 100 Hz, sends it over UDP, exposes it through a TypeScript backend and WebSocket dashboard, and can optionally bridge data to hardware-oriented interfaces such as CAN, UART, SPI and I2C wrappers.
The project is designed for lab testing firmware, telemetry pipelines, dashboards, fault handling and repeatable debugging before real sensors or a complete test bench are available.
ENTROPY is useful when you need a controlled telemetry source that behaves like a spacecraft or orbital sensor environment.
Typical uses:
- Firmware-in-the-loop testing for embedded controllers.
- Hardware-in-the-loop telemetry injection into lab systems.
- Backend telemetry ingestion testing over UDP.
- WebSocket dashboard testing with live values.
- Replayable debugging with deterministic binary recordings.
- Fault-tolerance tests using anomaly commands and SEU event injection.
- Real-time loop and jitter evaluation on Linux.
- Sensor-bus integration experiments through CAN, serial, SPI and I2C wrappers.
ENTROPY is not a full orbital mechanics suite like GMAT, STK or Orekit. It is a deterministic telemetry and environment injector focused on integration testing, real-time behavior and system validation.
entropy/
core/
CMakeLists.txt
src/
main.cpp
simulator.hpp
bus/
shared_bus.hpp
udp_sender.hpp
command_receiver.hpp
replay.hpp
can_bus.hpp
serial_bus.hpp
spi_i2c.hpp
physics/
physics_engine.hpp
atmosphere.hpp
thermal.hpp
imu.hpp
magnetic.hpp
seu.hpp
nasa_constants.hpp
rt/
rt_loop.hpp
jitter_logger.hpp
watchdog.hpp
security/
hmac.hpp
cmd_auth.hpp
tests/
backend/
package.json
tsconfig.json
src/
index.ts
telemetry_receiver.ts
websocket_server.ts
influx_writer.ts
dashboard/
index.html
The C++ core is the real-time telemetry generator.
Main responsibilities:
- Runs the simulation loop at
100 Hz. - Produces orbital telemetry frames.
- Publishes frames through a lock-free-style seqlock shared bus.
- Sends telemetry over UDP to
127.0.0.1:9000. - Listens for UDP commands on port
9001. - Supports binary record and replay.
- Attempts real-time scheduling on Linux.
- Logs loop jitter periodically.
The backend receives UDP packets from the core and broadcasts decoded telemetry to WebSocket clients.
Default ports:
- UDP telemetry input:
9000 - WebSocket output:
9010
It can also write telemetry to InfluxDB when the required environment variables are configured.
The dashboard is a static HTML page in dashboard/index.html.
It connects to the backend WebSocket on port 9010, displays live telemetry values and renders a simple orbit visualization.
- CMake
>= 3.18 - C++17 compiler
- POSIX-like environment for sockets and threads
Tested-style commands assume:
- macOS with Apple Clang, or
- Linux with GCC/Clang
- Node.js
- npm
For best deterministic behavior on Linux:
- Linux kernel with POSIX real-time scheduling
- Permission for
SCHED_FIFO - Permission for
mlockall - Optional
/dev/watchdog - Optional SocketCAN device such as
can0 - Optional serial device such as
/dev/ttyUSB0 - Optional SPI/I2C devices such as
/dev/spidev0.0and/dev/i2c-1
On macOS, the software still runs, but Linux-specific real-time and hardware-bus features are unavailable or compiled as safe fallbacks.
From the repository root:
cd /Volumes/WDElements/ntxspace-website/ntx-space-web/entropy
cmake -S core -B core/build -DENTROPY_ENABLE_TESTS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build core/buildRun tests:
ctest --test-dir core/build --output-on-failureThe core binary is produced at:
core/build/entropy
Use three terminals.
cd /Volumes/WDElements/ntxspace-website/ntx-space-web/entropy/backend
npm install
npm run build
npm startExpected output includes:
[WebSocket] listening on 9010
[Telemetry] UDP listening on 9000
Open the static file directly:
open /Volumes/WDElements/ntxspace-website/ntx-space-web/entropy/dashboard/index.htmlOr serve it over HTTP:
cd /Volumes/WDElements/ntxspace-website/ntx-space-web/entropy
python3 -m http.server 8080 -d dashboardThen open:
http://localhost:8080
cd /Volumes/WDElements/ntxspace-website/ntx-space-web/entropy
./core/build/entropyRecommended startup order:
- Backend
- Dashboard
- Core
The core sends UDP telemetry to the backend, the backend broadcasts it over WebSocket, and the dashboard renders it live.
./core/build/entropy./core/build/entropy --record run.binThis writes binary FullTelemetryFrame records to run.bin.
./core/build/entropy --replay run.binThis reads frames from run.bin and republishes them at the normal loop cadence.
- Replay publishes stored telemetry frames through the same
SharedBus. - UDP output still works during replay.
- Replay stops when the file ends.
- Recording and replay files are raw binary and intended for this project version.
| Port | Direction | Protocol | Purpose |
|---|---|---|---|
9000 |
core to backend | UDP | Telemetry stream |
9001 |
external to core | UDP | Command input |
9010 |
backend to browser | WebSocket | Live dashboard telemetry |
Backend ports can be changed with environment variables:
ENTROPY_UDP_PORT=9100 ENTROPY_WS_PORT=9110 npm startThe C++ core currently uses compile-time defaults in core/src/simulator.hpp:
udp_host = "127.0.0.1"
udp_port = 9000
cmd_udp_port = 9001
loop_hz = 100
rt_priority = 90
rt_cpu = 1UDP telemetry packets use a small versioned header followed by a payload.
struct WireHeader {
uint32_t magic; // 0x45544e50, "ETNP"
uint16_t version; // 1
uint16_t payloadSize; // 40 or 104 bytes
};Size: 40 bytes
struct TelemetryFrame {
uint64_t sequenceNumber;
double pressurePa;
double temperatureK;
double altitudeKm;
double altitudeDelta;
};Size: 104 bytes
struct FullTelemetryFrame {
TelemetryFrame base;
double accelerationMps2[3];
double imuBias;
double magneticFieldUt[3];
uint32_t seuEvents;
uint32_t reserved;
};The TypeScript backend accepts:
8 + 104byte framed full packets8 + 40byte framed legacy packets104byte raw full packets40byte raw legacy packets
The core listens on UDP port 9001.
Current command bytes:
| Byte | Action |
|---|---|
1 |
Enable anomaly injection |
2 |
Reset simulation state and clear anomaly |
Examples:
printf '\x01' | nc -u -w0 127.0.0.1 9001
printf '\x02' | nc -u -w0 127.0.0.1 9001The command receiver polls with a short timeout so shutdown does not wait on a long blocking read.
The physics code lives in core/src/physics.
Implemented components:
- NASA Glenn style atmospheric pressure and density model.
- Thermal equilibrium model using solar constant, albedo, Earth IR flux and Stefan-Boltzmann constant.
- Deterministic IMU acceleration model with noise and bias drift.
- Earth magnetic dipole approximation scaled by altitude.
- Deterministic SEU event injection.
- Altitude oscillation between
400 kmand450 km. - Anomaly mode that increases pressure and temperature.
Important constants are centralized in:
core/src/physics/nasa_constants.hpp
This is suitable for integration testing. It is not a replacement for a high-fidelity orbital propagation and atmosphere stack.
The loop target is:
100 Hz
10 ms period
On Linux, the core attempts:
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, ...)SCHED_FIFOwith priority90- CPU affinity to CPU
1 mlockall(MCL_CURRENT | MCL_FUTURE)/dev/watchdogkick when available
If SCHED_FIFO fails, the program continues and prints a warning.
To grant the binary permission for real-time scheduling on Linux:
sudo setcap cap_sys_nice+ep ./core/build/entropyFor more deterministic operation:
- Run on Linux rather than macOS.
- Use an isolated CPU core.
- Avoid running the dashboard/browser on the same isolated core.
- Disable CPU frequency scaling if your lab setup allows it.
- Keep I/O off the RT thread.
SharedBus is the handoff between the real-time producer and sender thread.
It uses a manual seqlock:
- Writer marks sequence odd.
- Writer copies the frame.
- Writer marks sequence even.
- Reader retries until it sees a stable even sequence.
- Reader falls back to the last valid frame after bounded spinning.
This avoids std::atomic<FullTelemetryFrame> because large atomic structs are generally not lock-free and may use hidden locks.
The backend is TypeScript/Node.
Install and build:
cd backend
npm install
npm run buildRun:
npm start| Variable | Default | Purpose |
|---|---|---|
ENTROPY_UDP_PORT |
9000 |
UDP telemetry receiver port |
ENTROPY_WS_PORT |
9010 |
WebSocket server port |
INFLUX_URL |
unset | InfluxDB endpoint |
INFLUX_TOKEN |
unset | InfluxDB auth token |
INFLUX_ORG |
unset | InfluxDB organization |
INFLUX_BUCKET |
unset | InfluxDB bucket |
If all Influx variables are present, telemetry is written to InfluxDB. If they are missing, Influx writing is disabled and the backend still runs normally.
Dashboard file:
dashboard/index.html
It displays:
- Sequence number
- Altitude
- Pressure
- Temperature
- IMU acceleration
- Magnetic field
- SEU event count
- Orbit visualization
- WebSocket connection status
By default it connects to:
ws://<current-host>:9010
If opened directly as a file, most browsers use an empty host. The dashboard falls back to 127.0.0.1.
The project includes hardware-bus wrappers. They are available to integrate into lab-specific sender paths.
File:
core/src/bus/can_bus.hpp
Linux only.
Expected interface:
can0
Example Linux setup:
sudo modprobe can
sudo modprobe can_raw
sudo ip link set can0 up type can bitrate 1000000 dbitrate 2000000 fd onFile:
core/src/bus/serial_bus.hpp
Supports common baud rates such as:
9600192003840057600115200
Example devices:
/dev/ttyUSB0
/dev/ttyS0
File:
core/src/bus/spi_i2c.hpp
Linux device examples:
/dev/spidev0.0
/dev/i2c-1
These wrappers provide low-level access primitives. They are not automatically active in main.cpp; connect them where your lab hardware requires.
The project includes HMAC helpers in:
core/src/security/
Current command receiver behavior is still byte-command based on UDP port 9001. The HMAC helpers are available for authenticated command packet expansion, but the default command path is intentionally simple for lab control.
Do not expose command or telemetry ports to untrusted networks.
To enable InfluxDB:
cd backend
export INFLUX_URL=http://localhost:8086
export INFLUX_TOKEN=your-token
export INFLUX_ORG=your-org
export INFLUX_BUCKET=your-bucket
npm startMeasurement name:
entropy_telemetry
Fields include:
sequencepressure_patemperature_kaltitude_kmaltitude_deltaimu_biasseu_events
cmake -S core -B core/build -DENTROPY_ENABLE_TESTS=ON
cmake --build core/build
cd backend
npm install
npm run build
npm startIn another terminal:
open dashboard/index.html
./core/build/entropy./core/build/entropy --record fault-run.binInject anomaly:
printf '\x01' | nc -u -w0 127.0.0.1 9001Reset:
printf '\x02' | nc -u -w0 127.0.0.1 9001Replay later:
./core/build/entropy --replay fault-run.binIf you have another telemetry source that emits ENTROPY-compatible packets:
cd backend
npm startSend packets to UDP port 9000.
Install CMake:
brew install cmakeor on Debian/Ubuntu:
sudo apt-get install cmake build-essentialOn Linux:
sudo setcap cap_sys_nice+ep ./core/build/entropyYou may also need to check system real-time limits.
Check that the backend is running:
cd backend
npm startCheck WebSocket port:
lsof -i :9010Check core is running:
./core/build/entropyCheck UDP port:
lsof -i UDP:9000Check that the core still targets 127.0.0.1:9000 in:
core/src/simulator.hpp
Confirm all four variables are set:
echo "$INFLUX_URL"
echo "$INFLUX_TOKEN"
echo "$INFLUX_ORG"
echo "$INFLUX_BUCKET"If any are missing, Influx writing is disabled by design.
Confirm you are on Linux and can0 exists:
ip link show can0Bring it up:
sudo ip link set can0 up type can bitrate 1000000 dbitrate 2000000 fd onThe replay file may be empty or from an incompatible frame layout. Record a fresh session:
./core/build/entropy --record run.binThen replay:
./core/build/entropy --replay run.bin- C++ standard: C++17
- Backend TypeScript target: ES2020 / Node16 module resolution
- Core tests live in
core/tests - Build artifacts should stay under
core/build compile_commands.jsonis ignored at the repository root/core level- The dashboard is static and does not require a frontend build step
- The orbital model is intentionally simple and deterministic.
- NASA/JPL constants and NASA Glenn style atmosphere values are used for integration-test realism, not mission analysis precision.
- Hardware bus wrappers exist, but
main.cppcurrently sends live telemetry over UDP by default. - HMAC helpers exist, but default command UDP packets are simple one-byte lab commands.
- Real-time scheduling quality depends on the operating system, permissions and hardware.
- macOS is suitable for development, but Linux is the intended target for RT and hardware bus use.
# Build core
cmake -S core -B core/build -DENTROPY_ENABLE_TESTS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build core/build
# Test core
ctest --test-dir core/build --output-on-failure
# Backend
cd backend
npm install
npm run build
npm start
# Core live
./core/build/entropy
# Record
./core/build/entropy --record run.bin
# Replay
./core/build/entropy --replay run.bin
# Dashboard via HTTP
python3 -m http.server 8080 -d dashboard
# Inject anomaly
printf '\x01' | nc -u -w0 127.0.0.1 9001
# Reset
printf '\x02' | nc -u -w0 127.0.0.1 9001=======
Real-time Hardware-in-the-Loop orbital telemetry injector — 100Hz LEO simulation over UDP, WebSocket dashboard, C++17 + TypeScript