Skip to content

Internal Telemetry (Extended Fields)

Overview

Organizations with internal_testing enabled receive expanded device telemetry beyond the standard location fields. This is used by the manufacturing team for MRF (Manufactured Ready for Flight) validation and internal QA workflows.

Internal telemetry is a strict superset of the public SDK — all standard fields are always present, with additional fields nested under an internal property on each device.

Enabling Internal Telemetry

The internal_testing flag is set directly on the organization record in the database. There is no UI toggle — this is by design, mirroring the super_admin pattern.

sql
UPDATE organization SET internal_testing = true WHERE id = 'YOUR_ORG_ID';

Once enabled, all SDK connections for that organization will automatically include internal telemetry fields. No SDK code changes or token regeneration required.

Data Structure

Balloon Update (with internal fields)

javascript
{
  balloonId: "BLN-2024-001",
  missionId: "MSN-2024-001",
  devices: [
    {
      // Standard public fields (always present)
      deviceId: "APX-001",
      deviceType: "APX",
      lat: 40.7128,
      lng: -74.0060,
      altitude: 15240,
      timestamp: "2024-01-15T10:30:00.000Z",
      motion: {
        groundSpeed: 12.5,
        heading: 90.0,
        ascentRate: 2.3
      },

      // Internal fields (only for internal_testing orgs)
      internal: {
        ack: true,
        batteryVoltage: 3.7,
        batterySoc: 85,
        batteryTemp: 22.5,
        tempBoard: 25.0,
        airTemp: -30.5,
        batteryHeating: true,
        firmwareVersion: "12.73.35",
        hardwareVersion: "11.24.38",
        flightStage: "FLOAT",
        geofenceActive: true,
        geofenceBroken: false,
        gpsFixStatus: 3,
        gpsSatellites: 12,
        ascentRate: 2.3,
        uptime: 3600000,
        massDropped: 150,
        dispenserIsDropping: false
      }
    }
  ]
}

Field Reference

FieldTypeUnitDescription
ackbooleanDevice acknowledged the last command
batteryVoltagenumberVBattery voltage
batterySocnumber%Battery state of charge
batteryTempnumber°CBattery temperature
tempBoardnumber°CBoard-level temperature
airTempnumber°CExternal air temperature
batteryHeatingbooleanBattery heater is active
firmwareVersionstringRunning firmware version (e.g. "12.73.35")
hardwareVersionstringHardware version (e.g. "11.24.38")
flightStagestringCurrent flight stage (see below)
geofenceActivebooleanGeofence is currently active
geofenceBrokenbooleanGeofence boundary has been violated
gpsFixStatusnumberGPS fix type (0=none, 2=2D, 3=3D)
gpsSatellitesnumberSatellites in view
ascentRatenumberm/sVertical ascent rate
uptimenumbermsSystem uptime
massDroppednumbergBallast mass dropped (ballaster devices only)
dispenserIsDroppingbooleanBallast dispenser actively dropping (ballaster only)

Flight Stages

ValueDescription
PREFLIGHTDevice powered on, pre-launch checks
ASCENTBalloon ascending
FLOATAt target altitude, floating
DESCENTDescending (valve open or terminated)
GROUNDEDOn the ground post-flight

Usage Examples

JavaScript — MRF Validation

javascript
import { UrbanSkySDK } from '@urbansky/sdk'

const sdk = await UrbanSkySDK.init({
  apiToken: 'your-internal-api-token',
  baseUrl: 'https://api.ops.atmosys.com',
})

sdk.on('balloon:update', update => {
  for (const device of update.devices) {
    if (!device.internal) continue

    // Verify ACK received
    if (device.internal.ack) {
      console.log(`✓ ${device.deviceId}: ACK received`)
    }

    // Check battery health
    const bv = device.internal.batteryVoltage
    if (bv !== undefined && bv < 3.3) {
      console.warn(`⚠ ${device.deviceId}: Low battery ${bv}V`)
    }

    // Verify firmware version
    if (device.internal.firmwareVersion) {
      console.log(`  FW: ${device.internal.firmwareVersion}`)
    }
  }
})

Python — MRF Validation

python
from urbansky_sdk import UrbanSkySDK, SDKConfig

sdk = await UrbanSkySDK.init(SDKConfig(
  api_token="your-internal-api-token",
  base_url="https://api.ops.atmosys.com",
))

def handle_update(update):
  for device in update.devices:
    if not device.internal:
      continue

    # Verify ACK received
    if device.internal.ack:
      print(f"✓ {device.device_id}: ACK received")

    # Check battery health
    if device.internal.battery_voltage is not None:
      if device.internal.battery_voltage < 3.3:
        print(f"⚠ {device.device_id}: Low battery {device.internal.battery_voltage}V")

    # Verify firmware version
    if device.internal.firmware_version:
      print(f"  FW: {device.internal.firmware_version}")

sdk.on("balloon:update", handle_update)

Important Notes

  • Public SDK consumers are unaffected. The internal field is simply absent for non-internal orgs — no breaking changes.
  • All data flows through MC. Internal telemetry follows the full pipeline: Iridium → packet-manager → MC database → Centrifugal → SDK. This validates the entire system, not just raw device output.
  • Field availability varies by device type. Not all devices report all fields. Ballaster-specific fields (massDropped, dispenserIsDropping) only appear on BAL devices. Always check for undefined/None before using a field.
  • The internal_testing flag is cached for 5 minutes on the server. After toggling the flag in the DB, allow up to 5 minutes for the change to take effect on active connections.