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
ackboolean—Device acknowledged the last command
batteryVoltagenumberVBattery voltage
batterySocnumber%Battery state of charge
batteryTempnumber°CBattery temperature
tempBoardnumber°CBoard-level temperature
airTempnumber°CExternal air temperature
batteryHeatingboolean—Battery heater is active
firmwareVersionstring—Running firmware version (e.g. "12.73.35")
hardwareVersionstring—Hardware version (e.g. "11.24.38")
flightStagestring—Current flight stage (see below)
geofenceActiveboolean—Geofence is currently active
geofenceBrokenboolean—Geofence boundary has been violated
gpsFixStatusnumber—GPS fix type (0=none, 2=2D, 3=3D)
gpsSatellitesnumber—Satellites in view
ascentRatenumberm/sVertical ascent rate
uptimenumbermsSystem uptime
massDroppednumbergBallast mass dropped (ballaster devices only)
dispenserIsDroppingboolean—Ballast 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.