# Decoding Trackio (Tabs Object Locator) GPS data with Node-RED JS

**URL:** <https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276>\
**Category:** End Devices (Nodes)\
**Created:** [March 24, 2019, 5:11pm UTC](https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276 "2019-03-24T17:11:36Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![IoTLatam](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/iotlatam/32/25844_2.png) [@IoTLatam](https://www.thethingsnetwork.org/forum/u/IoTLatam)\
**Post date:** [March 24, 2019, 5:11pm UTC](https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276/1 "2019-03-24T17:11:36Z")

</div>

Hey there, I was wondering if anyone could help me decoding a GPS device I got. I’ve been searchig everywhere and have not been able to find out how to parse a multy-byte value that uses a fraction of those bytes for other purposes. On the other hand, I achieved retrieveng the hex.  
If there is anyone who could help me I would be very happy lol  
This is the documentation I have of the device, the multy byte values are represented in little-endian encoding…  
 ![image](https://www.thethingsnetwork.org/forum/uploads/default/original/3X/f/8/f89d7dfcc231c89170a002fd41d93b227af88f03.png)  
And this is the HEX I need to get decoded: “08fe3962ff010e78baca5b”

---

<div class="post-metadata">

**Author:** ![BoRRoZ](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/borroz/32/44234_2.png) [@BoRRoZ](https://www.thethingsnetwork.org/forum/u/BoRRoZ)\
**Post date:** [March 24, 2019, 5:14pm UTC](https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276/2 "2019-03-24T17:14:54Z")

</div>

and what’s the **type** and **manufacturer** of this device ?  
_maybe this is solved by another owner_

---

<div class="post-metadata">

**Author:** ![IoTLatam](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/iotlatam/32/25844_2.png) [@IoTLatam](https://www.thethingsnetwork.org/forum/u/IoTLatam)\
**Post date:** [March 24, 2019, 5:45pm UTC](https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276/3 "2019-03-24T17:45:02Z")

</div>

Hey! It’s a Trackio device, but already searched with all that info and nothing ☹I forgotted to mention that signed values use two´s complement encoding 🤐

---

<div class="post-metadata">

**Author:** ![arjanvanb](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/arjanvanb/32/46626_2.png) [@arjanvanb](https://www.thethingsnetwork.org/forum/u/arjanvanb)\
**Post date:** [March 24, 2019, 6:16pm UTC](https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276/4 "2019-03-24T18:16:07Z")

</div>

> [@IoTLatam](#):
>
> parse a multy-byte value that uses a fraction of those bytes for other purposes

First, apply a mask to ignore the high bits, like `bytes[6] & 0x0F`. Next, JavaScript’s bitwise operators always assume two’s complement, when handling 32 bits. But, as you’re ignoring the high bits, some [sign extension](https://en.wikipedia.org/wiki/Sign_extension) for the 3 or 4 bits that you’re ignoring is still needed… Something like, very much untested:

```javascript
// LSB, and bits 28-31 reserved for future use. Sign-extend to handle
// negative values; 0b00001000 = 0x08 and 0b11110000 = 0xF0:
var lat = bytes[3] | bytes[4]<<8 | bytes[5]<<16 
  | (bytes[6] & 0x0F | (bytes[6] & 0x08 ? 0xF0 : 0x00))<<24;

// LSB, and bits 29-31 used for accuracy. Sign-extend to handle
// negative values; 0b00010000 = 0x10 and 0b11100000 = 0xE0:
var lng = bytes[7] | bytes[8]<<8 | bytes[9]<<16
  | (bytes[10] & 0x1F | (bytes[10] & 0x10 ? 0xE0 : 0x00))<<24;

var accuracy = bytes[10]>>5;

```

Probably easier to shift 4 and 3 bytes too far to the left, and shift it back, as the bitwise operator `>>` is the [sign-propagating right shift](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift):

```javascript
// LSB, ignoring the 4 and 3 high bits, and sign-extending to 32 bits
// to support negative values, by shifting 4 or 3 bytes too far to
// the left (discarding those bits, as only 32 bits are preserved),
// followed by a sign-propagating right shift:
var lat = bytes[3] | bytes[4]<<8 | bytes[5]<<16 | bytes[6]<<28>>4;
var lng = bytes[7] | bytes[8]<<8 | bytes[9]<<16 | bytes[10]<<27>>3;

```

To parse a few bits from a single byte, see:

> [@Abeeway Microtracker Decoder](https://www.thethingsnetwork.org/forum/t/abeeway-microtracker-decoder/22473/7):
>
> ```javascript
> // Gets the unsigned numeric value of the given zero-based bit(s)
> function bits(value, lsb, msb) {
> var len = msb - lsb + 1;
> var mask = (1<<len) - 1;
> return value>>lsb & mask;
> }
> 
> // Gets the boolean value of the given bit
> function bit(value, bit) {
> return (value & (1<<bit)) > 0;
> }
> 
> ```

…which you can use for:

```javascript
var voltage = (25 + bits(byte[1], 0, 3)) / 10;
var capacity = 100 * (bits(byte[1], 4, 7) / 15);
var accuracy = bits(byte[10], 5, 7);

```

---

<div class="post-metadata">

**Author:** ![IoTLatam](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/iotlatam/32/25844_2.png) [@IoTLatam](https://www.thethingsnetwork.org/forum/u/IoTLatam)\
**Post date:** [March 26, 2019, 2:55pm UTC](https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276/5 "2019-03-26T14:55:07Z")

</div>

I want to thank you very very much for your complete and perfect explanation and answer! It worked out perfectly!!  
Here is the Node Red flow! 🕶

```
[{"id":"cbe4d8e3.b361e8","type":"inject","z":"1b040b7a.d3b095","name":"Payload GPS","topic":"","payload":"{\"topic\":\"iotlatam1/payloads_ul\",\"payload\":{\"payloads_ul\":{\"id\":1553522985733,\"deveui\":\"58a0cb0000403be3\",\"timestamp\":\"2019-03-25T14:09:45.733Z\",\"devaddr\":279538127,\"live\":false,\"dataFrame\":\"CPs/nQECDmW+yjs=\",\"fcnt\":256,\"session_id\":\"686c68e2-7e7b-410c-9d96-bd9526ef166f\",\"port\":136,\"rssi\":-87,\"snr\":9.8,\"freq\":916200012.2070312,\"sf_used\":7,\"dr_used\":\"SF7BW125\",\"cr_used\":\"4/5\",\"device_redundancy\":1,\"time_on_air_ms\":71.936,\"gtw_info\":[{\"gtw_id\":\"000800fffe4a44cd\",\"rssi\":-87,\"snr\":9.8}],\"decrypted\":true}},\"qos\":0,\"retain\":false,\"_msgid\":\"316ad2a4.6207fe\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":350,"y":220,"wires":[["8bc01482.90c508"]]},{"id":"bcd2f116.331cc","type":"function","z":"1b040b7a.d3b095","name":"DECODE","func":"// Primer paso es decodificar Base64 a Hex\n\n var data = msg.payload.dataFrame\n bytes = new Buffer(data,'Base64')\n hex = bytes.toString('hex')\n\n \n \nvar bytes = new Buffer(data, 'base64');\n var temp = bytes[2]-37;\n // Gets the zero-based unsigned numeric value of the given bit(s)\n function bits(value, lsb, msb) {\n var len = msb - lsb + 1;\n var mask = (1<<len) - 1;\n return value>>lsb & mask;\n }\n\n // Gets the boolean value of the given bit\n function bit(value, bit) {\n return (value & (1<<bit)) > 0;\n } \n \n var voltage = (25 + bits(bytes[1], 0, 3)) / 10;\n var capacity = 100 * (bits(bytes[1], 4, 7) / 15);\n var accuracy = bits(bytes[10], 5, 7);\n var status = bit(bytes[0],3)\n \n // LSB, and bits 28-31 reserved for future use. Sign-extend to handle\n// negative values; 0b00001000 = 0x08 and 0b11110000 = 0xF0:\nvar lat = bytes[3] | bytes[4]<<8 | bytes[5]<<16 | (bytes[6] & 0x0F | (bytes[6] & 0x08 ? 0xF0 : 0x00))<<24;\n\n\n// LSB, and bits 29-31 used for accuracy. Sign-extend to handle\n// negative values; 0b00010000 = 0x10 and 0b11100000 = 0xE0:\n//var lon = bytes[7] | bytes[8]<<8 | bytes[9]<<16 | (bytes[10] & 0x1F | (bytes[10] & 0x10 ? 0xE0 : 0x00))<<24;\nvar lon = bytes[7] | bytes[8]<<8 | bytes[9]<<16\n | (bytes[10] & 0x1F | (bytes[10] & 0x10 ? 0xE0 : 0x00))<<24;\n\n \n\n \n \n msg.payload.data= data \n msg.payload.hex= hex\n msg.payload.lat= lat/1000000\n msg.payload.lon = lon/1000000\n msg.payload.status = status\n msg.payload.volt = voltage\n msg.payload.capacity = capacity\n msg.payload.temp = temp\n msg.payload.accu = accuracy\n \nreturn msg;","outputs":1,"noerr":0,"x":720,"y":220,"wires":[["4573bcf5.5aa244"]]},{"id":"4573bcf5.5aa244","type":"debug","z":"1b040b7a.d3b095","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":850,"y":220,"wires":[]},{"id":"8bc01482.90c508","type":"change","z":"1b040b7a.d3b095","name":"Cambiamos Payload","rules":[{"t":"move","p":"payload.payload.payloads_ul","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":220,"wires":[["bcd2f116.331cc"]]}]
```

---

<div class="post-metadata">

**Author:** ![arjanvanb](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/arjanvanb/32/46626_2.png) [@arjanvanb](https://www.thethingsnetwork.org/forum/u/arjanvanb)\
**Post date:** [March 26, 2019, 6:25pm UTC](https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276/6 "2019-03-26T18:25:36Z")

</div>

Nice. Peeking at your DECODE node, a few remarks though 😉

```javascript
var temp = bytes[2] - 37;

```

Your documentation shows that bit 7 of `byte[2]` is RFU, _reserved for future use_. So, while it might always be zero, it’s safer to exclude it. Also, the documentation says to subtract 32, not 37? So, I’d use one of:

```javascript
// Bit 7 is RFU; exclude. 0x7F = 0b01111111
var temp = (bytes[2] & 0x7F) - 32;
// Or:
var temp = bits(bytes[2], 0, 7) - 32;

```

Accuracy could be converted to meters:

```javascript
// Accuracy in meters; 1 << x+2 is the same as Pow(2, x+2) for x < 32
var accuracy = 1 << bits(bytes[10], 5, 7) + 2;

```

Also, you’re defining `bytes` two times.

And for future readers, a decoder for a TTN Console Payload Format could be something like:

```javascript
/**
 * Decoder for Trackio GPS device.
 *
 * Test with: 08FE3962FF010E78BACA5B
 */
function Decoder(bytes, port) {
  var decoded = {};

  // Some "common status format"
  decoded.status = bits(bytes[0], 4, 7);

  decoded.battery = {
    voltage: (25 + bits(bytes[1], 0, 3)) / 10,
    capacity: 100 * (bits(bytes[1], 4, 7) / 15)
  };

  // Bit 7 is RFU; exclude
  decoded.temperature = bits(bytes[2], 0, 7) - 32;

  decoded.position = {
    gnssFix: bit(bytes[0], 3),
    // LSB, ignoring the 4 high bits (RFU) and sign-extending to 32 bits
    // to support negative values, by shifting 4 bytes too far to the
    // left (which discards those bits, as only 32 bits are preserved),
    // followed by a sign-propagating right shift:
    lat: (bytes[3] | bytes[4] << 8 | bytes[5] << 16
      | bytes[6] << 28 >> 4) / 1e6,
    // Likewise, ignoring the 3 high bits (used for accuracy):
    lng: (bytes[7] | bytes[8] << 8 | bytes[9] << 16
      | bytes[10] << 27 >> 3) / 1e6,
    // Accuracy in meters; 1 << x+2 is the same as Pow(2, x+2) for x < 32
    accuracy: 1 << bits(bytes[10], 5, 7) + 2
  };

  return decoded;
}

// Gets the zero-based unsigned numeric value of the given bit(s)
function bits(value, lsb, msb) {
  var len = msb - lsb + 1;
  var mask = (1 << len) - 1;
  return value >> lsb & mask;
}

// Gets the boolean value of the given bit
function bit(value, bit) {
  return (value & (1 << bit)) > 0;
}

```

For `08FE3962FF010E78BACA5B` this would show:

```json
{
  "battery": {
    "capacity": 100,
    "voltage": 3.9
  },
  "position": {
    "accuracy": 16,
    "gnssFix": true,
    "lat": -33.423518,
    "lng": -70.600072
  },
  "status": 0,
  "temperature": 25
}

```

If you have any specific details about a specific model, then please edit your title to add that to “Trackio”. Also, if you have details about the “common status format” from the first byte, then please add those? Thanks!

---

<div class="post-metadata">

**Author:** ![arjanvanb](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/arjanvanb/32/46626_2.png) [@arjanvanb](https://www.thethingsnetwork.org/forum/u/arjanvanb)\
**Post date:** [February 18, 2020, 8:16am UTC](https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276/9 "2020-02-18T08:16:12Z")

</div>

The device seems to (also) be branded [Tabs Object Locator](https://connectedthings.store/gb/tabs-object-locator-lorawan-gps-tracker.html).

![status field](https://www.thethingsnetwork.org/forum/uploads/default/original/3X/f/4/f4c7184a1af5ef50953dd0b5542195bf1a6fe318.png)

[Tabs\_ObjectLocator.pdf](https://www.thethingsnetwork.org/forum/uploads/short-url/pyAskbn2PnxI1eG3vHOED9Mckki.pdf) (789.6 KB)

---

<div class="post-metadata">

**Author:** ![arainsford](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/arainsford/32/35697_2.png) [@arainsford](https://www.thethingsnetwork.org/forum/u/arainsford)\
**Post date:** [May 29, 2021, 9:46pm UTC](https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276/10 "2021-05-29T21:46:41Z")

</div>

This is an old thread, but hoping you can help. I tried to implement your code in Python. It doesn’t work for lat & lng.

```auto
# TABS Object Tracker
def bits(value, lsb, msb):
    len = msb - lsb + 1
    mask = (1 << len) - 1
    return value >> lsb & mask

def bit(value, bit):
    return (value & (1 << bit)) > 0

hex='08FE3962FF010E78BACA5B'
bytes=bytes.fromhex(hex)
status = bits(bytes[0], 4, 7)
voltage = (25 + bits(bytes[1], 0, 3)) / 10
capacity = 100 * (bits(bytes[1], 4, 7) / 15)
temperature = bits(bytes[2], 0, 7) - 32
lat = (bytes[3] + (bytes[4] << 8) +(bytes[5] << 16) + (bytes[6] << 28) >> 4) / 1e6
lng = (bytes[7] | bytes[8] << 8 | bytes[9] << 16 | bytes[10] << 27 >> 3) / 1e6
accuracy = 1 << bits(bytes[10], 5, 7) + 2
print(status, voltage, capacity, temperature, lat,lng, gnssFix, accuracy)
```

I get this result:  
0 3.9 100.0 25 234.889206 1540.012664 True 16

---

<div class="post-metadata">

**Author:** ![kersing](https://www.thethingsnetwork.org/forum/user_avatar/www.thethingsnetwork.org/kersing/32/44777_2.png) [@kersing](https://www.thethingsnetwork.org/forum/u/kersing)\
**Post date:** [May 29, 2021, 10:56pm UTC](https://www.thethingsnetwork.org/forum/t/decoding-trackio-tabs-object-locator-gps-data-with-node-red-js/24276/11 "2021-05-29T22:56:17Z")

</div>

When posting code please spend 30 seconds on [formatting](https://www.thethingsnetwork.org/forum/t/how-do-i-format-my-forum-post-howto/8735) it properly. Makes it easier to read (especially python which depends on whitespace) for people trying to help out.
