Decoder incorrect in chirp but correct in node testing

Having trouble with a decoder for a browan industrial tracker, the payload decodes the gnss fine in node, but doesnt seem to decode them at all in chirpstack. is there a function or inbuilt being called thats not supported by the quickjs? appreciate any help.

//hex to binary function
function hex2bin(hex) {
    return (parseInt(hex, 16).toString(2)).padStart(8, '0');
}

// little-endian format
function littleendian(inputpayload) {
  let data = inputpayload.match(/../g);
  let buf = new ArrayBuffer(4);
  // Create a data view of it
  let view = new DataView(buf);
  // set bytes
  data.forEach(function (b, i) {view.setUint8(i, parseInt(b, 16));});
  // get an int32 with little endian
  let num = view.getInt32(0, 1);
  return num;
}

// GPS Lat calculate
function gnss_LatCal(firstbit, intput_lat) {
  if (firstbit==0) {
    let Lat_littleend_int = littleendian(intput_lat);
    //const Lat_bit_01 = (Lat_littleend_int| (0xF0000000)) ^ (0xF0000000);
    let Lat_bit_01 = (Lat_littleend_int| (0xF0000000)) ^ (0xF0000000);
    //const Lat_bit_02 = Lat_bit_01/1000000;
    let Lat_bit_02 = Lat_bit_01/1000000;
    return (Lat_bit_02.toString());
  } else {
    let Lat_littleend_int = littleendian(intput_lat);
    let Lat_bit_01 = Lat_littleend_int^0xFFFFFFFF;
    //const Lat_bit_02 = (Lat_bit_01| (0xF0000000)) ^ (0xF0000000);
    let Lat_bit_02 = (Lat_bit_01| (0xF0000000)) ^ (0xF0000000);
    //const Lat_bit_03 = Lat_bit_02/1000000;
    let Lat_bit_03 = Lat_bit_02/1000000;
    return -(Lat_bit_03.toString());
  }
}

// GPS Lat calculate
function gnss_LongCal(firstbit, intput_long){
  if (firstbit==0) {
    let Long_littleend_int = littleendian(intput_long);
    //const Long_bit_01 = (Long_littleend_int| (0xE0000000)) ^ (0xE0000000);
    let Long_bit_01 = (Long_littleend_int| (0xE0000000)) ^ (0xE0000000);
    //const Long_bit_02 = Long_bit_01/1000000;
    let Long_bit_02 = Long_bit_01/1000000;
    return (Long_bit_02.toString());
  } else {
    let Lat_littleend_int = littleendian(intput_long);
    let Long_bit_01 = Lat_littleend_int^0xFFFFFFFF;
    //const Long_bit_02 = (Long_bit_01| (0xE0000000)) ^ (0xE0000000);
    let Long_bit_02 = (Long_bit_01| (0xE0000000)) ^ (0xE0000000);
    //const Long_bit_03 = Long_bit_02/1000000;
    let Long_bit_03 = Long_bit_02/1000000;
    return -(Long_bit_03.toString());
  }
}

// GNSS position estimate calculate
function gnss_PositCal(intput_posit){
  let Posit_int_01 = parseInt(intput_posit,16);
  //const Posit_bit_01 = (Posit_int_01>>5);
  let Posit_bit_01 = (Posit_int_01>>5);
  //const Posit_bit_02 = Math.pow(2,(Posit_bit_01+2));
  let Posit_bit_02 = Math.pow(2,(Posit_bit_01+2));
  return (Posit_bit_02.toString());
}

