function Decoder(bytes) {
var Battery = bytes[2];
var d = (bytes[3]<<24) | (bytes[4] <<16) | (bytes[5]<<8) | (bytes[6]);
var e = (bytes[7]<<24) | (bytes[8] <<16) | (bytes[9]<<8) | (bytes[10]);
var Longitude = parseInt(e) * 0.000001;
var Latitude = parseInt(d) * 0.000001;
return {Battery:Battery,Latitude:Latitude,Longitude:Longitude};
Ive made the longitude decode work… but struggling for a correct latitude. also Im southern hemisphere… so the negative is likely causing issues with the decode…
i tried subtracting 180 and still not right.
// Sydney: -33.8688, 151.2092 = MSB FAD500 17129C, LSB 00D5FA 9C1217
// 8002 2C3252 64EFD2 42 4B 00
// Perth: -31.95224, 115.8614 == LSB 2D3252 66EFD2
// Stirling -31.896675, 115.815534
/*
80022B325261EFD2424100
8002 protocol and command
2B3252 longitude
61EFD2 latitude
42 gps
41 batt
00 preserved
"frm_payload": "gAIrMlJh79JCQQA=",
"decoded_payload": {
"Battery": 65,
"LatiHex": "D2EF61",
"Latitude": 149.2974828, ** still not right ???
"LongHex": "52322B",
"Longitude": 115.8160925
*/
function Decoder(bytes) {
//protocol version
var a = bytes[0];
//command id
var b = bytes[1];
//longitude - little endian
var c = (bytes[2] | bytes[3]<<8 | bytes[4]<<16 ) ;
//latitude - little endian
var d = (bytes[5] | bytes[6]<<8 | bytes[7]<<16 ) ;
//gps fix status and report type
var e = bytes[8];
//battery capacity
var f = bytes[9];
//preserved
var g = bytes[10];
//convert as per guide - little endian and then multiple/divide etc
var Longitude = parseInt(c) * 215 / 10 * 0.000001;
var Latitude = parseInt(d) *108 / 10 * 0.000001;
//check hex little endian is swapping correctly
var Long = c.toString(16).toUpperCase();
var Lati = d.toString(16).toUpperCase();
//display battery level - little endian
var Battery = parseInt(f)
return {LongHex:Long,LatiHex:Lati, Longitude:Longitude, Latitude:Latitude, Battery:Battery};
}
function decodeUplink(input) {
var data = input.bytes;
var valid = true;
if (typeof Decoder === "function") {
data = Decoder(data, input.fPort);
}
if (typeof Converter === "function") {
data = Converter(data, input.fPort);
}
if (typeof Validator === "function") {
valid = Validator(data, input.fPort);
}
if (valid) {
return {
data: data
};
} else {
return {
data: {},
errors: ["Invalid data received"]
};
}
}
I have come across the following kinda interesting code for the LT-20. Ive not yet made it work in TTN but its got some interesting (for me as a nonproficient codehacker manipulator).
I have questioned the github owner around the validity of the calculation as it doesnt match the PDF manual (or the image at the top of this thread).
function toLittleEndian(hex) {
var data = hex.match(/…/g);
// Create a buffer
var buf = new ArrayBuffer(4);
// Create a data view of it
var view = new DataView(buf);
// set bytes
data.forEach(function (b, i) {
view.setUint8(i, parseInt(b, 16));
});
// get an int32 with little endian
var num = view.getInt32(0, 1);
return num;
}
function consume(event) {
var payload = event.data.payload_hex;
var bits = Bits.hexToBits(payload);
var data = {};
var protocolVersion = Bits.bitsToUnsigned(bits.substr(0, 8));
var commandID = Bits.bitsToUnsigned(bits.substr(8, 8));
I seem to (with some help from AH [a friend], and the akenza.io team) made the following work within TTNv3.
I have been given some additional improvements, so might try those in the not too distant future. But for now this works for me.
//Functions Convert HEX to Bits
function hexToBits(hex){
var out = "";
for (var c of hex) {
switch (c) {
case '0': out += "0000"; break;
case '1': out += "0001"; break;
case '2': out += "0010"; break;
case '3': out += "0011"; break;
case '4': out += "0100"; break;
case '5': out += "0101"; break;
case '6': out += "0110"; break;
case '7': out += "0111"; break;
case '8': out += "1000"; break;
case '9': out += "1001"; break;
case 'a': out += "1010"; break;
case 'b': out += "1011"; break;
case 'c': out += "1100"; break;
case 'd': out += "1101"; break;
case 'e': out += "1110"; break;
case 'f': out += "1111"; break;
default: return "";
}
}
return out;
}
//Convert Bits to Signed INT
function bitsToSigned(bits){
var value = parseInt(bits, 2);
var limit = 1 << (bits.length - 1);
if (value >= limit) {
// Value is negative; calculate two's complement.
value = value - limit - limit;
}
return value;
}
function bitsToUnsigned(bits){
return parseInt(bits, 2);
}
function toLittleEndianSigned(hex) {
// Creating little endian hex DCBA
var hexArray = [];
while (hex.length >= 2) {
hexArray.push(hex.substring(0, 2));
hex = hex.substring(2, hex.length);
}
// seems to already be reversed so not needed
//hexArray.reverse();
hex = hexArray.join('');
// Hex to Bits
var hex2bits = hexToBits(hex);
// To signed int
var signedInt = bitsToSigned(hex2bits);
return signedInt;
}
function decodeUplink(input) {
var data = {};
//protocol version
//data.protocolVersion = (input.bytes[0] << 8);
//command id
//data.commandID = (input.bytes[1] << 8);
//longitude - little endian
//note - old method (works for positive only)
//data.longitude = (((input.bytes[2] ) + (input.bytes[3] << 8) + (input.bytes[4] << 16)) * 215) / 10 * 0.000001;
//note - new method (works for positive and negative)
data.longitude = ((toLittleEndianSigned(((input.bytes[2] ) + (input.bytes[3] << 8) + (input.bytes[4] << 16)).toString(16).toLowerCase())) * 215) / 10 * 0.000001;
//data.longraw = (input.bytes[2] ) + (input.bytes[3] << 8) + (input.bytes[4] << 16);
//data.longhexupper = ((input.bytes[2] ) + (input.bytes[3] << 8) + (input.bytes[4] << 16)).toString(16).toUpperCase();
//data.longhexlower = ((input.bytes[2] ) + (input.bytes[3] << 8) + (input.bytes[4] << 16)).toString(16).toLowerCase();
//data.longsignedint = (toLittleEndianSigned(((input.bytes[2] ) + (input.bytes[3] << 8) + (input.bytes[4] << 16)).toString(16).toLowerCase()));
//help from AH & akenza.io - with thanks!
//latitude - little endian
data.latitude = ((toLittleEndianSigned(((input.bytes[5] ) + (input.bytes[6] << 8) + (input.bytes[7] << 16)).toString(16).toLowerCase())) * 108) / 10 * 0.000001;
//data.latraw = (input.bytes[5] ) + (input.bytes[6] << 8) + (input.bytes[7] << 16);
//data.lathexupper = ((input.bytes[5] ) + (input.bytes[6] << 8) + (input.bytes[7] << 16)).toString(16).toUpperCase();
//data.lathexlower = ((input.bytes[5] ) + (input.bytes[6] << 8) + (input.bytes[7] << 16)).toString(16).toLowerCase();
//data.latisignedint = (toLittleEndianSigned(((input.bytes[5] ) + (input.bytes[6] << 8) + (input.bytes[7] << 16)).toString(16).toLowerCase()));
//gps fix status and report type
//data.reportType = (input.bytes[8] << 8);
var gps = Math.round((bitsToUnsigned(hexToBits((input.bytes[8] << 8).toString(16).toLowerCase()))/256)/32);
if (gps === 0) {
data.gps = "No fix";
} else if (gps == 1) {
data.gps = "2D fix";
} else if (gps == 2) {
data.gps = "3D fix";
}
//battery capacity
data.batterypercent = bitsToUnsigned(hexToBits((input.bytes[9] << 8).toString(16).toLowerCase()))/256;
//preserved
//data.preserved = (input.bytes[10] << 8);
var warnings = [];
/* if (data.battery < 30) {
warnings.push("low battery");
} */
return {
data: data,
warnings: warnings
};
}
Thanks for the script. But it has one major flaw. if the longitude or latitude has a leading zero in it’s hex-string, the “.toString(16)” would delete it. And this would lead to a wrong GPS coordinate.
I edited the script and added it to Github Gist.
I life in northern hemisphere, and this works for me. would be happy if anyone would give their feedback.