Jump to content

Telemetry Packet: Difference between revisions

From Transight Wiki
No edit summary
Line 146: Line 146:
<html lang="en">
<html lang="en">
<head>
<head>
   <meta charset="UTF-8" />
   <meta charset="UTF-8">
   <title>Telemetry Packet Parser – Grouped Frames</title>
   <title>Telemetry Packet Parser</title>
   <style>
   <style>
     body { font-family: Arial, sans-serif; margin: 24px; }
     body { font-family: Arial, sans-serif; }
     textarea { width: 100%; height: 140px; font-family: monospace; }
     textarea { width: 100%; height: 100px; font-family: monospace; }
     button {
     button {
       padding: 10px 20px; border-radius: 50px; font-weight: bold;
       padding: 10px 20px;
       background-color: #680022; color: white; border: none; cursor: pointer;
      border-radius: 50px;
      font-weight: bold;
       background-color: #680022;
      color: white;
      border: none;
      cursor: pointer;
     }
     }
     button:hover { background-color: #4c0019; }
     button:hover {
     table { width: 100%; border-collapse: collapse; margin-top: 12px; table-layout: fixed; word-wrap: break-word; }
      background-color: #4c0019;
     th, td { border: 1px solid #ccc; padding: 6px; text-align: left; font-size: 13px; }
    }
     table { width: 100%; border-collapse: collapse; margin-top: 20px; }
     th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
     th { background-color: #680022; color: white; }
     th { background-color: #680022; color: white; }
    .error { color: #b00020; margin-top: 8px; font-weight: bold; }
    .section { margin-top: 30px; }
    .json-output { background: #f4f4f4; padding: 12px; font-family: monospace; font-size: 13px; overflow-x: auto; }
    .pkt-title { margin: 18px 0 6px; font-size: 16px; font-weight: bold; color: #333; }
   </style>
   </style>
</head>
</head>
<body>
<body>
  <h2>Telemetry Packet Parser</h2>


  <textarea id="hexInput" placeholder="Paste Telemetry Hex Packet Here..."></textarea><br/>
<h2>Telemetry Packet Parser</h2>
  <button id="parseBtn">Parse Packet</button>
<textarea id="hexInput" placeholder="Paste packet here..."></textarea><br>
<button onclick="parseTelemetry()">Parse Packet</button>


  <div id="errors" class="error"></div>
<h3>Parsed Output:</h3>
<table id="resultTable">
  <thead><tr><th>Field</th><th>Value</th></tr></thead>
  <tbody></tbody>
</table>
<script>
function hexToBits(hex) {
  return hex.match(/.{1,2}/g).map(byte =>
    parseInt(byte, 16).toString(2).padStart(8, '0')).join('');
}


   <div class="section">
function bitsToInt(bits) {
    <h3>Header</h3>
   return parseInt(bits, 2);
    <table id="headerTable">
}
      <thead><tr><th>Field</th><th>Value</th></tr></thead>
      <tbody></tbody>
    </table>
  </div>


   <div class="section">
function bitsToSignedInt(bits) {
    <h3>Data Packets</h3>
  let value = parseInt(bits, 2);
    <div id="dataTables"></div>
   const max = Math.pow(2, bits.length);
  </div>
  return value >= max / 2 ? value - max : value;
}


   <div class="section">
function bigIntFromBits(bits) {
    <h3>JSON Output</h3>
   return BigInt('0b' + bits).toString();
    <pre class="json-output" id="jsonOut"></pre>
}
  </div>


<script>
function parseTelemetry() {
document.addEventListener('DOMContentLoaded', function () {
  const hex = document.getElementById("hexInput").value.trim().toLowerCase();
  const resultBody = document.querySelector("#resultTable tbody");
  resultBody.innerHTML = "";


   function cleanHex(str) {
   if (!hex || !hex.startsWith("24")) {
    return (str || "").toString().trim().replace(/[^0-9a-fA-F]/g, "").toLowerCase();
     alert("Invalid packet");
  }
     return;
  function hexToBits(hex) {
    if (!hex) return "";
    if (hex.length % 2 === 1) hex = "0" + hex; // pad odd length
    var bytes = hex.match(/.{1,2}/g) || [];
    return bytes.map(function (b) {
      return parseInt(b, 16).toString(2).padStart(8, "0");
    }).join("");
  }
  function bitsToInt(bits) { return bits ? parseInt(bits, 2) : 0; }
  function bitsToSignedInt(bits) {
    if (!bits) return 0;
    var v = parseInt(bits, 2);
    var max = 1 << bits.length;
    return v >= max / 2 ? v - max : v;
  }
  function addRow(tbody, k, v) {
     var tr = document.createElement("tr");
     tr.innerHTML = "<td>" + k + "</td><td>" + v + "</td>";
    tbody.appendChild(tr);
   }
   }


   /* --- Field Map (bit indices) --- */
   const bits = hexToBits(hex);
   var FULL_FIELDS = [
  const output = (label, value) => {
    const row = document.createElement("tr");
    row.innerHTML = `<td>${label}</td><td>${value}</td>`;
    resultBody.appendChild(row);
  };
 
   const fields = [
     ["Start Byte", 0, 8], ["Data Length", 8, 20], ["No. of Packets", 20, 25],
     ["Start Byte", 0, 8], ["Data Length", 8, 20], ["No. of Packets", 20, 25],
     ["IMEI", 25, 75], ["Packet Type", 75, 80], ["Packet Status", 80, 81],
     ["IMEI", 25, 75], ["Packet Type", 75, 80], ["Packet Status", 80, 81],
Line 244: Line 241:
   ];
   ];


   /* Sub-packet fields (strip first 80 header bits) */
   let tempDateTime = 0;
  var SUB_FIELDS = FULL_FIELDS
    .filter(function (x) { return x[1] >= 80 && x[2] <= 520; })
    .map(function (x) { return [x[0], x[1] - 80, x[2] - 80]; });


   /* Packet type label fix */
   for (const [label, start, end] of fields) {
  var PACKET_TYPE_NAME = { 1: "TelemetryPacket" };
     const val = bits.slice(start, end);
 
     switch (label) {
  /* Render each packet as a 2-column table */
       case "IMEI":
  function renderPacketsAsKVTables(dataArray) {
        output(label, bigIntFromBits(val));
     var container = document.getElementById("dataTables");
        break;
     container.innerHTML = "";
       case "Latitude":
    dataArray.forEach(function (pkt, idx) {
       case "Longitude":
       var title = document.createElement("div");
        output(label, bitsToInt(val) / 1e6);
       title.className = "pkt-title";
        break;
       title.textContent = "Packet #" + (idx + 1);
       case "HDOP":
      container.appendChild(title);
       case "PDOP":
 
         output(label, bitsToInt(val) / 100);
      var table = document.createElement("table");
        break;
      var thead = document.createElement("thead");
      case "Speed":
       thead.innerHTML = "<tr><th>Field</th><th>Value</th></tr>";
        output(label, bitsToInt(val) / 100);
       var tbody = document.createElement("tbody");
        break;
 
      case "Altitude":
      SUB_FIELDS.forEach(function (f) {
      case "Supply Voltage":
         var name = f[0];
       case "Internal Battery Voltage":
        addRow(tbody, name, pkt[name.replace(/\s+/g, '')]);
       case "Fuel Percentage 1":
      });
       case "Fuel Percentage 2":
 
       case "Fuel Percentage 3":
      table.appendChild(thead);
       case "Analog Input 1":
      table.appendChild(tbody);
       case "Analog Input 2":
      container.appendChild(table);
        output(label, bitsToInt(val) / 10);
    });
        break;
  }
       case "Temperature 1":
 
       case "Temperature 2":
  /* Parse button logic */
       case "Temperature 3":
  var parseBtn = document.getElementById("parseBtn");
        output(label, bitsToSignedInt(val) / 10);
  if (parseBtn) {
        break;
    parseBtn.addEventListener("click", function () {
       case "DateTime UTC":
       var hex = cleanHex(document.getElementById("hexInput").value);
        tempDateTime = bitsToInt(val);
       var errors = document.getElementById("errors");
        const utc = new Date(tempDateTime * 1000);
       var headerBody = document.querySelector("#headerTable tbody");
        output("DateTime (UTC)", utc.toISOString());
       var dataTables = document.getElementById("dataTables");
        break;
       var jsonOut = document.getElementById("jsonOut");
       case "TimeZone":
 
        const tz = bitsToSignedInt(val);
       errors.textContent = "";
        const offsetMin = tz * 15;
      headerBody.innerHTML = "";
        const sign = offsetMin >= 0 ? "+" : "-";
       dataTables.innerHTML = "";
        const h = Math.floor(Math.abs(offsetMin) / 60);
       jsonOut.textContent = "";
        const m = Math.abs(offsetMin % 60);
 
        output("TimeZone", `${offsetMin} mins = UTC${sign}${h}:${m.toString().padStart(2, '0')}`);
       if (!hex) { errors.textContent = "No data provided."; return; }
        const localTime = new Date((tempDateTime + offsetMin * 60) * 1000);
      if (!hex.startsWith("24")) { errors.textContent = "Invalid frame: Start Byte must be 0x24."; return; }
         const formatted = localTime.toISOString().replace("T", " ").replace(".000Z", "");
 
        output("DateTime (Local Time)", formatted);
      var bits = hexToBits(hex);
         break;
       if (!bits) { errors.textContent = "Invalid hex payload."; return; }
       default:
 
        output(label, bitsToInt(val));
      var dataLenBytes = bitsToInt(bits.slice(8, 20));
     }
      var numItems    = bitsToInt(bits.slice(20, 25));
 
      // IMEI (BigInt fallback)
      var imeiBits = bits.slice(25, 75);
      var imei;
       try { imei = BigInt("0b" + imeiBits).toString(); }
      catch (e) { imei = parseInt(imeiBits, 2).toString(); }
 
      var pktTypeVal  = bitsToInt(bits.slice(75, 80));
      var pktTypeName = PACKET_TYPE_NAME[pktTypeVal] || pktTypeVal.toString();
 
      var perItemBytes = numItems > 0 ? dataLenBytes / numItems : 0;
      var perItemBits  = perItemBytes * 8;
      var totalBits    = bits.length;
      var subStart    = 80;
 
      if (!Number.isInteger(perItemBytes)) { errors.textContent = "Data Length not divisible by packet count."; return; }
      if (totalBits < subStart + perItemBits * numItems) { errors.textContent = "Frame too short for given count."; return; }
 
      /* Header table (2-column) */
      addRow(headerBody, "Start Byte", "$");
      addRow(headerBody, "Length", dataLenBytes);
      addRow(headerBody, "Data Items", numItems);
      addRow(headerBody, "IMEI", imei);
      addRow(headerBody, "Packet Type", pktTypeName);
 
      /* Build per-packet objects keyed by compact field name */
      var dataArray = [];
      var KEY_MAP = new Map(FULL_FIELDS.map(function (x) { return [x[0], x[0].replace(/\s+/g, '')]; }));
 
      for (var i = 0; i < numItems; i++) {
        var sbeg = subStart + i * perItemBits;
        var send = sbeg + perItemBits;
        var subBits = bits.slice(sbeg, send);
         var obj = {};
 
        SUB_FIELDS.forEach(function (f) {
          var name = f[0], s = f[1], e = f[2];
          var b = subBits.slice(s, e);
          var val;
 
          if (name === "Temperature 1" || name === "Temperature 2" || name === "Temperature 3") {
            val = bitsToSignedInt(b);
          } else if (name === "Supply Voltage" || name === "Internal Battery Voltage") {
            val = bitsToInt(b) / 10; // 123 -> 12.3 V
          } else {
            val = bitsToInt(b);
          }
          obj[KEY_MAP.get(name)] = val;
        });
 
         dataArray.push(obj);
       }
 
      /* Render each packet as its own key/value table */
      renderPacketsAsKVTables(dataArray);
 
      /* JSON */
      var out = { StartByte: "$", Length: dataLenBytes, DataItems: numItems, IMEI: imei, PacketType: pktTypeName, Data: dataArray };
      jsonOut.textContent = JSON.stringify(out, null, 2);
     });
   }
   }
}); // DOMContentLoaded
}
</script>
</script>
</body>
</body>
</html>
</html>

Revision as of 08:17, 22 August 2025

Field Size (bits) Bit Range Description Breakdown
Header (10 bytes)
Start byte 8 0–7 Starting character $ (ASCII value 36) $
Data length 12 08–19 2-byte length of the data following the header
Num of data packets 5 20–24 Number of packets (0–32) 0–32
IMEI 50 25–74 Unique device identifier e.g., 887744556677882
packet type 5 75–79 Integer type:
  • 00 - Device Info Packet
  • 01 - Telemetry Packet
  • 02 - OTA Packet
  • 03 - Error Packet
  • 04 - Device Configuration Packet
  • 05 - IP Configuration Packet
Data (= 55 bytes × number of packets)
Packet Status 1 80 Type of packet 0:History, 1:Live
Frame Number 16 81-96 Frame number
AlertID 5 97–101 Alert identifier
Operator 4 102–105 Network operator 00-Airtel, 01-BSNL, 02-VI, 04-JIO
Signal Strength 5 106–110 Signal strength Integer (0–31)
MCC 10 111–120 Mobile country code Integer
MNC 10 121–130 Mobile network code Integer
Cell Id 16 131–146 Cell tower ID Integer
Location Area Code 16 147–162 Location area code Integer
Fix_status 1 163 GPS fix status 0: No fix, 1: Valid Fix
Latitude 29 164–192 Latitude coordinate Divide by 1,000,000 for float value
NS_Indication 1 193 N or S 0: N, 1: S
Longitude 29 194–222 Longitude coordinate Divide by 1,000,000 for float value
EW_Indication 1 223 East/West Indication 0: E, 1: W
HDOP 10 224–233 Horizontal dilution Divide by 10 for float value
PDOP 10 234–243 Position dilution Divide by 10 for float value
Speed 15 244–258 Speed in km/h Divide by 10 for float value
Altitude 15 259–273 Altitude in meters Divide by 10 for float value
Power Status 1 274 Power connection status 0: Power disconnected, 1: Power connected
Ignition Status 1 275 Ignition status 0: OFF, 1: ON
Immobilizer Status 1 276 Immobilizer status 0: OFF, 1: ON
Tamper 1 277 Wire Tamper detection 0: Tamper alert OFF, 1: Tamper alert ON
Supply Voltage 10 278–287 External battery voltage Divide by 10 for float value
Internal Battery Voltage 6 288–293 Internal battery voltage Divide by 10 for float value
FuelSensorstatus 1 1 294 Status of fuel sensor 1 1 if connected, 0 if not connected
Fuel Percentage 1 10 295–304 Fuel level percentage Divide by 10 for float value
Fuel SensorValue 1 16 305–320 Fuel sensor value Divide by 10 if float
FuelSensorstatus 2 1 321 Status of fuel sensor 2 1 if connected, 0 if not connected
Fuel Percentage 2 10 322–331 Fuel level percentage Divide by 10 for float value
Fuel SensorValue 2 16 332–347 Fuel sensor value Divide by 10 if float
FuelSensorstatus 3 1 348 Status of fuel sensor 3 1 if connected, 0 if not connected
Fuel Percentage 3 16 349–358 Fuel sensor value Divide by 10 if float
Fuel SensorValue 3 16 359–374 Fuel level percentage Divide by 10 for float value
Analog Input 1 10 375–384 Analog input Divide by 10 for float value
Analog Input 2 10 385–394 Analog input 1 Divide by 10 for float value
Digital Input 1 1 395 Digital input 1 0 or 1
Digital Input 2 1 396 Digital input 2 0 or 1
Digital Output 1 1 397 Digital output 1 0 or 1
Digital Output 2 1 398 Digital output 2 0 or 1
Temperature sensor status 1 1 399 Status of temperature sensor 1 1 if connected, 0 if not connected
Temperature 1 12 400–411 Temperature in °C Signed, divide by 10
Temperature sensor status 2 1 412 Status of temperature sensor 2 1 if connected, 0 if not connected
Temperature 2 12 413–424 Temperature in °C Signed, divide by 10
Temperature sensor status 3 1 425 Status of temperature sensor 3 1 if connected, 0 if not connected
Temperature 3 12 426–437 Temperature in °C Signed, divide by 10
Humidity 7 438–444 Humidity percentage
Odometer 35 445–479 Odometer value in meters
DateTime UTC 32 480–511 Timestamp UTC time in seconds
TimeZone 8 512–519

Timezone in quarter-hours (e.g., 22 = +5:30). Each unit = 15 mins. Value 22 = 22 × 15 mins = 330 mins = +5:30 Range: -48 to 56, 2's complement ||

Tail
End Character 8 0–7 Starting character * (ASCII value 42) *
CRC 8 8–15 8-bit XOR CRC of data starting from $ to * (excluding $ and *)

Sample Packet

HEX "240370e21aeb7abfd5818011047aca0260081b780000000000000000000000000000307890000000000000000000000000000000000000000000000068a7f192162abf"

{"imei": 862942074896044, "packet_type": 1, "no_packets": 1, "packet_status": 1, "frame_number": 34, "alert_id": 1, "operator": 1, "signal_strength": 29, "mcc": 404, "mnc": 19, "cell_id": 64, "lac": 56256, "fix_status": 0, "latitude": 0.0, "latitude_dir": 0, "longitude": 0.0, "longitude_dir": 0, "hdop": 0.0, "pdop": 0.0, "speed": 0.0, "altitude": 0.0, "power": 1, "ignition": 1, "immobilizer": 0, "tamper": 0, "supply_volatge": 12.0, "internal_battery_volatge": 3.6, "fuel_sensor_status_1": 0, "fuel_percentage_1": 0.0, "fuel_value_sensor_1": 0, "fuel_sensor_status_2": 0, "fuel_percentage_2": 0.0, "fuel_value_sensor_2": 0, "fuel_sensor_status_3": 0, "fuel_percentage_3": 0.0, "fuel_value_sensor_3": 0, "analog_input1": 0.0, "analog_input2": 0.0, "digital_input1": 0, "digital_input2": 0, "digital_output1": 0, "digital_output2": 0, "temp_sensor_status_1": 0, "temperature_1": 0.0, "temp_sensor_status_2": 0, "temperature_2": 0.0, "temp_sensor_status_3": 0, "temperature_3": 0.0, "humidity": 0, "odometer": 0, "dateTime": 1755836818, "timezone": 22, "dateTime_tz": "2025-08-22 09:56:58", "error_code": 0
}


Telemetry Packet Parser

Telemetry Packet Parser


Parsed Output:

FieldValue