//decoder function
function decodeUplink(input) {
  let b = input.bytes;
  let port = input.fPort;
  let decoded = {};

  // create the object to collect the data for returning the decoded payload
  if (port == 136) { // Industrial Tracker Sensor
    let indust_hex = b.toString("hex").substring(0, 2); // Sensors status HEX
    let indust_binary = hex2bin(indust_hex);
    let moving_st = indust_binary.substring(7, 8);      // moving mode
    let nognss_st = indust_binary.substring(4, 5);      // no GNSS fix status
    let gnsserror_st = indust_binary.substring(3, 4);   // GNSS error
    let battry_hex = b.toString("hex").substring(2, 4); // battery calculate
    let battry_int = parseInt(battry_hex, 16);
    let battry_volt = (25+battry_int)/10;
    let temperature_hex = b.toString("hex").substring(4, 6); //temperature calculate
    let temperature_int = parseInt(temperature_hex, 16);
    let temperature_final = temperature_int -32;
    /* Latitude as last reported by GNSS receiver - calculate */
    let gnssLat_hex = b.toString("hex").substring(6, 14);
    let gnssLatbit_hex = b.toString("hex").substring(12, 14); // Latitude Negative sign bit
    let gnssLatbit_neg = (hex2bin(gnssLatbit_hex)).toString().substring(4,5);
    let gnssLat_final = gnss_LatCal(gnssLatbit_neg,gnssLat_hex);
    /* Longitude as last reported by GNSS receiver - calculate */
    let gnssLong_hex = b.toString("hex").substring(14, 22);
    let gnssLongbit_hex = b.toString("hex").substring(20, 22); // Latitude Negative sign bit
    let gnssLongbit_neg = (hex2bin(gnssLongbit_hex)).toString().substring(3,4);
    let gnssLong_final = gnss_LongCal(gnssLongbit_neg,gnssLong_hex);
    /* position accuracy estimate - calculate */
    let gnssPosit_hex = b.toString("hex").substring(20, 22);
    let gnssPosit_final = gnss_PositCal(gnssPosit_hex);

    decoded.moving = parseInt(moving_st);                   // 1 - moving mode, 0 - stationary mode
    decoded.gnss_fix = parseInt(nognss_st);                 // 1 - no GNSS fix, 0 - GNSS fixed
    decoded.gnss_error = parseInt(gnsserror_st);            // 1 - GNSS error, 0 - GNSS OK
    decoded.batt_volt = parseFloat(battry_volt);
    decoded.temperature = parseFloat(temperature_final);
    decoded.latitude = parseFloat(gnssLat_final);
    decoded.longitude = parseFloat(gnssLong_final);
    decoded.pos_est = parseInt(gnssPosit_final);
  }
  return {data: decoded}
}

pl = "AAwykBYlDjIv/wg=";
buff = Buffer.from(pl, 'base64');
console.log(buff);

//Test function output
console.log(decodeUplink({bytes: buff, fPort: 136}));

output in node (expected in chirp)

<Buffer 00 0c 32 90 16 25 0e 32 2f ff 08>
{
  data: {
    moving: 0,
    gnss_fix: 0,
    gnss_error: 0,
    batt_volt: 3.7,
    temperature: 18,
    latitude: -31.123823,
    longitude: 150.94149,
    pos_est: 4
  }
}

actual decoded in chirpstack.

Hi,

Made some tests with https://github.com/browanofficial/Sensor-Decoder/blob/main/decoder_Industrial_Tracker_v1.0.1.js and this is what I’m getting with minor mods.

image

Where did you get this JS ?

BTW I’m using CS v4, with a node that emulate the browan industrial tracker payload. So I can’t confirm that everything is accurate.

Regards.

That is how my chirpstack decoded that same payload, thats why i put the 2 x examples there as i’m pulling my hair out.

so if this decodes correctly for the browan industrial tracker on yours? i have no idea exactly whats going on with mine :face_holding_back_tears:

actually, sorry it looks like it decoded incorrectly on yours as well. the correct decoding for the packet is

{
  data: {
    moving: 0,
    gnss_fix: 0,
    gnss_error: 0,
    batt_volt: 3.7,
    temperature: 18,
    latitude: -31.123823,
    longitude: 150.94149,
    pos_est: 4
  }
}

Let me clarify something…

I used this payload 080a368c49fc0d843d0269 into my node, with your codec to get the result I gave you.

With your data “AAwykBYIDjlv/wg=”, I’m getting the same result as you have temperature: null.

So my question was where or how did you get this payload ???

it got sent to me in a pdf from where i bought the sensors.

it is what was sent, with the console.log’s replaced to output the data and the main function to accept chirpstack v4 input type.

it appears to be the same payload you linked me too which i adjusted to work with v4.
so yeh idk, it decodes correctly in vscode/node but incorrectly in chirp v4 so ig i’m still lost.

this is what us being passed in (i’m just printing the buffer to check what it is the output is the json).

pl = "AAwykBYlDjIv/wg="
buff = Buffer.from(pl, 'base64');
bytes = new Uint8Array(buff);

console.log(bytes);

--
> this should be whats being passed in iirc.

Uint8Array(11) [
   0, 12, 50, 144,  22,
  37, 14, 50,  47, 255,
   8
]

you may have reminded me to pass it as a new Uint8Array, doing this i get a match of the same incorrect payload conversion. now i have something to work with :smiley:

thanks for the rubber duck decoding ric… this is the solution!
i’ll clone the official repo and push a pr soon.

//hex to binary function
function hex2bin(hex) {
    return (parseInt(hex, 16).toString(2)).padStart(8, '0');
}

