OTA PACKET PARSER: Difference between revisions
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> | <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; | ||
font-weight: bold; background-color: #680022; color: white; | |||
font-weight: bold; | border: none; cursor: pointer; | ||
border: none; | |||
} | } | ||
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 | 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> | <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=" | <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( | return hex.match(/.{1,2}/g).map(b => | ||
parseInt( | parseInt(b, 16).toString(2).padStart(8, '0')).join(''); | ||
} | } | ||
| Line 50: | Line 45: | ||
} | } | ||
function | function bitsToAscii(bits) { | ||
return | 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 | 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 ( | if (!hex.startsWith("24")) { | ||
alert(" | 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 | // Correct IMEI using hex logic, not bit-based bigint | ||
const imei = parseIMEIFromHex(hex, 25, 75); | |||
output("IMEI", imei); | |||
output("Packet Type", bitsToInt(getBits(75, 80))); | |||
output("OTA Source", bitsToInt(getBits(80, 85))); | |||
output("Error Status", bitsToInt(getBits(85, 93))); | |||
const cellBits = getBits(93, 144); | |||
const isWithCountryCode = cellBits[0] === '1'; | |||
const cellNo = parseInt(cellBits.slice(1), 2); | |||
output("Cell Number", isWithCountryCode ? `+${cellNo}` : cellNo); | |||
const dt = bitsToInt(getBits(144, 176)); | |||
output("UTC Timestamp", new Date(dt * 1000).toISOString()); | |||
const timezoneRaw = bitsToSignedInt(getBits(176, 184)); | |||
const offsetMinutes = timezoneRaw * 15; | |||
const h = Math.floor(Math.abs(offsetMinutes) / 60); | |||
const m = Math.abs(offsetMinutes % 60); | |||
const tzSign = offsetMinutes >= 0 ? "+" : "-"; | |||
output("Timezone", `${offsetMinutes} mins = UTC${tzSign}${h}:${m.toString().padStart(2, '0')}`); | |||
const localTime = new Date((dt + offsetMinutes * 60) * 1000) | |||
.toISOString().replace("T", " ").replace(".000Z", ""); | |||
output("DateTime (Local)", localTime); | |||
const cmdLength = bitsToInt(getBits(184, 192)); | |||
output("Command Length", cmdLength); | |||
const cmdBits = getBits(192, 192 + cmdLength * 8); | |||
output("Command", bitsToAscii(cmdBits)); | |||
const respLenStart = 192 + cmdLength * 8; | |||
const responseLength = bitsToInt(getBits(respLenStart, respLenStart + 8)); | |||
output("Response Length", responseLength); | |||
const respBits = getBits(respLenStart + 8, respLenStart + 8 + responseLength * 8); | |||
output("Response", bitsToAscii(respBits)); | |||
} | } | ||
</script> | </script> | ||
</body> | </body> | ||
</html> | </html> | ||
Revision as of 09:36, 21 July 2025
OTA Packet Parser
Parsed Output:
| Field | Value |
|---|