Looking at the documentation, the payload you’re showing for “position type” 9 does not provide GPS coordinates. Instead, it gives you up to 4 MAC addresses of WiFi access points. I guess you should also get other payloads, that do provide the GPS data. Or maybe you need to move outside to get a GPS fix.
The following has only been tested with 032CD1890900C46E1FF44B9EC5C83A355A3898A6
and 0358D895090EC46E1FF44B9EB76466B3B87454AD500959CA1ED4AD525E67DA14A1AC
, the latter yielding:
{
"ack": 0,
"age": 112,
"battery": 3.99,
"data": 9,
"position_type": "WIFI BSSIDs",
"stations": [
{
"mac_address": "c4:6e:1f:f4:4b:9e",
"rssi": -73
},
{
"mac_address": "64:66:b3:b8:74:54",
"rssi": -83
},
{
"mac_address": "50:09:59:ca:1e:d4",
"rssi": -83
},
{
"mac_address": "52:5e:67:da:14:a1",
"rssi": -84
}
],
"status": {
"mode": {
"code": 2,
"message": "Permanent tracking"
},
"moving": 0,
"on_demand": 0,
"periodic": 0,
"sos": 1,
"tracking": 1
},
"temperature": 31.46,
"type": "POSITION"
}
So, most of the following is really untested, so you’ve got a lot of testing to do now (and I’d suggest you thoroughly compare the code to the documentation as well…).
For the GPS coordinates, in function degree(bytes)
, you might actually need:
var d = bytes[0] * 0x1000000 + bytes[1] * 0x10000 + bytes[2] * 0x100;
But we need some test payload to confirm that. I guess some payload types (and the conditions for which you get them) would be nice for future readers as well.
Click for old version of the decoder, or see posts below for updates
//
// SEE UPDATED VERSION(S) IN FORUM POSTS BELOW
//
function Decoder(bytes, port) {
function step_size(lo, hi, nbits, nresv) {
return 1.0 / ((((1<<nbits) - 1) - nresv) / (hi - lo));
}
function mt_value_decode(value, lo, hi, nbits, nresv) {
return ((value - nresv / 2) * step_size(lo, hi, nbits, nresv) + lo);
}
// Gets the zero-based unsigned numeric value of the given bit(s)
function bits(value, lsb, msb) {
var len = (msb ? msb : lsb) - lsb + 1;
var mask = (1<<len) - 1;
return value>>lsb & mask;
}
// Gets a hexadecimal representation with leading zeroes for each byte
function hex(bytes, separator) {
return bytes.map(function (b) {
return ("0" + b.toString(16)).substr(-2);
}).join(separator || "");
}
// Decodes 4 bytes into a signed integer, MSB
function int32(bytes) {
// JavaScript bitwise operators always work with 32 bits signed integers
return bytes[0]<<24 | bytes[1]<<16 | bytes[2]<<8 | bytes[3];
}
// Decodes 4 bytes into an unsigned integer, MSB
function uint32(bytes) {
// Force an unsigned 32 bits integer using zero-fill right shift:
return (bytes[0]<<24 | bytes[1]<<16 | bytes[2]<< 8 | bytes[3])>>>0;
// Alternatively, don't use bitwise operators at all:
// return bytes[0] * 0x1000000 + bytes[1] * 0x10000 + bytes[2] * 0x100 + bytes[3];
}
// Decodes the decimal degree for either latitude or longitude
function degree(bytes) {
// TODO This is probably an unsigned number, hence needs:
// var d = (bytes[0]<<24 | bytes[1]<<16 | bytes[2]<<8)>>>0
// or:
// var d = bytes[0] * 0x1000000 + bytes[1] * 0x10000 + bytes[2] * 0x100;
var d = bytes[0]<<24 | bytes[1]<<16 | bytes[2]<<8;
if (d > 0x7FFFFFFF) {
d = d - 0x100000000;
}
return d / 1e7;
}
// Decodes 1 to 4 MAC addresses and their RSSI
function mac_rssi(bytes) {
var items = [];
for (var offset = 0; offset < bytes.length; offset += 7) {
items.push({
// Get hexadecimal bytes with a leading zero
mac_address: hex(bytes.slice(offset, offset + 6), ":"),
// Sign-extend to 32 bits to support negative values
rssi: bytes[offset + 6]<<24>>24,
});
}
return items;
}
function message(value, messages) {
return {
code: value,
message: value > messages.length ? "UNKNOWN" : messages[value]
};
}
var decoded = {};
var i;
var type = bytes[0];
if (type !== 0x00) {
// All message types, except for Frame pending messages, share the same header
decoded.status = {
mode: message(bits(bytes[1], 5, 7), ["Standby", "Motion tracking", "Permanent tracking",
"Motion start/end tracking", "Activity tracking", "OFF"]),
sos: bits(bytes[1], 4),
tracking: bits(bytes[1], 3),
moving: bits(bytes[1], 2),
periodic: bits(bytes[1], 1),
on_demand: bits(bytes[1], 0)
};
decoded.battery = Math.round(100 * mt_value_decode(bytes[2], 2.8, 4.2, 8, 2)) / 100;
decoded.temperature = Math.round(100 * mt_value_decode(bytes[3], -44, 85, 8, 2)) / 100;
decoded.ack = bits(bytes[4], 4, 7);
decoded.data = bits(bytes[4], 0, 3);
}
switch (type) {
case 0x00:
decoded.type = "FRAME PENDING";
decoded.token = bytes[1];
break;
case 0x03:
decoded.type = "POSITION";
switch (decoded.data) {
case 0:
decoded.position_type = "GPS fix";
decoded.age = mt_value_decode(bytes[5], 0, 2040, 8, 0);
decoded.latitude = degree(bytes.slice[6]);
decoded.longitude = degree(bytes.slice[9]);
// Estimated Horizontal Position Error
decoded.ehpe = mt_value_decode(bytes[12], 0, 1000, 8, 0);
break;
case 1:
decoded.position_type = "GPS timeout";
decoded.timeout_cause = message(bytes[5], ["User timeout cause"]);
for (i = 0; i < 4; i++) {
// Carrier over noise (dBm) for the i-th satellite seen
decoded["cn" + i] = mt_value_decode(bytes[6 + i], 0, 2040, 8, 0);
}
break;
case 2:
// Documented as obsolete
decoded.error = "UNSUPPORTED";
break;
case 3:
decoded.position_type = "WIFI timeout";
for (i = 0; i < 6; i++) {
decoded["v_bat" + (i + 1)] = mt_value_decode(bytes[5 + i], 2.8, 4.2, 8, 2);
}
break;
case 4:
decoded.position_type = "WIFI failure";
for (i = 0; i < 6; i++) {
// Most of time a WIFI timeout occurs due to a low battery condition
decoded["v_bat" + (i + 1)] = mt_value_decode(bytes[5 + i], 2.8, 4.2, 8, 2);
}
decoded.error = message(bytes[11], ["WIFI connection failure", "Scan failure",
"Antenna unavailable", "WIFI not supported on this device"]);
break;
case 5:
case 6:
decoded.position_type = "LP-GPS data";
// Encrypted, not described in the documentation
decoded.error = "UNSUPPORTED";
break;
case 7:
decoded.position_type = "BLE beacon scan";
decoded.age = mt_value_decode(bytes[5], 0, 2040, 8, 0);
// Remaining data: up to 4 beacons
decoded.beacons = mac_rssi(bytes.slice(6));
break;
case 8:
decoded.position_type = "BLE beacon failure";
decoded.error = message(bytes[5], ["BLE is not responding", "Internal error", "Shared antenna not available",
"Scan already on going", "No beacon detected", "Hardware incompatibility"]);
break;
// Test with: 0358D895090EC46E1FF44B9EB76466B3B87454AD500959CA1ED4AD525E67DA14A1AC
// or 032CD1890900C46E1FF44B9EC5C83A355A3898A6
case 9:
decoded.position_type = "WIFI BSSIDs";
decoded.age = mt_value_decode(bytes[5], 0, 2040, 8, 0);
// Remaining data: up to 4 WiFi BSSIDs
decoded.stations = mac_rssi(bytes.slice(6));
break;
default:
decoded.position_type = "UNSUPPORTED";
}
break;
case 0x04:
decoded.type = "ENERGY STATUS";
break;
case 0x05:
decoded.type = "HEARTBEAT";
break;
case 0x07:
// Activity status message and configuration message share the same identifier
var tag = bytes[5];
switch (tag) {
case 1:
decoded.type = "Activity Status";
decoded.activity_counter = uint32(bytes.slice(6, 10));
break;
case 2:
decoded.type = "Configuration";
for (i = 0; i < 5; i++) {
var offset = 6 + 5 * i;
decoded["param" + i] = {
type: bytes[offset],
value: uint32(bytes.slice(offset + 1, offset + 5))
};
}
break;
default:
decoded.error = "UNSUPPORTED";
}
break;
case 0x09:
decoded.type = "Shutdown";
break;
case 0xFF:
decoded.type = "Debug";
break;
default:
decoded.type = "UNSUPPORTED";
}
// Just some redundant debug info
decoded.debug = {
payload: hex(bytes),
length: bytes.length,
port: port
};
return decoded;
}