// little-endian format
function littleendian(inputpayload) {
  let data = inputpayload.match(/../g);
  let buf = new ArrayBuffer(4);
  // Create a data view of it
  let view = new DataView(buf);
  // set bytes
  data.forEach(function (b, i) {view.setUint8(i, parseInt(b, 16));});
  // get an int32 with little endian
  let num = view.getInt32(0, 1);
  return num;
}

// GPS Lat calculate
function gnss_LatCal(firstbit, intput_lat) {
  if (firstbit==0) {
    let Lat_littleend_int = littleendian(intput_lat);
    const Lat_bit_01 = (Lat_littleend_int| (0xF0000000)) ^ (0xF0000000);
    const Lat_bit_02 = Lat_bit_01/1000000;
    return (Lat_bit_02.toString());
  } else {
    let Lat_littleend_int = littleendian(intput_lat);
    let Lat_bit_01 = Lat_littleend_int^0xFFFFFFFF;
    const Lat_bit_02 = (Lat_bit_01| (0xF0000000)) ^ (0xF0000000);
    const Lat_bit_03 = Lat_bit_02/1000000;
    return -(Lat_bit_03.toString());
  }
}

// GPS Lat calculate
function gnss_LongCal(firstbit, intput_long){
  if (firstbit==0) {
    let Long_littleend_int = littleendian(intput_long);
    const Long_bit_01 = (Long_littleend_int| (0xE0000000)) ^ (0xE0000000);
    const Long_bit_02 = Long_bit_01/1000000;
    return (Long_bit_02.toString());
  } else {
    let Lat_littleend_int = littleendian(intput_long);
    let Long_bit_01 = Lat_littleend_int^0xFFFFFFFF;
    const Long_bit_02 = (Long_bit_01| (0xE0000000)) ^ (0xE0000000);
    const Long_bit_03 = Long_bit_02/1000000;
    return -(Long_bit_03.toString());
  }
}

// GNSS position estimate calculate
function gnss_PositCal(intput_posit){
  let Posit_int_01 = parseInt(intput_posit,16);
  //const Posit_bit_01 = (Posit_int_01>>5);
  let Posit_bit_01 = (Posit_int_01>>5);
  //const Posit_bit_02 = Math.pow(2,(Posit_bit_01+2));
  let Posit_bit_02 = Math.pow(2,(Posit_bit_01+2));
  return (Posit_bit_02.toString());
}

//decoder function
function decodeUplink(input) {
  let b = new Buffer.from(input.bytes, 'hex');
  let port = input.fPort;
  let decoded = {};

  // create the object to collect the data for returning the decoded payload
  if (port == 136) { // Industrial Tracker Sensor
    let indust_hex = b.toString('hex').substring(0, 2); // Sensors status HEX
    let indust_binary = hex2bin(indust_hex);
    let moving_st = indust_binary.substring(7, 8);      // moving mode
    let nognss_st = indust_binary.substring(4, 5);      // no GNSS fix status
    let gnsserror_st = indust_binary.substring(3, 4);   // GNSS error
    let battry_hex = b.toString().substring(2, 4); // battery calculate
    let battry_int = parseInt(battry_hex, 16);
    let battry_volt = (25+battry_int)/10;
    let temperature_hex = b.toString("hex").substring(4, 6); //temperature calculate
    let temperature_int = parseInt(temperature_hex, 16);
    let temperature_final = temperature_int -32;
    /* Latitude as last reported by GNSS receiver - calculate */
    let gnssLat_hex = b.toString("hex").substring(6, 14);
    let gnssLatbit_hex = b.toString("hex").substring(12, 14); // Latitude Negative sign bit
    let gnssLatbit_neg = (hex2bin(gnssLatbit_hex)).toString().substring(4,5);
    let gnssLat_final = gnss_LatCal(gnssLatbit_neg,gnssLat_hex);
    /* Longitude as last reported by GNSS receiver - calculate */
    let gnssLong_hex = b.toString("hex").substring(14, 22);
    let gnssLongbit_hex = b.toString("hex").substring(20, 22); // Latitude Negative sign bit
    let gnssLongbit_neg = (hex2bin(gnssLongbit_hex)).toString().substring(3,4);
    let gnssLong_final = gnss_LongCal(gnssLongbit_neg,gnssLong_hex);
    /* position accuracy estimate - calculate */
    let gnssPosit_hex = b.toString("hex").substring(20, 22);
    let gnssPosit_final = gnss_PositCal(gnssPosit_hex);

    decoded.moving = parseInt(moving_st);                   // 1 - moving mode, 0 - stationary mode
    decoded.gnss_fix = parseInt(nognss_st);                 // 1 - no GNSS fix, 0 - GNSS fixed
    decoded.gnss_error = parseInt(gnsserror_st);            // 1 - GNSS error, 0 - GNSS OK
    decoded.batt_volt = parseFloat(battry_volt);
    decoded.temperature = parseFloat(temperature_final);
    decoded.latitude = parseFloat(gnssLat_final);
    decoded.longitude = parseFloat(gnssLong_final);
    decoded.pos_est = parseInt(gnssPosit_final);
  }
  return {data: decoded}
}

