|
|
| Line 97: |
Line 97: |
| </head> | | </head> |
| <body> | | <body> |
|
| |
| <h2>OTA Packet Parser</h2>
| |
| <textarea id="hexInput" placeholder="Paste OTA packet hex here..."></textarea><br>
| |
| <button onclick="parseOTAPacket()">Parse OTA Packet</button>
| |
|
| |
| <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(b =>
| |
| parseInt(b, 16).toString(2).padStart(8, '0')).join('');
| |
| }
| |
|
| |
| function bitsToInt(bits) {
| |
| return parseInt(bits, 2);
| |
| }
| |
|
| |
| function bitsToSignedInt(bits) {
| |
| let value = parseInt(bits, 2);
| |
| const max = Math.pow(2, bits.length);
| |
| return value >= max / 2 ? value - max : value;
| |
| }
| |
|
| |
| function bigIntFromBits(bits) {
| |
| return BigInt('0b' + bits).toString();
| |
| }
| |
|
| |
| function bitsToAscii(bits) {
| |
| let ascii = '';
| |
| for (let i = 0; i < bits.length; i += 8) {
| |
| const byte = bits.slice(i, i + 8);
| |
| ascii += String.fromCharCode(parseInt(byte, 2));
| |
| }
| |
| return ascii;
| |
| }
| |
|
| |
| function parseOTAPacket() {
| |
| const hex = document.getElementById("hexInput").value.trim().toLowerCase();
| |
| const resultBody = document.querySelector("#resultTable tbody");
| |
| resultBody.innerHTML = "";
| |
|
| |
| if (!hex.startsWith("24")) {
| |
| alert("Packet must start with hex '24'");
| |
| return;
| |
| }
| |
|
| |
| const bits = hexToBits(hex);
| |
| const getBits = (start, end) => bits.slice(start, end);
| |
| const output = (label, value) => {
| |
| const row = document.createElement("tr");
| |
| row.innerHTML = `<td>${label}</td><td>${value}</td>`;
| |
| resultBody.appendChild(row);
| |
| };
| |
|
| |
| // Parse known fixed fields
| |
| const imei = bigIntFromBits(getBits(25, 75)); // Correct IMEI parse
| |
| 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);
| |
| const command = bitsToAscii(cmdBits);
| |
| output("Command", command);
| |
|
| |
| 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);
| |
| const response = bitsToAscii(respBits);
| |
| output("Response", response);
| |
| }
| |
| </script>
| |
| Field
|
Size (bits)
|
Bit Range
|
Description
|
Breakdown
|
| Header
|
| Start byte |
8 |
0–7 |
Starting character $ (ASCII value 36) |
$
|
| Data length |
12 |
8–18 |
2-byte length of the data following the header |
—
|
| Num of data packets |
5 |
19–23 |
Number of packets (0–32) |
0 - 32
|
| IMEI |
50 |
24–74 |
Unique device identifier |
Integer type: e.g., 887744556677882
|
| Packet Type |
5 |
75–79 |
Type of packet |
00 - Device info
01 - Alert packet
02 - OTA pkt
03 - Error Packet
04 - Device configuration packet
05 - IP configuration packet
|
| Data
|
| OTA Source |
5 |
— |
Source of OTA |
00 - OTA_SRC_SERIAL
01 - OTA_SRC_SMS
02 - OTA_SRC_IP_TRANSIGHT
03 - OTA_SRC_IP_CUSTOMER
|
| Error status |
8 |
— |
Non-zero value indicates an error |
0 = No Error, Non-zero = Error
|
| Cell Number |
51 |
— |
First bit (MSB) indicates presence of country code. Remaining 50 bits = mobile number |
Example: 400D6276ACF52 (+919784312658)
|
| Date & Time |
32 |
— |
UTC Timestamp |
UTC time in seconds
|
| Timezone |
8 |
— |
Timezone in quarter-hours (e.g., 22 = +5:30) |
22 × 15 mins = 330 mins = +5:30
|
| Command Length |
8 |
— |
Length of command string |
—
|
| Command |
up to 600 bits (75 bytes) |
— |
Command with arguments, null-terminated string |
Example: SET CUIOCFG:IO1-DI-IGN*IO2_AN_GEN*IO3-DI-PBN*IO4-DO-IMB*
|
| Response Length |
8 |
— |
Length of response string |
—
|
| Response |
up to 512 bits (64 bytes) |
— |
Response string from device |
Example: IOCFG:IO1-DI-IGN*IO2-AN_GEN*IO3-DI-PBN*IO4-DO-IMB*
|
| Tail
|
| End Character |
8 |
0–7 |
End character * (ASCII value 42) |
*
|
| CRC |
8 |
8–15 |
8-bit XOR CRC from $ to * (excluding both) |
—
|
Sample Packet
{"HEX:240340e1cabd09208ca210000000000000006824646f160947455420545355524c1c55524c3a5443503a3134312e3134372e3132382e3135303a383738382aa2",
"cell_no": 0,
"command": "GET TSURL",
"command_length": 9,
"dateTime": 1747215471,
"dateTime_tz": "2025-05-14 15:07:51.000",
"error_status": 0,
"imei": 860187062240357,
"insert_time": "Wed, 14 May 2025 09:38:03 GMT",
"ota_source": 2,
"packetType": 2,
"response": "URL:TCP:141.147.128.150:8788",
"response_length": 28,
"send_time": "2025-05-14 15:07:51",
"timezone": 22}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OTA Packet Parser</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
textarea { width: 100%; height: 100px; font-family: monospace; margin-bottom: 10px; }
button {
padding: 10px 20px; border-radius: 20px;
font-weight: bold; background-color: #680022; color: white;
border: none; cursor: pointer;
}
button:hover { background-color: #680022; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { border: 1px solid #ccc; padding: 8px; }
th { background-color: #680022; color: white; }
</style>
</head>
<body>