357 lines
7.4 KiB
JavaScript
357 lines
7.4 KiB
JavaScript
|
//Copied from from https://github.com/nherment/node-nmea/blob/master/lib/Helper.js
|
||
|
|
||
|
var m_hex = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
|
||
|
|
||
|
exports.toHexString = function(v) {
|
||
|
var lsn;
|
||
|
var msn;
|
||
|
|
||
|
msn = (v >> 4) & 0x0f;
|
||
|
lsn = (v >> 0) & 0x0f;
|
||
|
return m_hex[msn] + m_hex[lsn];
|
||
|
};
|
||
|
|
||
|
exports.padLeft = function(s, len, ch) {
|
||
|
while(s.length < len) {
|
||
|
s = ch + s;
|
||
|
}
|
||
|
return s;
|
||
|
};
|
||
|
|
||
|
// verify the checksum
|
||
|
exports.verifyChecksum = function(sentence, checksum) {
|
||
|
var q;
|
||
|
var c1;
|
||
|
var c2;
|
||
|
var i;
|
||
|
|
||
|
// skip the $
|
||
|
i = 1;
|
||
|
|
||
|
// init to first character
|
||
|
c1 = sentence.charCodeAt(i);
|
||
|
|
||
|
// process rest of characters, zero delimited
|
||
|
for( i = 2; i < sentence.length; ++i) {
|
||
|
c1 = c1 ^ sentence.charCodeAt(i);
|
||
|
}
|
||
|
|
||
|
// checksum is a 2 digit hex value
|
||
|
c2 = parseInt(checksum, 16);
|
||
|
|
||
|
// should be equal
|
||
|
return ((c1 & 0xff) === c2);
|
||
|
};
|
||
|
|
||
|
// generate a checksum for a sentence (no trailing *xx)
|
||
|
exports.computeChecksum = function(sentence) {
|
||
|
var c1;
|
||
|
var i;
|
||
|
|
||
|
// skip the $
|
||
|
i = 1;
|
||
|
|
||
|
// init to first character var count;
|
||
|
|
||
|
c1 = sentence.charCodeAt(i);
|
||
|
|
||
|
// process rest of characters, zero delimited
|
||
|
for( i = 2; i < sentence.length; ++i) {
|
||
|
c1 = c1 ^ sentence.charCodeAt(i);
|
||
|
}
|
||
|
|
||
|
return '*' + exports.toHexString(c1);
|
||
|
};
|
||
|
|
||
|
// =========================================
|
||
|
// field encoders
|
||
|
// =========================================
|
||
|
|
||
|
// encode latitude
|
||
|
// input: latitude in decimal degrees
|
||
|
// output: latitude in nmea format
|
||
|
// ddmm.mmm
|
||
|
exports.encodeLatitude = function(lat) {
|
||
|
var d;
|
||
|
var m;
|
||
|
var f;
|
||
|
var h;
|
||
|
var s;
|
||
|
var t;
|
||
|
if(lat === undefined) {
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
if(lat < 0) {
|
||
|
h = 'S';
|
||
|
lat = -lat;
|
||
|
} else {
|
||
|
h = 'N';
|
||
|
}
|
||
|
// get integer degrees
|
||
|
d = Math.floor(lat);
|
||
|
// degrees are always 2 digits
|
||
|
s = d.toString();
|
||
|
if(s.length < 2) {
|
||
|
s = '0' + s;
|
||
|
}
|
||
|
// get fractional degrees
|
||
|
f = lat - d;
|
||
|
// convert to fractional minutes
|
||
|
m = (f * 60.0);
|
||
|
// format the fixed point fractional minutes
|
||
|
t = m.toFixed(3);
|
||
|
if(m < 10) {
|
||
|
// add leading 0
|
||
|
t = '0' + t;
|
||
|
}
|
||
|
|
||
|
s = s + t + ',' + h;
|
||
|
return s;
|
||
|
};
|
||
|
|
||
|
// encode longitude
|
||
|
// input: longitude in decimal degrees
|
||
|
// output: longitude in nmea format
|
||
|
// dddmm.mmm
|
||
|
exports.encodeLongitude = function(lon) {
|
||
|
var d;
|
||
|
var m;
|
||
|
var f;
|
||
|
var h;
|
||
|
var s;
|
||
|
var t;
|
||
|
|
||
|
if(lon === undefined) {
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
if(lon < 0) {
|
||
|
h = 'W';
|
||
|
lon = -lon;
|
||
|
} else {
|
||
|
h = 'E';
|
||
|
}
|
||
|
|
||
|
// get integer degrees
|
||
|
d = Math.floor(lon);
|
||
|
// degrees are always 3 digits
|
||
|
s = d.toString();
|
||
|
while(s.length < 3) {
|
||
|
s = '0' + s;
|
||
|
}
|
||
|
|
||
|
// get fractional degrees
|
||
|
f = lon - d;
|
||
|
// convert to fractional minutes and round up to the specified precision
|
||
|
m = (f * 60.0);
|
||
|
// minutes are always 6 characters = mm.mmm
|
||
|
t = m.toFixed(3);
|
||
|
if(m < 10) {
|
||
|
// add leading 0
|
||
|
t = '0' + t;
|
||
|
}
|
||
|
s = s + t + ',' + h;
|
||
|
return s;
|
||
|
};
|
||
|
|
||
|
// 1 decimal, always meters
|
||
|
exports.encodeAltitude = function(alt) {
|
||
|
if(alt === undefined) {
|
||
|
return ',';
|
||
|
}
|
||
|
return alt.toFixed(1) + ',M';
|
||
|
};
|
||
|
|
||
|
// 1 decimal, always meters
|
||
|
exports.encodeGeoidalSeperation = function(geoidalSep) {
|
||
|
if(geoidalSep === undefined) {
|
||
|
return ',';
|
||
|
}
|
||
|
return geoidalSep.toFixed(1) + ',M';
|
||
|
};
|
||
|
|
||
|
// magnetic variation
|
||
|
exports.encodeMagVar = function(v) {
|
||
|
var a;
|
||
|
var s;
|
||
|
if(v === undefined) {
|
||
|
return ',';
|
||
|
}
|
||
|
a = Math.abs(v);
|
||
|
s = (v < 0) ? (a.toFixed(1) + ',E') : (a.toFixed(1) + ',W');
|
||
|
return exports.padLeft(s, 7, '0');
|
||
|
};
|
||
|
|
||
|
// degrees
|
||
|
exports.encodeDegrees = function(d) {
|
||
|
if(d === undefined) {
|
||
|
return '';
|
||
|
}
|
||
|
return exports.padLeft(d.toFixed(2), 6, '0');
|
||
|
};
|
||
|
|
||
|
exports.encodeDate = function(d) {
|
||
|
var yr;
|
||
|
var mn;
|
||
|
var dy;
|
||
|
|
||
|
if(d === undefined) {
|
||
|
return '';
|
||
|
}
|
||
|
yr = d.getUTCFullYear();
|
||
|
mn = d.getUTCMonth() + 1;
|
||
|
dy = d.getUTCDate();
|
||
|
return exports.padLeft(dy.toString(), 2, '0') + exports.padLeft(mn.toString(), 2, '0') + yr.toString().substr(2);
|
||
|
};
|
||
|
|
||
|
exports.encodeTime = function(d) {
|
||
|
var h;
|
||
|
var m;
|
||
|
var s;
|
||
|
|
||
|
if(d === undefined) {
|
||
|
return '';
|
||
|
}
|
||
|
h = d.getUTCHours();
|
||
|
m = d.getUTCMinutes();
|
||
|
s = d.getUTCSeconds();
|
||
|
return exports.padLeft(h.toString(), 2, '0') + exports.padLeft(m.toString(), 2, '0') + exports.padLeft(s.toString(), 2, '0');
|
||
|
};
|
||
|
|
||
|
exports.encodeKnots = function(k) {
|
||
|
if(k === undefined) {
|
||
|
return '';
|
||
|
}
|
||
|
return exports.padLeft(k.toFixed(1), 5, '0');
|
||
|
};
|
||
|
|
||
|
exports.encodeValue = function(v) {
|
||
|
if(v === undefined) {
|
||
|
return '';
|
||
|
}
|
||
|
return v.toString();
|
||
|
};
|
||
|
|
||
|
exports.encodeFixed = function(v, f) {
|
||
|
if(v === undefined) {
|
||
|
return '';
|
||
|
}
|
||
|
return v.toFixed(f);
|
||
|
};
|
||
|
|
||
|
// =========================================
|
||
|
// field traditionalDecoders
|
||
|
// =========================================
|
||
|
|
||
|
// separate number and units
|
||
|
exports.parseAltitude = function(alt, units) {
|
||
|
var scale = 1.0;
|
||
|
if(units === 'F') {
|
||
|
scale = 0.3048;
|
||
|
}
|
||
|
return parseFloat(alt) * scale;
|
||
|
};
|
||
|
|
||
|
// separate degrees value and quadrant (E/W)
|
||
|
exports.parseDegrees = function(deg, quadrant) {
|
||
|
var q = (quadrant === 'E') ? -1.0 : 1.0;
|
||
|
|
||
|
return parseFloat(deg) * q;
|
||
|
};
|
||
|
|
||
|
// fields can be empty so have to wrap the global parseFloat
|
||
|
exports.parseFloatX = function(f) {
|
||
|
if(f === '') {
|
||
|
return 0.0;
|
||
|
}
|
||
|
return parseFloat(f);
|
||
|
};
|
||
|
|
||
|
// decode latitude
|
||
|
// input : latitude in nmea format
|
||
|
// first two digits are degress
|
||
|
// rest of digits are decimal minutes
|
||
|
// output : latitude in decimal degrees
|
||
|
exports.parseLatitude = function(lat, hemi) {
|
||
|
var h = (hemi === 'N') ? 1.0 : -1.0;
|
||
|
var a;
|
||
|
var dg;
|
||
|
var mn;
|
||
|
var l;
|
||
|
a = lat.split('.');
|
||
|
if(a[0].length === 4) {
|
||
|
// two digits of degrees
|
||
|
dg = lat.substring(0, 2);
|
||
|
mn = lat.substring(2);
|
||
|
} else if(a[0].length === 3) {
|
||
|
// 1 digit of degrees (in case no leading zero)
|
||
|
dg = lat.substring(0, 1);
|
||
|
mn = lat.substring(1);
|
||
|
} else {
|
||
|
// no degrees, just minutes (nonstandard but a buggy unit might do this)
|
||
|
dg = '0';
|
||
|
mn = lat;
|
||
|
}
|
||
|
// latitude is usually precise to 5-8 digits
|
||
|
return ((parseFloat(dg) + (parseFloat(mn) / 60.0)) * h).toFixed(8);
|
||
|
};
|
||
|
|
||
|
// decode longitude
|
||
|
// first three digits are degress
|
||
|
// rest of digits are decimal minutes
|
||
|
exports.parseLongitude = function(lon, hemi) {
|
||
|
var h;
|
||
|
var a;
|
||
|
var dg;
|
||
|
var mn;
|
||
|
h = (hemi === 'E') ? 1.0 : -1.0;
|
||
|
a = lon.split('.');
|
||
|
if(a[0].length === 5) {
|
||
|
// three digits of degrees
|
||
|
dg = lon.substring(0, 3);
|
||
|
mn = lon.substring(3);
|
||
|
} else if(a[0].length === 4) {
|
||
|
// 2 digits of degrees (in case no leading zero)
|
||
|
dg = lon.substring(0, 2);
|
||
|
mn = lon.substring(2);
|
||
|
} else if(a[0].length === 3) {
|
||
|
// 1 digit of degrees (in case no leading zero)
|
||
|
dg = lon.substring(0, 1);
|
||
|
mn = lon.substring(1);
|
||
|
} else {
|
||
|
// no degrees, just minutes (nonstandard but a buggy unit might do this)
|
||
|
dg = '0';
|
||
|
mn = lon;
|
||
|
}
|
||
|
// longitude is usually precise to 5-8 digits
|
||
|
return ((parseFloat(dg) + (parseFloat(mn) / 60.0)) * h).toFixed(8);
|
||
|
};
|
||
|
|
||
|
// fields can be empty so have to wrap the global parseInt
|
||
|
exports.parseIntX = function(i) {
|
||
|
if(i === '') {
|
||
|
return 0;
|
||
|
}
|
||
|
return parseInt(i, 10);
|
||
|
};
|
||
|
|
||
|
exports.parseDateTime = function(date, time) {
|
||
|
var h = parseInt(time.slice(0, 2), 10);
|
||
|
var m = parseInt(time.slice(2, 4), 10);
|
||
|
var s = parseInt(time.slice(4, 6), 10);
|
||
|
var D = parseInt(date.slice(0, 2), 10);
|
||
|
var M = parseInt(date.slice(2, 4), 10) - 1; // UTC = 0..11
|
||
|
var Y = parseInt(date.slice(4, 6), 10);
|
||
|
// hack : GPRMC date doesn't specify century. GPS came out in 1973
|
||
|
// so if year is less than 73 its 2000, otherwise 1900
|
||
|
if (Y < 73) {
|
||
|
Y = Y + 2000;
|
||
|
}
|
||
|
else {
|
||
|
Y = Y + 1900;
|
||
|
}
|
||
|
|
||
|
return new Date(Date.UTC(Y, M, D, h, m, s));
|
||
|
};
|