Jump to content

OTA PACKET PARSER: Difference between revisions

From Transight Wiki
Created page with "<html lang="en"> <head> <meta charset="UTF-8"> <title>Telemetry Packet Parser</title> <style> body { font-family: Arial, sans-serif; } textarea { width: 100%; height: 100px; font-family: monospace; } button { padding: 10px 20px; border-radius: 20px; font-weight: bold; background-color: #680022; color: white; border: none; cursor: pointer; } button:hover { background-color: #4c0019; } table..."
 
No edit summary
Line 2: Line 2:
<head>
<head>
   <meta charset="UTF-8">
   <meta charset="UTF-8">
   <title>Telemetry Packet Parser</title>
   <title>OTA Packet Parser</title>
   <style>
   <style>
     body { font-family: Arial, sans-serif; }
     body { font-family: Arial, sans-serif; padding: 20px; }
     textarea { width: 100%; height: 100px; font-family: monospace; }
     textarea { width: 100%; height: 100px; font-family: monospace; margin-bottom: 10px; }
     button {
     button {
       padding: 10px 20px;
       padding: 10px 20px; border-radius: 20px;
      border-radius: 20px;
       font-weight: bold; background-color: #680022; color: white;
       font-weight: bold;
       border: none; cursor: pointer;
      background-color: #680022;
      color: white;
       border: none;
      cursor: pointer;
    }
    button:hover {
      background-color: #4c0019;
     }
     }
    button:hover { background-color: #4c0019; }
     table { width: 100%; border-collapse: collapse; margin-top: 20px; }
     table { width: 100%; border-collapse: collapse; margin-top: 20px; }
     th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
     th, td { border: 1px solid #ccc; padding: 8px; }
     th { background-color: #680022; color: white; }
     th { background-color: #680022; color: white; }
   </style>
   </style>
Line 25: Line 19:
<body>
<body>


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


<h3>Parsed Output:</h3>
<h3>Parsed Output:</h3>
Line 34: Line 28:
   <tbody></tbody>
   <tbody></tbody>
</table>
</table>
<script>
<script>
function hexToBits(hex) {
function hexToBits(hex) {
   return hex.match(/.{1,2}/g).map(byte =>
   return hex.match(/.{1,2}/g).map(b =>
     parseInt(byte, 16).toString(2).padStart(8, '0')).join('');
     parseInt(b, 16).toString(2).padStart(8, '0')).join('');
}
}


Line 50: Line 45:
}
}


function bigIntFromBits(bits) {
function bitsToAscii(bits) {
   return BigInt('0b' + bits).toString();
  let ascii = '';
  for (let i = 0; i < bits.length; i += 8) {
    ascii += String.fromCharCode(parseInt(bits.slice(i, i + 8), 2));
  }
   return ascii;
}
 
// Correct IMEI parser from hex (BCD)
function parseIMEIFromHex(hex, bitStart, bitEnd) {
  const byteStart = bitStart / 8;
  const byteEnd = bitEnd / 8;
  const imeiHex = hex.slice(byteStart * 2, byteEnd * 2); // 2 hex chars per byte
  let imei = '';
  for (let i = 0; i < imeiHex.length; i += 2) {
    const byte = imeiHex.slice(i, i + 2);
    const firstDigit = (parseInt(byte, 16) & 0xF0) >> 4;
    const secondDigit = parseInt(byte, 16) & 0x0F;
    imei += firstDigit.toString() + secondDigit.toString();
  }
  return imei.replace(/^0+/, '');
}
}


function parseTelemetry() {
function parseOTAPacket() {
   const hex = document.getElementById("hexInput").value.trim().toLowerCase();
   const hex = document.getElementById("hexInput").value.trim().toLowerCase();
   const resultBody = document.querySelector("#resultTable tbody");
   const resultBody = document.querySelector("#resultTable tbody");
   resultBody.innerHTML = "";
   resultBody.innerHTML = "";


   if (!hex || !hex.startsWith("24")) {
   if (!hex.startsWith("24")) {
     alert("Invalid packet");
     alert("Packet must start with hex '24'");
     return;
     return;
   }
   }


   const bits = hexToBits(hex);
   const bits = hexToBits(hex);
  const getBits = (start, end) => bits.slice(start, end);
   const output = (label, value) => {
   const output = (label, value) => {
     const row = document.createElement("tr");
     const row = document.createElement("tr");
Line 71: Line 86:
   };
   };


   const fields = [
  // Correct IMEI using hex logic, not bit-based bigint
    ["Start Byte", 0, 8], ["Data Length", 8, 20], ["No. of Packets", 20, 25],
   const imei = parseIMEIFromHex(hex, 25, 75);
    ["IMEI", 25, 75], ["Packet Type", 75, 80], ["Packet Status", 80, 81],
  output("IMEI", imei);
    ["Frame Number", 81, 97], ["Alert ID", 97, 102], ["Operator", 102, 106],
 
    ["Signal Strength", 106, 111], ["MCC", 111, 121], ["MNC", 121, 131],
  output("Packet Type", bitsToInt(getBits(75, 80)));
    ["Cell ID", 131, 147], ["LAC", 147, 163], ["Fix Status", 163, 164],
  output("OTA Source", bitsToInt(getBits(80, 85)));
    ["Latitude", 164, 193], ["NS Indication", 193, 194], ["Longitude", 194, 223],
  output("Error Status", bitsToInt(getBits(85, 93)));
    ["EW Indication", 223, 224], ["HDOP", 224, 234], ["PDOP", 234, 244],
    ["Speed", 244, 259], ["Altitude", 259, 274], ["Power Status", 274, 275],
    ["Ignition Status", 275, 276], ["Immobilizer Status", 276, 277],
    ["Tamper", 277, 278], ["Supply Voltage", 278, 288], ["Internal Battery Voltage", 288, 294],
    ["Fuel Sensor Status 1", 294, 295], ["Fuel Percentage 1", 295, 305],
    ["Fuel Sensor Value 1", 305, 321], ["Fuel Sensor Status 2", 321, 322],
    ["Fuel Percentage 2", 322, 332], ["Fuel Sensor Value 2", 332, 348],
    ["Fuel Sensor Status 3", 348, 349], ["Fuel Percentage 3", 349, 359],
    ["Fuel Sensor Value 3", 359, 375], ["Analog Input 1", 375, 385],
    ["Analog Input 2", 385, 395], ["Digital Input 1", 395, 396],
    ["Digital Input 2", 396, 397], ["Digital Output 1", 397, 398],
    ["Digital Output 2", 398, 399], ["Temperature Sensor Status 1", 399, 400],
    ["Temperature 1", 400, 412], ["Temperature Sensor Status 2", 412, 413],
    ["Temperature 2", 413, 425], ["Temperature Sensor Status 3", 425, 426],
    ["Temperature 3", 426, 438], ["Humidity", 438, 445], ["Odometer", 445, 480],
    ["DateTime UTC", 480, 512], ["TimeZone", 512, 520]
  ];


   let tempDateTime = 0;
   const cellBits = getBits(93, 144);
  const isWithCountryCode = cellBits[0] === '1';
  const cellNo = parseInt(cellBits.slice(1), 2);
  output("Cell Number", isWithCountryCode ? `+${cellNo}` : cellNo);


   for (const [label, start, end] of fields) {
   const dt = bitsToInt(getBits(144, 176));
    const val = bits.slice(start, end);
  output("UTC Timestamp", new Date(dt * 1000).toISOString());
    switch (label) {
 
      case "IMEI":
  const timezoneRaw = bitsToSignedInt(getBits(176, 184));
        output(label, bigIntFromBits(val));
  const offsetMinutes = timezoneRaw * 15;
        break;
  const h = Math.floor(Math.abs(offsetMinutes) / 60);
      case "Latitude":
  const m = Math.abs(offsetMinutes % 60);
      case "Longitude":
  const tzSign = offsetMinutes >= 0 ? "+" : "-";
        output(label, bitsToInt(val) / 1e6);
  output("Timezone", `${offsetMinutes} mins = UTC${tzSign}${h}:${m.toString().padStart(2, '0')}`);
        break;
 
      case "HDOP":
  const localTime = new Date((dt + offsetMinutes * 60) * 1000)
      case "PDOP":
    .toISOString().replace("T", " ").replace(".000Z", "");
        output(label, bitsToInt(val) / 100);
  output("DateTime (Local)", localTime);
        break;
 
      case "Speed":
  const cmdLength = bitsToInt(getBits(184, 192));
        output(label, bitsToInt(val) / 100);
  output("Command Length", cmdLength);
        break;
 
      case "Altitude":
  const cmdBits = getBits(192, 192 + cmdLength * 8);
      case "Supply Voltage":
  output("Command", bitsToAscii(cmdBits));
      case "Internal Battery Voltage":
 
      case "Fuel Percentage 1":
  const respLenStart = 192 + cmdLength * 8;
      case "Fuel Percentage 2":
  const responseLength = bitsToInt(getBits(respLenStart, respLenStart + 8));
      case "Fuel Percentage 3":
  output("Response Length", responseLength);
      case "Analog Input 1":
 
      case "Analog Input 2":
  const respBits = getBits(respLenStart + 8, respLenStart + 8 + responseLength * 8);
        output(label, bitsToInt(val) / 10);
   output("Response", bitsToAscii(respBits));
        break;
      case "Temperature 1":
      case "Temperature 2":
      case "Temperature 3":
        output(label, bitsToSignedInt(val) / 10);
        break;
      case "DateTime UTC":
        tempDateTime = bitsToInt(val);
        const utc = new Date(tempDateTime * 1000);
        output("DateTime (UTC)", utc.toISOString());
        break;
      case "TimeZone":
        const tz = bitsToSignedInt(val);
        const offsetMin = tz * 15;
        const sign = offsetMin >= 0 ? "+" : "-";
        const h = Math.floor(Math.abs(offsetMin) / 60);
        const m = Math.abs(offsetMin % 60);
        output("TimeZone", `${offsetMin} mins = UTC${sign}${h}:${m.toString().padStart(2, '0')}`);
        const localTime = new Date((tempDateTime + offsetMin * 60) * 1000);
        const formatted = localTime.toISOString().replace("T", " ").replace(".000Z", "");
        output("DateTime (Local Time)", formatted);
        break;
      default:
        output(label, bitsToInt(val));
    }
   }
}
}
</script>
</script>
</body>
</body>
</html>
</html>

Revision as of 09:36, 21 July 2025

OTA Packet Parser

OTA Packet Parser


Parsed Output:

FieldValue