diff --git a/apps.json b/apps.json index c2b6ca075..40d3ea45c 100644 --- a/apps.json +++ b/apps.json @@ -2766,6 +2766,20 @@ {"name":"testuserinput.app.js","url":"app.js"}, {"name":"testuserinput.img","url":"app-icon.js","evaluate":true} ] +}, +{ "id": "gpssetup", + "name": "GPS Setup", + "shortName":"GPS Setup", + "icon": "gpssetup.png", + "version":"0.01", + "description": "Configure the GPS power options and store them in the GPS nvram", + "tags": "gps, tools, outdoors", + "readme": "README.md", + "storage": [ + {"name":"gpssetup.settings.js","url":"settings.js"}, + {"name":"gpssetup.settings.json","url":"settings.json"}, + {"name":"gpssetup.app.js","url":"app.js"}, + {"name":"gpssetup.img","url":"icon.js","evaluate":true} + ] } - ] diff --git a/apps/gpssetup/ChangeLog b/apps/gpssetup/ChangeLog new file mode 100644 index 000000000..0099beb29 --- /dev/null +++ b/apps/gpssetup/ChangeLog @@ -0,0 +1 @@ +0.01: First version of GPS Setup app diff --git a/apps/gpssetup/README.md b/apps/gpssetup/README.md new file mode 100644 index 000000000..a8f0ce5b7 --- /dev/null +++ b/apps/gpssetup/README.md @@ -0,0 +1,66 @@ +# GPS Setup + +An App to enable the GPS to be configured into low power mode. + +## Goals + +To develop an app that configures the GPS to run with the lowest +possible power consumption. + +Example power consumption of the GPS while powered on: + +* 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 with 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. A + simple test using a 120s update period, 6s search period was still + running with 45% battery 20 hours after it started. + + +## Settings + +The Settings App enables you set the options below. Either start the +App from the launcher or go to Settings, select App/Widgets and then +'GPS Setup'. + +When you exit the setup app, the settings will be stored in the +gpssetup.settings.json file, the GPS will be switched on and the +necessary commands sent to the GPS to configure it. The GPS is then +powered off. The GPS configuration is stored in the GPS non-volatile +memory so that next time the GPS is powered, that configuration is +used. These settings will remain for all apps that use the GPS. + + +- Power Mode: + + - SuperE - the factory default setup for the GPS. The recommended + power saving mode. If you need frequent (every second) updates on + position, then this is the mode for you. + + - 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. + +- update - the time between two position fix attempts. + +- search - the time between two acquisition attempts if the receiver + is unable to get a position fix. + +## References + +* [UBLOX M8 Receiver Data Sheet](https://www.u-blox.com/sites/default/files/products/documents/u-blox8-M8_ReceiverDescrProtSpec_%28UBX-13003221%29.pdf) + +* [UBLOX Power Management App Note](https://www.u-blox.com/sites/default/files/products/documents/PowerManagement_AppNote_%28UBX-13005162%29.pdf) + +* Some useful code on Github can be found [here](https://portal.u-blox.com/s/question/0D52p0000925T00CAE/ublox-max-m8q-getting-stuck-when-sleeping-with-extint-pin-control) +and [here](https://github.com/thasti/utrak/blob/master/gps.c) + diff --git a/apps/gpssetup/app.js b/apps/gpssetup/app.js new file mode 100644 index 000000000..f4cade2d6 --- /dev/null +++ b/apps/gpssetup/app.js @@ -0,0 +1,265 @@ +/* + * GPS Setup app, hughbarney AT googlemail DOT com + * With thanks to Gordon Williams for support and advice + * + * UBLOX power modes: + * SuperE - will provide updates every second and consume 35mA, 75mA with LCD on + * PSMOO - will sleep for update time and consume around 7mA during that period + * after acquiring satelite fixes the GPS will settle into a cycle of + * obtaining fix, sleeping for update seconds, wake up, get fix and sleep. + * + * See README file for more details + * + */ + +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +const SETTINGS_FILE = "gpssetup.settings.json"; +let settings = undefined; +let settings_changed = false; + +function updateSettings() { + require("Storage").write(SETTINGS_FILE, settings); + settings_changed = true; +} + +function loadSettings() { + log_debug("loadSettings()"); + settings = require("Storage").readJSON(SETTINGS_FILE,1)||{}; + settings.update = settings.update||120; + settings.search = settings.search||5; + settings.power_mode = settings.power_mode||"SuperE"; + log_debug(settings); +} + +/*********** GPS Power and Setup Functions ******************/ + +function log_debug(o) { + //let timestamp = new Date().getTime(); + //console.log(timestamp + " : " + o); +} + +function setupGPS() { + Bangle.setGPSPower(1); + if (settings.power_mode === "PSMOO") { + setupPSMOO(); + } else { + setupSuperE(); + } +} + +function delay(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +function setupSuperE() { + log_debug("setupGPS() Super-E"); + Promise.resolve().then(function() { + UBX_CFG_RESET(); + return delay(100); + }).then(function() { + UBX_CFG_PMS(); + return delay(20); + }).then(function() { + UBX_CFG_SAVE(); + return delay(20); + }).then(function() { + log_debug("Powering GPS Off"); + /* + * must be part of the promise chain to ensure that + * setup does not return and powerOff before config functions + * have run + * + */ + Bangle.setGPSPower(0); + return delay(20); + }); +} + +function setupPSMOO() { + log_debug("setupGPS() PSMOO"); + Promise.resolve().then(function() { + UBX_CFG_RESET(); + return delay(100); + }).then(function() { + UBX_CFG_PM2(settings.update, settings.search); + return delay(20); + }).then(function() { + UBX_CFG_RXM(); + return delay(20); + }).then(function() { + UBX_CFG_SAVE(); + return delay(20); + }).then(function() { + log_debug("Powering GPS Off"); + /* + * must be part of the promise chain to ensure that + * setup does not return and powerOff before config functions + * have run + * + */ + Bangle.setGPSPower(0); + return delay(20); + }); +} + +function writeGPScmd(cmd) { + var d = [0xB5,0x62]; // sync chars + d = d.concat(cmd); + var a=0,b=0; + for (var i=2;i>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) { + log_debug("UBX_CFG_PM2()"); + + 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() { + log_debug("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() { + log_debug("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() { + log_debug("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]); +} + + +/*********** GPS Setup Menu App *****************************/ + +function showMainMenu() { + var power_options = ["SuperE","PSMOO"]; + + const mainmenu = { + '': { 'title': 'GPS Setup' }, + '< Back': ()=>{exitSetup();}, + 'Power Mode': { + value: 0 | power_options.indexOf(settings.power_mode), + min: 0, max: 1, + format: v => power_options[v], + onchange: v => { + settings.power_mode = power_options[v]; + updateSettings(); + }, + }, + + 'Update (s)': { + value: settings.update, + min: 10, + max: 1800, + step: 10, + onchange: v => { + settings.update =v; + updateSettings(); + } + }, + 'Search (s)': { + value: settings.search, + min: 1, + max: 65, + step: 1, + onchange: v => { + settings.search = v; + updateSettings(); + } + } + }; + + return E.showMenu(mainmenu); +} + +function exitSetup() { + log_debug("exitSetup()"); + if (settings_changed) { + log_debug(settings); + E.showMessage("Configuring GPS"); + setTimeout(function() { + setupGPS(); + setTimeout(function() { load() }, 750); + }, 500); + } else { + load(); + }; +} + +loadSettings(); +showMainMenu(); + diff --git a/apps/gpssetup/gpssetup.png b/apps/gpssetup/gpssetup.png new file mode 100644 index 000000000..ca5899983 Binary files /dev/null and b/apps/gpssetup/gpssetup.png differ diff --git a/apps/gpssetup/icon.js b/apps/gpssetup/icon.js new file mode 100644 index 000000000..114e6600e --- /dev/null +++ b/apps/gpssetup/icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/ACmsAAIss1mBwIfZqwABFpwuYC4IADGBYuaDQwuDF5ouYLo4vJIYousCYQOIHJIuUCo4uIHJIuUf4wlGEIQaHFywvHGAguiF5GBEIgbHFzAvJDwwuRvV6F6xLJFxmkAAQuKqwtKPQ4RKFwgwICgobHVRJAJFQOr0owIE5AtNDIYRI0ulGBAuNapYABCRGkGBAuGAoIpNGBIIFGBF6FwuBFygAKGBAulGBgujGBWAF0jpBehIvlqwws1jnCGFOs1heBGFQuBFoYwoFxFWwAwjFw+BAAIMBGEIuMGEIuIeQIQFGDouJ1gSHGDYuSGDYuUGDSzBFYP+dAQuNGBN6F6RcBFyAwHwAvQGAYuSGAheCF54wCAAYuRGAQABwGAC6QuWGAV6FyYA/AH4A2A=")); diff --git a/apps/gpssetup/settings.js b/apps/gpssetup/settings.js new file mode 100644 index 000000000..0e3c621d1 --- /dev/null +++ b/apps/gpssetup/settings.js @@ -0,0 +1,4 @@ +(function(back) { + // just go right to our app + load("gpssetup.app.js"); +})(); diff --git a/apps/gpssetup/settings.json b/apps/gpssetup/settings.json new file mode 100644 index 000000000..631ececdc --- /dev/null +++ b/apps/gpssetup/settings.json @@ -0,0 +1 @@ +{"power_mode":"SuperE", "update":120, "search":6}