mirror of https://github.com/espruino/BangleApps
gpsservice and two watch faces for gps and grid reference info
parent
04dad49e6f
commit
8af4646340
|
@ -0,0 +1,44 @@
|
|||
# GPS Service
|
||||
|
||||
A configurable GPS widget that runs in the background.
|
||||
|
||||
## Goals
|
||||
|
||||
The goals of this project were to develop a GPS widget that could be
|
||||
ran in low power mode in the background and could be used to display
|
||||
a OS grid reference in a watch face.
|
||||
|
||||
|
||||
* Using an App that turns on the GPS and constantly displays the
|
||||
screen will use around 75mA, the battery will last between 3-4
|
||||
hours.
|
||||
|
||||
* Using the GPS in a Widget in Super-E Power Saving Mode (PSM) with
|
||||
the screen off (most of the time) will consume around 35mA and you
|
||||
might get 10hrs before a recharge.
|
||||
|
||||
* Using the GPS in Power Saving Mode On/Off (PSMOO) with suitable
|
||||
settings can reduce the average consumption to around 15mA.
|
||||
|
||||
## Settings
|
||||
|
||||
The Settings App enables you set the following options for the GPS Service.
|
||||
|
||||
* GPS - On/Off. When this value is changed the GPS Service will be
|
||||
powered on or off and the GPS Widget will be displayed.
|
||||
|
||||
* Power Mode:
|
||||
|
||||
** PSM-Super-E - the factory default setup for the GPS. The
|
||||
recommended power saving mode.
|
||||
|
||||
** PSMOO - On/Off power saving mode. Configured by interval and
|
||||
search time. Choose this mode if you are happy to get a GPS
|
||||
position update less often (say every 1 or 2 minutes). The longer
|
||||
the interval the more time the GPS will spend sleeping in low
|
||||
power mode (7mA) between obtaining fixes (35mA). For walking in
|
||||
open country an update once every 60 seconds is adequate to put
|
||||
you within a 6 digit grid refernce sqaure.
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
const SETTINGS_FILE = "gpsservice.settings.json";
|
||||
const POWER_OPTIONS = ['SuperE', 'PSMOO'];
|
||||
let settings = require("Storage").readJSON(SETTINGS_FILE,1)||{};
|
||||
|
||||
function updateSettings() {
|
||||
require("Storage").write(SETTINGS_FILE, settings);
|
||||
}
|
||||
|
||||
function reloadWidget() {
|
||||
if (WIDGETS.gpsservice)
|
||||
WIDGETS.gpsservice.reload();
|
||||
}
|
||||
|
||||
function showMainMenu() {
|
||||
const mainmenu = {
|
||||
'': { 'title': 'GPS Service' },
|
||||
'< Exit': ()=>{load();},
|
||||
'GPS': {
|
||||
value: !!settings.service,
|
||||
format: v =>v?'On':'Off',
|
||||
onchange: v => {
|
||||
settings.service = v;
|
||||
updateSettings();
|
||||
reloadWidget(); // only when we change On/Off status
|
||||
},
|
||||
},
|
||||
'Period (s)': {
|
||||
value: settings.period,
|
||||
min: 1,
|
||||
max: 1800,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
settings.period =v;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
'Ontime (s)': {
|
||||
value: settings.ontime,
|
||||
min: 1,
|
||||
max: 65,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
settings.ontime = v;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
'Rate (s)': {
|
||||
value: settings.period,
|
||||
min: 1,
|
||||
max: 60,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
settings.rate = v;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
|
||||
'< Back': ()=>{load();}
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
}
|
||||
|
||||
showMainMenu();
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwghC/AH4AKg9wC6t3u4uVC6wWBI6t3uJeVuMQCqcBLisAi4XLxAABFxAXKgc4DBAuBRhQXEDAq7MmYXEwBHEXZYXFGAOqAAKDMmczC4mIC62CC50PC4JIBkQABiIvRmURAAUSjQXSFwMoxGKC6CRFwUSVYgXLPIgXXwMYegoXLJAYXCGBnzGA0hPQIwMgYwGC6gwCC4ZIMC4gYBC604C4ZISmcRVgapQAAMhC6GIJIwXCMBcIxGDDBAuLC4IwGAARGMAAQWGmAXPJQoWMC4pwCCpoXJAB4XXAH4A/ABQA="))
|
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,4 @@
|
|||
(function(back) {
|
||||
// just go right to our app
|
||||
load("gpsservice.app.js");
|
||||
})();
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
|
||||
test bed for working out lowest power consumption, with workable GPS
|
||||
Load into IDE and upload code to RAM when connected to watch
|
||||
|
||||
*/
|
||||
|
||||
|
||||
Bangle.on('GPS-raw',function (d) {
|
||||
if (d[0]=="$") return;
|
||||
if (d.startsWith("\xB5\x62\x05\x01")) print("GPS ACK");
|
||||
else if (d.startsWith("\xB5\x62\x05\x00")) print("GPS NACK");
|
||||
// 181,98 sync chars
|
||||
else print("GPS",E.toUint8Array(d).join(","));
|
||||
});
|
||||
|
||||
function writeGPScmd(cmd) {
|
||||
var d = [0xB5,0x62]; // sync chars
|
||||
d = d.concat(cmd);
|
||||
var a=0,b=0;
|
||||
for (var i=2;i<d.length;i++) {
|
||||
a += d[i];
|
||||
b += a;
|
||||
}
|
||||
d.push(a&255,b&255);
|
||||
console.log(d);
|
||||
Serial1.write(d);
|
||||
}
|
||||
|
||||
// quick hack
|
||||
function wait(ms){
|
||||
var start = new Date().getTime();
|
||||
var end = start;
|
||||
while(end < start + ms) {
|
||||
end = new Date().getTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* gps_disable_nmea_output
|
||||
*
|
||||
* disables all NMEA messages to be output from the GPS.
|
||||
* even though the parser can cope with NMEA messages and ignores them, it
|
||||
* may save power to disable them completely.
|
||||
*
|
||||
*
|
||||
* DO NOT USE
|
||||
* HB: This is a bad idea on a Bangle JS
|
||||
* You wont get any fixes if you call this function
|
||||
*
|
||||
*
|
||||
*/
|
||||
function UBX_CFG_DISABLE_NMEA() {
|
||||
writeGPScmd([0x06, 0x00, /* UBX-CFG-PRT */
|
||||
20, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, /* UART1, reserved, no TX ready */
|
||||
0xe0, 0x08, 0x00, 0x00, /* UART mode (8N1) */
|
||||
0x80, 0x25, 0x00, 0x00, /* UART baud rate (9600) */
|
||||
0x01, 0x00, /* input protocols (uBx only) */
|
||||
0x01, 0x00, /* output protocols (uBx only) */
|
||||
0x00, 0x00, /* flags */
|
||||
0x00, 0x00]); /* reserved */
|
||||
}
|
||||
|
||||
/*
|
||||
* gps_disable_nmea_output
|
||||
*
|
||||
* disables all NMEA messages to be output from the GPS.
|
||||
* even though the parser can cope with NMEA messages and ignores them, it
|
||||
* may save power to disable them completely.
|
||||
*
|
||||
* Can use this to switch NMEA back on if you call
|
||||
* UBX_CFG_DISABLE_NMEA()
|
||||
*
|
||||
*/
|
||||
function UBX_CFG_RESTORE_NMEA() {
|
||||
writeGPScmd([0x06, 0x00, /* UBX-CFG-PRT */
|
||||
20, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, /* UART1, reserved, no TX ready */
|
||||
0xe0, 0x08, 0x00, 0x00, /* UART mode (8N1) */
|
||||
0x80, 0x25, 0x00, 0x00, /* UART baud rate (9600) */
|
||||
0x03, 0x00, /* input protocols (uBx only) */
|
||||
0x03, 0x00, /* output protocols (uBx only) */
|
||||
0x00, 0x00, /* flags */
|
||||
0x00, 0x00]); /* reserved */
|
||||
}
|
||||
|
||||
function UBX_CFG_PMS() {
|
||||
// UBX-CFG-PMS - enable power management - Super-E
|
||||
writeGPScmd([0x06,0x86, // msg class + type
|
||||
8,0,//length
|
||||
0x00,0x03, 0,0, 0,0, 0,0]);
|
||||
}
|
||||
|
||||
function UBX_CFG_INTERVAL(period, ontime) {
|
||||
writeGPScmd([0x06,0x86, // msg class + type
|
||||
8,0, //length
|
||||
//v0, interval period ontime reserved
|
||||
0x00, 0x02, period, 0, ontime, 0, 0, 0 ]);
|
||||
// the values are little endian, least significant byte first
|
||||
}
|
||||
|
||||
/*
|
||||
* set update baud rate
|
||||
*
|
||||
* the setting is in milliseconds in 2 bytes, max 65 seconds
|
||||
* we are passing in a value in seconds
|
||||
* we set the most significant byte only
|
||||
* 8 seconds ~ 8192ms 0x2000, 0x20 = 32 = 4*8
|
||||
*
|
||||
*/
|
||||
function UBX_CFG_RATE(rate) {
|
||||
rate = (rate * 4) % 256;
|
||||
//console.log("rate=" + rate);
|
||||
|
||||
writeGPScmd([0x06,0x08, // class, id
|
||||
0x06, 0, // length
|
||||
0x00, rate, // b0: 8192ms 0x2000, 0x00FF (~65sec)
|
||||
0x01, 0x00, // b2:
|
||||
0x01, 0x00]); // b4: timeref GPS
|
||||
}
|
||||
|
||||
/*
|
||||
* Save configuration otherwise it will reset when the GPS wakes up
|
||||
*
|
||||
*/
|
||||
function UBX_CFG_SAVE() {
|
||||
writeGPScmd([0x06, 0x09, // class id
|
||||
0x0D, 0x00, // length
|
||||
0x00, 0x00, 0x00, 0x00, // clear mask
|
||||
0xFF, 0xFF, 0x00, 0x00, // save mask
|
||||
0x00, 0x00, 0x00, 0x00, // load mask
|
||||
0x07]); // b2=eeprom b1=flash b0=bat backed ram
|
||||
// code on github had 7 - all 3 set ?
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Reset to factory settings using clear mask in UBX_CFG_CFG
|
||||
* https://portal.u-blox.com/s/question/0D52p0000925T00CAE/ublox-max-m8q-getting-stuck-when-sleeping-with-extint-pin-control
|
||||
*/
|
||||
function UBX_CFG_RESET() {
|
||||
writeGPScmd([0x06, 0x09, // class id
|
||||
0x0D, 0x00,
|
||||
0xFF, 0xFB, 0x00, 0x00, // clear mask
|
||||
0x00, 0x00, 0x00, 0x00, // save mask
|
||||
0xFF, 0xFF, 0x00, 0x00, // load mask
|
||||
0x17]);
|
||||
}
|
||||
|
||||
|
||||
// convert an integer to an array of bytes
|
||||
function int_2_bytes( x ){
|
||||
var bytes = [];
|
||||
var i = 4;
|
||||
do {
|
||||
bytes[--i] = x & (255);
|
||||
x = x>>8;
|
||||
} while (i);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Extended Power Management
|
||||
* update and search are in seconds
|
||||
*
|
||||
* https://github.com/thasti/utrak/blob/master/gps.c
|
||||
*/
|
||||
function UBX_CFG_PM2(update,search) {
|
||||
|
||||
var u = int_2_bytes(update*1000);
|
||||
var s = int_2_bytes(search*1000);
|
||||
|
||||
writeGPScmd([0x06, 0x3B, /* class id */
|
||||
44, 0, /* length */
|
||||
0x01, 0x00, 0x00, 0x00, /* v1, reserved 1..3 */
|
||||
0x00, 0x10, 0x00, 0x00, /* on/off-mode, update ephemeris */
|
||||
// little endian, lsb first
|
||||
//0x30, 0x75, 0x00, 0x00, /* update period, ms, 120s=00 01 D4 C0, 30s= 00 00 75 30 */
|
||||
//0x88, 0x13, 0x00, 0x00, /* search period, ms, 120s, 20s = 00 00 4E 20, 5s = 13 88 */
|
||||
u[3], u[2], u[1], u[0], /* update period, ms, 120s=00 01 D4 C0, 30s= 00 00 75 30 */
|
||||
s[3], s[2], s[1], s[0], /* search period, ms, 120s, 20s = 00 00 4E 20, 5s = 13 88 */
|
||||
0x00, 0x00, 0x00, 0x00, /* grid offset */
|
||||
0x00, 0x00, /* on-time after first fix */
|
||||
0x01, 0x00, /* minimum acquisition time */
|
||||
0x00, 0x00, 0x00, 0x00, /* reserved 4,5 */
|
||||
0x00, 0x00, 0x00, 0x00, /* reserved 6 */
|
||||
0x00, 0x00, 0x00, 0x00, /* reserved 7 */
|
||||
0x00, 0x00, 0x00, 0x00, /* reserved 8,9,10 */
|
||||
0x00, 0x00, 0x00, 0x00]); /* reserved 11 */
|
||||
}
|
||||
|
||||
|
||||
// enable power saving mode, after configured with PM2
|
||||
function UBX_CFG_RXM() {
|
||||
writeGPScmd([0x06, 0x11, /* UBX-CFG-RXM */
|
||||
2, 0, /* length */
|
||||
0x08, 0x01]); /* reserved, enable power save mode */
|
||||
}
|
||||
|
||||
function onGPS(fix) {
|
||||
console.log(fix);
|
||||
}
|
||||
|
||||
|
||||
function setupGPS() {
|
||||
Bangle.setGPSPower(1);
|
||||
|
||||
UBX_CFG_RESET();
|
||||
wait(100);
|
||||
|
||||
//UBX_CFG_RESTORE_NMEA();
|
||||
//wait(20);
|
||||
|
||||
UBX_CFG_PM2(120,5);
|
||||
wait(20);
|
||||
|
||||
UBX_CFG_RXM();
|
||||
wait(20);
|
||||
|
||||
UBX_CFG_SAVE();
|
||||
wait(20);
|
||||
|
||||
Bangle.on('GPS',onGPS);
|
||||
}
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
(() => {
|
||||
var settings = {};
|
||||
var fixToggle = false; // toggles once for each reading
|
||||
var have_fix = false;
|
||||
|
||||
var last_fix = {
|
||||
fix: 0,
|
||||
alt: 0,
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
speed: 0,
|
||||
time: 0,
|
||||
satellites: 0
|
||||
};
|
||||
|
||||
function gps_get_fix() { return last_fix; }
|
||||
function gps_get_status() { return WIDGETS.gpsservice.width === 24 ? true : false;}
|
||||
function gps_get_version() { return "0.2"; }
|
||||
|
||||
// Called by the GPS widget settings to reload settings and decide what to do
|
||||
function reload() {
|
||||
settings = require("Storage").readJSON("gpsservice.settings.json",1)||{};
|
||||
settings.period = settings.period||12;
|
||||
settings.ontime = settings.ontime||5;
|
||||
//settings.rate = settings.rate||10;
|
||||
console.log(settings);
|
||||
|
||||
Bangle.removeListener('GPS',onGPS);
|
||||
|
||||
if (settings.service) {
|
||||
gps_power_on();
|
||||
} else {
|
||||
gps_power_off();
|
||||
}
|
||||
}
|
||||
|
||||
function gps_power_on() {
|
||||
have_fix = false;
|
||||
fixToggle = false;
|
||||
setupGPS();
|
||||
WIDGETS.gpsservice.width = 24;
|
||||
}
|
||||
|
||||
function gps_power_off() {
|
||||
Bangle.setGPSPower(0);
|
||||
have_fix = false;
|
||||
fixToggle = false;
|
||||
last_fix.fix = 0;
|
||||
WIDGETS.gpsservice.width = 0;
|
||||
}
|
||||
|
||||
// quick hack
|
||||
function wait(ms){
|
||||
var start = new Date().getTime();
|
||||
var end = start;
|
||||
while(end < start + ms) {
|
||||
end = new Date().getTime();
|
||||
}
|
||||
}
|
||||
|
||||
function setupGPS() {
|
||||
Bangle.setGPSPower(1);
|
||||
|
||||
UBX_CFG_RESET();
|
||||
wait(100);
|
||||
|
||||
UBX_CFG_PM2(settings.period, settings.ontime);
|
||||
wait(20);
|
||||
|
||||
UBX_CFG_RXM();
|
||||
wait(20);
|
||||
|
||||
UBX_CFG_SAVE();
|
||||
wait(20);
|
||||
|
||||
Bangle.on('GPS',onGPS);
|
||||
}
|
||||
|
||||
function writeGPScmd(cmd) {
|
||||
var d = [0xB5,0x62]; // sync chars
|
||||
d = d.concat(cmd);
|
||||
var a=0,b=0;
|
||||
for (var i=2;i<d.length;i++) {
|
||||
a += d[i];
|
||||
b += a;
|
||||
}
|
||||
d.push(a&255,b&255);
|
||||
Serial1.write(d);
|
||||
}
|
||||
|
||||
// UBX-CFG-PMS - enable power management - Super-E
|
||||
function UBX_CFG_PMS() {
|
||||
writeGPScmd([0x06,0x86, // msg class + type
|
||||
8,0,//length
|
||||
0x00,0x03, 0,0, 0,0, 0,0]);
|
||||
}
|
||||
|
||||
// convert an integer to an array of bytes
|
||||
function int_2_bytes( x ){
|
||||
var bytes = [];
|
||||
var i = 4;
|
||||
do {
|
||||
bytes[--i] = x & (255);
|
||||
x = x>>8;
|
||||
} while (i);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Extended Power Management
|
||||
* update and search are in milli seconds
|
||||
* settings are loaded little endian, lsb first
|
||||
*
|
||||
* https://github.com/thasti/utrak/blob/master/gps.c
|
||||
*/
|
||||
function UBX_CFG_PM2(update,search) {
|
||||
|
||||
var u = int_2_bytes(update*1000);
|
||||
var s = int_2_bytes(search*1000);
|
||||
|
||||
writeGPScmd([0x06, 0x3B, /* class id */
|
||||
44, 0, /* length */
|
||||
0x01, 0x00, 0x00, 0x00, /* v1, reserved 1..3 */
|
||||
0x00, 0x10, 0x00, 0x00, /* on/off-mode, update ephemeris */
|
||||
u[3], u[2], u[1], u[0], /* update period, ms, 120s=00 01 D4 C0, 30s= 00 00 75 30 */
|
||||
s[3], s[2], s[1], s[0], /* search period, ms, 120s, 20s = 00 00 4E 20, 5s = 13 88 */
|
||||
0x00, 0x00, 0x00, 0x00, /* grid offset */
|
||||
0x00, 0x00, /* on-time after first fix */
|
||||
0x01, 0x00, /* minimum acquisition time */
|
||||
0x00, 0x00, 0x00, 0x00, /* reserved 4,5 */
|
||||
0x00, 0x00, 0x00, 0x00, /* reserved 6 */
|
||||
0x00, 0x00, 0x00, 0x00, /* reserved 7 */
|
||||
0x00, 0x00, 0x00, 0x00, /* reserved 8,9,10 */
|
||||
0x00, 0x00, 0x00, 0x00]); /* reserved 11 */
|
||||
}
|
||||
|
||||
// enable power saving mode, after configured with PM2
|
||||
function UBX_CFG_RXM() {
|
||||
writeGPScmd([0x06, 0x11, /* UBX-CFG-RXM */
|
||||
2, 0, /* length */
|
||||
0x08, 0x01]); /* reserved, enable power save mode */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Save configuration otherwise it will reset when the GPS wakes up
|
||||
*
|
||||
*/
|
||||
function UBX_CFG_SAVE() {
|
||||
writeGPScmd([0x06, 0x09, // class id
|
||||
0x0D, 0x00, // length
|
||||
0x00, 0x00, 0x00, 0x00, // clear mask
|
||||
0xFF, 0xFF, 0x00, 0x00, // save mask
|
||||
0x00, 0x00, 0x00, 0x00, // load mask
|
||||
0x07]); // b2=eeprom b1=flash b0=bat backed ram
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset to factory settings using clear mask in UBX_CFG_CFG
|
||||
* https://portal.u-blox.com/s/question/0D52p0000925T00CAE/ublox-max-m8q-getting-stuck-when-sleeping-with-extint-pin-control
|
||||
*/
|
||||
function UBX_CFG_RESET() {
|
||||
writeGPScmd([0x06, 0x09, // class id
|
||||
0x0D, 0x00,
|
||||
0xFF, 0xFB, 0x00, 0x00, // clear mask
|
||||
0x00, 0x00, 0x00, 0x00, // save mask
|
||||
0xFF, 0xFF, 0x00, 0x00, // load mask
|
||||
0x17]);
|
||||
}
|
||||
|
||||
// draw the widget
|
||||
function draw() {
|
||||
g.reset();
|
||||
g.drawImage(atob("GBgCAAAAAAAAAAQAAAAAAD8AAAAAAP/AAAAAAP/wAAAAAH/8C9AAAB/8L/QAAAfwv/wAAAHS//wAAAAL//gAAAAf/+AAAAAf/4AAAAL//gAAAAD/+DwAAAB/Uf8AAAAfA//AAAACAf/wAAAAAH/0AAAAAB/wAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),this.x,this.y);
|
||||
if (gps_get_status() === true && have_fix) {
|
||||
g.setColor("#00FF00");
|
||||
g.drawImage(fixToggle ? atob("CgoCAAAAA0AAOAAD5AAPwAAAAAAAAAAAAAAAAA==") : atob("CgoCAAABw0AcOAHj5A8PwHwAAvgAB/wABUAAAA=="),this.x,this.y+14);
|
||||
} else {
|
||||
g.setColor("#0000FF");
|
||||
if (fixToggle) g.setFont("6x8").drawString("?",this.x,this.y+14);
|
||||
}
|
||||
}
|
||||
|
||||
function onGPS(fix) {
|
||||
fixToggle = !fixToggle;
|
||||
WIDGETS.gpsservice.draw();
|
||||
|
||||
last_fix.satellites = fix.satellites;
|
||||
|
||||
/*
|
||||
* If we have a fix record it, we will get another soon. Apps
|
||||
* will see the timestamp of the last fix and be able to work out
|
||||
* if it is stale. This means an App will always have the last
|
||||
* known fix, and we avoid saying no fix all the time.
|
||||
*
|
||||
*/
|
||||
if (fix.fix) {
|
||||
last_fix.fix = fix.fix;
|
||||
last_fix.alt = fix.alt;
|
||||
last_fix.lat = fix.lat;
|
||||
last_fix.lon = fix.lon;
|
||||
last_fix.speed = fix.speed;
|
||||
last_fix.time = fix.time;
|
||||
}
|
||||
}
|
||||
|
||||
// redraw when the LCD turns on
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
if (on) WIDGETS.gpsservice.draw();
|
||||
});
|
||||
|
||||
// add the widget
|
||||
WIDGETS["gpsservice"]={
|
||||
area:"tl",
|
||||
width:24,
|
||||
draw:draw,
|
||||
gps_power_on:gps_power_on,
|
||||
gps_power_off:gps_power_off,
|
||||
gps_get_status:gps_get_status,
|
||||
gps_get_fix:gps_get_fix,
|
||||
gps_get_version:gps_get_version,
|
||||
reload:function() {
|
||||
reload();
|
||||
Bangle.drawWidgets(); // relayout all widgets
|
||||
}};
|
||||
|
||||
// load settings, set correct widget width
|
||||
reload();
|
||||
|
||||
})();
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
(() => {
|
||||
|
||||
function getFace(){
|
||||
|
||||
//var img = require("heatshrink").decompress(atob("mEwghC/AH4AKg9wC6t3u4uVC6wWBI6t3uJeVuMQCqcBLisAi4XLxAABFxAXKgc4DBAuBRhQXEDAq7MmYXEwBHEXZYXFGAOqAAKDMmczC4mIC62CC50PC4JIBkQABiIvRmURAAUSjQXSFwMoxGKC6CRFwUSVYgXLPIgXXwMYegoXLJAYXCGBnzGA0hPQIwMgYwGC6gwCC4ZIMC4gYBC604C4ZISmcRVgapQAAMhC6GIJIwXCMBcIxGDDBAuLC4IwGAARGMAAQWGmAXPJQoWMC4pwCCpoXJAB4XXAH4A/ABQA="));
|
||||
var nofix = 0;
|
||||
|
||||
function formatTime(now) {
|
||||
var fd = now.toUTCString().split(" ");
|
||||
return fd[4];
|
||||
}
|
||||
|
||||
|
||||
function timeSince(t) {
|
||||
var hms = t.split(":");
|
||||
var now = new Date();
|
||||
|
||||
var sn = 3600*(now.getHours()) + 60*(now.getMinutes()) + 1*(now.getSeconds());
|
||||
var st = 3600*(hms[0]) + 60*(hms[1]) + 1*(hms[2]);
|
||||
|
||||
return (sn - st);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
var gps_on = false;
|
||||
|
||||
var fix = {
|
||||
fix: 0,
|
||||
alt: 0,
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
speed: 0,
|
||||
time: 0,
|
||||
satellites: 0
|
||||
};
|
||||
|
||||
var y_line = 26;
|
||||
var y_start = 46;
|
||||
var x_start = 10;
|
||||
|
||||
// only attempt to get gps fix if gpsservuce is loaded
|
||||
if (WIDGETS.gpsservice !== undefined) {
|
||||
fix = WIDGETS.gpsservice.gps_get_fix();
|
||||
gps_on = WIDGETS.gpsservice.gps_get_status();
|
||||
}
|
||||
|
||||
g.reset();
|
||||
g.clearRect(0,24,239,239);
|
||||
|
||||
if (fix.fix) {
|
||||
var time = formatTime(fix.time);
|
||||
var age = timeSince(time);
|
||||
|
||||
g.setFontAlign(-1, -1);
|
||||
g.setFont("6x8");
|
||||
g.setFontVector(22);
|
||||
g.drawString("Alt: " + fix.alt +" m", x_start, y_start, true);
|
||||
g.drawString("Lat: "+ fix.lat, x_start, y_start + y_line, true);
|
||||
g.drawString("Lon: " + fix.lon, x_start, y_start + 2*y_line, true);
|
||||
g.drawString("Time: " + time, x_start, y_start + 3*y_line, true);
|
||||
g.drawString("Age(s): " + age, x_start, y_start + 4*y_line, true);
|
||||
g.drawString("Satellites: " + fix.satellites, x_start, y_start + 5*y_line, true);
|
||||
|
||||
} else if (gps_on) {
|
||||
|
||||
g.setFontAlign(0, 1);
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString("GPS Watch", 120, 60);
|
||||
g.drawString("Waiting for GPS", 120, 80);
|
||||
nofix = (nofix+1) % 4;
|
||||
g.drawString(".".repeat(nofix) + " ".repeat(4-nofix), 120, 120);
|
||||
g.setFontAlign(0,0);
|
||||
g.drawString(fix.satellites + " satellites", 120, 100);
|
||||
|
||||
} else if (!gps_on) {
|
||||
|
||||
g.setFontAlign(0, 0);
|
||||
g.setFont("6x8", 3);
|
||||
g.drawString("GPS Watch", 120, 80);
|
||||
g.drawString("GPS is off",120, 160);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function onSecond(){
|
||||
var t = new Date();
|
||||
if ((t.getSeconds() % 5) === 0) draw();
|
||||
}
|
||||
|
||||
return {init:draw, tick:onSecond};
|
||||
}
|
||||
|
||||
return getFace;
|
||||
|
||||
})();
|
|
@ -0,0 +1,202 @@
|
|||
(() => {
|
||||
|
||||
function getFace(){
|
||||
var nofix = 0;
|
||||
|
||||
function formatTime(now) {
|
||||
var fd = now.toUTCString().split(" ");
|
||||
return fd[4];
|
||||
}
|
||||
|
||||
function timeSince(t) {
|
||||
var hms = t.split(":");
|
||||
var now = new Date();
|
||||
|
||||
var sn = 3600*(now.getHours()) + 60*(now.getMinutes()) + 1*(now.getSeconds());
|
||||
var st = 3600*(hms[0]) + 60*(hms[1]) + 1*(hms[2]);
|
||||
|
||||
return (sn - st);
|
||||
}
|
||||
|
||||
|
||||
Number.prototype.toRad = function() { return this*Math.PI/180; };
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
/* Ordnance Survey Grid Reference functions (c) Chris Veness 2005-2014 */
|
||||
/* - www.movable-type.co.uk/scripts/gridref.js */
|
||||
/* - www.movable-type.co.uk/scripts/latlon-gridref.html */
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
function OsGridRef(easting, northing) {
|
||||
this.easting = 0|easting;
|
||||
this.northing = 0|northing;
|
||||
}
|
||||
OsGridRef.latLongToOsGrid = function(point) {
|
||||
var lat = point.lat.toRad();
|
||||
var lon = point.lon.toRad();
|
||||
|
||||
var a = 6377563.396, b = 6356256.909; // Airy 1830 major & minor semi-axes
|
||||
var F0 = 0.9996012717; // NatGrid scale factor on central meridian
|
||||
var lat0 = (49).toRad(), lon0 = (-2).toRad(); // NatGrid true origin is 49�N,2�W
|
||||
var N0 = -100000, E0 = 400000; // northing & easting of true origin, metres
|
||||
var e2 = 1 - (b*b)/(a*a); // eccentricity squared
|
||||
var n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n;
|
||||
|
||||
var cosLat = Math.cos(lat), sinLat = Math.sin(lat);
|
||||
var nu = a*F0/Math.sqrt(1-e2*sinLat*sinLat); // transverse radius of curvature
|
||||
var rho = a*F0*(1-e2)/Math.pow(1-e2*sinLat*sinLat, 1.5); // meridional radius of curvature
|
||||
var eta2 = nu/rho-1;
|
||||
|
||||
var Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);
|
||||
var Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(lat-lat0) * Math.cos(lat+lat0);
|
||||
var Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(lat-lat0)) * Math.cos(2*(lat+lat0));
|
||||
var Md = (35/24)*n3 * Math.sin(3*(lat-lat0)) * Math.cos(3*(lat+lat0));
|
||||
var M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc
|
||||
|
||||
var cos3lat = cosLat*cosLat*cosLat;
|
||||
var cos5lat = cos3lat*cosLat*cosLat;
|
||||
var tan2lat = Math.tan(lat)*Math.tan(lat);
|
||||
var tan4lat = tan2lat*tan2lat;
|
||||
|
||||
var I = M + N0;
|
||||
var II = (nu/2)*sinLat*cosLat;
|
||||
var III = (nu/24)*sinLat*cos3lat*(5-tan2lat+9*eta2);
|
||||
var IIIA = (nu/720)*sinLat*cos5lat*(61-58*tan2lat+tan4lat);
|
||||
var IV = nu*cosLat;
|
||||
var V = (nu/6)*cos3lat*(nu/rho-tan2lat);
|
||||
var VI = (nu/120) * cos5lat * (5 - 18*tan2lat + tan4lat + 14*eta2 - 58*tan2lat*eta2);
|
||||
|
||||
var dLon = lon-lon0;
|
||||
var dLon2 = dLon*dLon, dLon3 = dLon2*dLon, dLon4 = dLon3*dLon, dLon5 = dLon4*dLon, dLon6 = dLon5*dLon;
|
||||
|
||||
var N = I + II*dLon2 + III*dLon4 + IIIA*dLon6;
|
||||
var E = E0 + IV*dLon + V*dLon3 + VI*dLon5;
|
||||
|
||||
return new OsGridRef(E, N);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* converts northing, easting to standard OS grid reference.
|
||||
*
|
||||
* [digits=10] - precision (10 digits = metres)
|
||||
* to_map_ref(8, 651409, 313177); => 'TG 5140 1317'
|
||||
* to_map_ref(0, 651409, 313177); => '651409,313177'
|
||||
*
|
||||
*/
|
||||
function to_map_ref(digits, easting, northing) {
|
||||
if (![ 0,2,4,6,8,10,12,14,16 ].includes(Number(digits))) throw new RangeError(`invalid precision '${digits}'`); // eslint-disable-line comma-spacing
|
||||
|
||||
let e = easting;
|
||||
let n = northing;
|
||||
|
||||
// use digits = 0 to return numeric format (in metres) - note northing may be >= 1e7
|
||||
if (digits == 0) {
|
||||
const format = { useGrouping: false, minimumIntegerDigits: 6, maximumFractionDigits: 3 };
|
||||
const ePad = e.toLocaleString('en', format);
|
||||
const nPad = n.toLocaleString('en', format);
|
||||
return `${ePad},${nPad}`;
|
||||
}
|
||||
|
||||
// get the 100km-grid indices
|
||||
const e100km = Math.floor(e / 100000), n100km = Math.floor(n / 100000);
|
||||
|
||||
// translate those into numeric equivalents of the grid letters
|
||||
let l1 = (19 - n100km) - (19 - n100km) % 5 + Math.floor((e100km + 10) / 5);
|
||||
let l2 = (19 - n100km) * 5 % 25 + e100km % 5;
|
||||
|
||||
// compensate for skipped 'I' and build grid letter-pairs
|
||||
if (l1 > 7) l1++;
|
||||
if (l2 > 7) l2++;
|
||||
const letterPair = String.fromCharCode(l1 + 'A'.charCodeAt(0), l2 + 'A'.charCodeAt(0));
|
||||
|
||||
// strip 100km-grid indices from easting & northing, and reduce precision
|
||||
e = Math.floor((e % 100000) / Math.pow(10, 5 - digits / 2));
|
||||
n = Math.floor((n % 100000) / Math.pow(10, 5 - digits / 2));
|
||||
|
||||
// pad eastings & northings with leading zeros
|
||||
e = e.toString().padStart(digits/2, '0');
|
||||
n = n.toString().padStart(digits/2, '0');
|
||||
|
||||
return `${letterPair} ${e} ${n}`;
|
||||
}
|
||||
|
||||
function draw() {
|
||||
var gps_on = false;
|
||||
|
||||
var fix = {
|
||||
fix: 0,
|
||||
alt: 0,
|
||||
lat: 0,
|
||||
lon: 0,
|
||||
speed: 0,
|
||||
time: 0,
|
||||
satellites: 0
|
||||
};
|
||||
|
||||
var y_line = 26;
|
||||
var y_start = 46;
|
||||
var x_start = 10;
|
||||
|
||||
// only attempt to get gps fix if gpsservuce is loaded
|
||||
if (WIDGETS.gpsservice !== undefined) {
|
||||
fix = WIDGETS.gpsservice.gps_get_fix();
|
||||
gps_on = WIDGETS.gpsservice.gps_get_status();
|
||||
}
|
||||
|
||||
g.reset();
|
||||
g.clearRect(0,24,239,239);
|
||||
|
||||
if (fix.fix) {
|
||||
var time = formatTime(fix.time);
|
||||
var age = timeSince(time);
|
||||
var os = OsGridRef.latLongToOsGrid(fix);
|
||||
var ref = to_map_ref(6, os.easting, os.northing);
|
||||
age = age >=0 ? age : 0; // avoid -1 etc
|
||||
|
||||
g.reset();
|
||||
g.clearRect(0,24,239,239);
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont("Vector");
|
||||
g.setColor(1,1,1);
|
||||
g.setFontVector(40);
|
||||
g.drawString(time, 120, 80);
|
||||
|
||||
g.setFontVector(40);
|
||||
g.setColor(0xFFC0);
|
||||
g.drawString(ref, 120,140, true);
|
||||
|
||||
g.setFont("6x8",2);
|
||||
g.setColor(1,1,1);
|
||||
g.drawString(age, 120, 194);
|
||||
|
||||
} else if (gps_on) {
|
||||
|
||||
g.setFontAlign(0, 1);
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString("Gridref Watch", 120, 60);
|
||||
g.drawString("Waiting for GPS", 120, 80);
|
||||
nofix = (nofix+1) % 4;
|
||||
g.drawString(".".repeat(nofix) + " ".repeat(4-nofix), 120, 120);
|
||||
g.setFontAlign(0,0);
|
||||
g.drawString(fix.satellites + " satellites", 120, 100);
|
||||
|
||||
} else if (!gps_on) {
|
||||
|
||||
g.setFontAlign(0, 0);
|
||||
g.setFont("6x8", 3);
|
||||
g.drawString("Gridref Watch", 120, 80);
|
||||
g.drawString("GPS is off", 120, 160);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function onSecond(){
|
||||
var t = new Date();
|
||||
if ((t.getSeconds() % 5) === 0) draw();
|
||||
}
|
||||
|
||||
return {init:draw, tick:onSecond};
|
||||
}
|
||||
|
||||
return getFace;
|
||||
|
||||
})();
|
Loading…
Reference in New Issue