When sending 3 bytes, we’re basically not sending the 4th byte of each integer, which should be 0xFF
for negative numbers. Like for New York (40.712784, -74.005941, which would be sent as integers 407127 and -740059) we would send:
- For LSB:
0x573606 25B5F4
instead of0x57360600 25B5F4FF
(Least Significant Bit/Byte first, to match @JohanAdriaans’ encoding example above). - For MSB:
0x063657 F4B525
instead of0x00063657 FFF4B525
.
When decoding these 6 bytes back into two 32 bits signed integers, we need to compute the missing 4th and 8th bytes ourselves, to make JavaScript properly convert the negative values for us. Those bytes should be 0xFF
if the most significant bytes have their “high bit” set, which is called “sign extending”:
// LSB, Least Significant Bit/Byte first
// Sign-extend the 3rd and 6th bytes into a 4th and 8th byte:
lat = (b[0] | b[1]<<8 | b[2]<<16 | (b[2] & 0x80 ? 0xFF<<24 : 0)) / 10000;
lng = (b[3] | b[4]<<8 | b[5]<<16 | (b[5] & 0x80 ? 0xFF<<24 : 0)) / 10000;
// MSB, Most Significant Bit/Byte first
// Sign-extend the 1st and 4th bytes into leading bytes:
lat = ((b[0] & 0x80 ? 0xFF<<24 : 0) | b[0]<<16 | b[1]<<8 | b[2]) / 10000;
lng = ((b[3] & 0x80 ? 0xFF<<24 : 0) | b[3]<<16 | b[4]<<8 | b[5]) / 10000;
Alternatively, shift the most significant byte 8 bits too far to the left, and then shift it back, which will do the sign extension on the fly, as the bitwise operator >>
is the sign-propagating right shift:
lat = (b[0]<<24>>8 | b[1]<<8 | b[2]) / 10000;
lng = (b[3]<<24>>8 | b[4]<<8 | b[5]) / 10000;
Meanwhile, for the new production environment, payload functions should also include the function name, Decoder
. So, to support 6 byte coordinates with possible negative values:
function Decoder(b, port) {
// Amsterdam: 52.3731, 4.8924 = MSB 07FDD3 00BF1C, LSB D3FD07 1CBF00
// La Paz: -16.4896, -68.1192 = MSB FD7BE0 F59B18, LSB E07BFD 189BF5
// New York: 40.7127, -74.0059 = MSB 063657 F4B525, LSB 573606 25B5F4
// Sidney: -33.8688, 151.2092 = MSB FAD500 17129C, LSB 00D5FA 9C1217
// LSB, Least Significant Bit/Byte first! Your node likely sends MSB instead.
// Sign-extend the 3rd and 6th bytes into a 4th and 8th byte:
var lat = (b[0] | b[1]<<8 | b[2]<<16 | (b[2] & 0x80 ? 0xFF<<24 : 0)) / 10000;
var lng = (b[3] | b[4]<<8 | b[5]<<16 | (b[5] & 0x80 ? 0xFF<<24 : 0)) / 10000;
return {
location: {
lat: lat,
lng: lng
},
love: "TTN payload functions"
};
}
Alternatively, as coordinates in decimal degrees are -90…+90 for latitude, and -180…+180 for longitude: one could add 90 to the latitude and 180 to the longitude before sending, then send the positive values, and reverse that in the payload function.
And life can be made easy using libraries such as https://github.com/thesolarnomad/lora-serialization (though that one does not support 3 byte coordinates).