Hi ccall,
Had the same issue, thanks! Have you been able to test downlinks on the browan industrial tracker? I am missing an example in the description and the description is contradictory. At one point it says downlink payload length of 33 bytes and further down in the description it explains up to byte 31 (RFU?). Another question is whether it is possible to set values that are not to be changed to 00? If you have an example that worked, I would be grateful.

sorry anop, i havent tried with the downlinks yet.

check back with me in a few weeks or a few months - i was more interested in getting the coords decoded so i could use them :smiley:

this is also a better more cut down version of the payload decoder…

//Browan version:1.0
function twosCompLat(inputNum, comtimes) {
  let count02 = (Math.pow(2, comtimes + 1)) - 1;
  let final_Lat;
  if ((inputNum >> comtimes) == 0) {
    final_Lat = inputNum;
    return final_Lat;
  } else {
    final_Lat = -(inputNum ^ count02) - 1;
    return final_Lat;
  }
}

function twosCompLon(firstbit, inputNum, comtimes) {
  let count02 = (Math.pow(2, comtimes + 1)) - 1;
  let final_Long;
  if (firstbit == 0) {
    final_Long = inputNum;
    return final_Long;
  } else {
    final_Long = -(inputNum ^ count02) - 1;
    return final_Long;
  }
}

function decodeUplink(input) {
  let bytes = input.bytes;
  let port = input.fPort;
  let decoded = {};
  switch (port) {
    case 136:
      decoded.MovingMode = ((bytes[0] - ((bytes[0] >> 4) * 16)) % 8) % 2;
      decoded.NoGNSSFix = (bytes[0] - ((bytes[0] >> 4) * 16)) >> 3;
      decoded.GNSSerror = (bytes[0] >> 4);
      decoded.batt_volt = (25 + (bytes[1] - ((bytes[1] >> 4) * 16))) / 10;
      decoded.temp = bytes[2] - 32;
      let int_lat = (bytes[3] + bytes[4] * 256 + bytes[5] * 65536 + (bytes[6] - ((bytes[6] >> 4))) * 16777216);
      let int_lon = (bytes[7] + bytes[8] * 256 + bytes[9] * 65536 + (bytes[10] - (((bytes[10] >> 5) << 1) * 16)) * 16777216);
      let bit_lon = ((bytes[10] >> 4) % 2);
      decoded.pos_est = Math.pow(2, ((bytes[10] >> 5) + 2));
      decoded.lat = twosCompLat(int_lat, 27) / 1000000;
      decoded.lon = twosCompLon(bit_lon, int_lon, 28) / 1000000;
      // Decoded data
      return {data: decoded};
  }
}

test input…

pl = "AAwykBYlDjIv/wg="
buff = Buffer.from(pl, 'base64');
bytes = new Uint8Array(buff);
console.log(buff);
console.log(bytes);

//Test function output
console.log(decodeUplink({bytes: bytes, fPort: 136}));

Thanks, I also saw that they have published a TTN version on github as part of their tabs sensors:

Regarding downlinks has been resolved. Here is a downlink example, even though I have not yet been able to test it:

001E00000000000000605400000F000000010314053C0000003C002C010203140A

Values in the example:
Stationary Threshold 30
Stationary Mode reporting interval 21600
Time for turn off GPS 15
Time for check the GPS status (Full Tracking Mode) 0
Tracking Type Periodic GPS
Number of satellites 3
Satellites’ signal strength (dBHz) 20
Time for check satellites 5
Moving Mode reporting interval 60
Time for turn off GPS 60
Time for check the GPS status (Full Tracking Mode) 300
Tracking Type Full Tracking
Number of satellites 3
Satellites’ signal strength (dBHz) 20
Time for check satellites 10

where did you get that info, can you link the reference. I have a manual i found somewhere maybe it was on their site… but it doesn’t seem to line up with whats happening with the device. looks like they rejected my pr to make it chirp v4 compatible lol

Sorry for the delay. You will find info about downlinks in the reference manual appendix:

Industrial Tracker Reference Manual

I’ll have to try decode your downlink message and see if it makes sense to me broken down as I would want to make sure its right before i start sending it :smiley: