diff --git a/apps/sixths/ChangeLog b/apps/sixths/ChangeLog index 510748fb3..bff499d30 100644 --- a/apps/sixths/ChangeLog +++ b/apps/sixths/ChangeLog @@ -3,3 +3,11 @@ 0.03: minor code improvements 0.04: make height auto-calibration useful and slow ticks to save power 0.05: add ability to navigate to waypoints, better documentation +0.10: lots of updates + acknowledge commands by vibration and message + ui tweaks -- bigger font, compressing uninteresting info + display meters up/down + display pressure trend + adjust GPS on/off algorithm for more reliable fix + display warnings when GPS altitude does not match baro + diff --git a/apps/sixths/README.md b/apps/sixths/README.md index 18aa85beb..9fd7631f9 100644 --- a/apps/sixths/README.md +++ b/apps/sixths/README.md @@ -26,16 +26,17 @@ Useful gestures: B -- "Battery", show/buzz battery info D -- "Down", previous waypoint -F -- "oFf", disable GPS. -G -- "Gps", enable GPS for 4 hours in low power mode. +F -- "turn oFf gps", disable GPS. +T -- "Turn on gps", enable GPS for 4 hours in low power mode. I -- "Info", toggle info display L -- "aLtimeter", load altimeter app M -- "Mark", create mark from current position N -- "Note", take a note and write it to the log. O -- "Orloj", run orloj app - R -- "Run", run "runplus" app + P -- "runPlus", run "runplus" app +R -- "Reset" daily statistics S -- "Speed", enable GPS for 30 minutes in high power mode. - T -- "Time", buzz current time + G -- "Get time", buzz current time U -- "Up", next waypoint Y -- "compass", reset compass @@ -44,6 +45,7 @@ to communicate back to the user. B -- battery low. E -- acknowledge, gesture understood. +I -- unknown gesture. T -- start of new hour. Three colored dots may appear on display. North is on the 12 o'clock @@ -73,27 +75,18 @@ Todo: *) only turn on compass when needed -*) only warn about battery low when it crosses thresholds, update -battery low message - -*) rename "show" to something else -- it collides with built-in - -*) adjust clock according to GPS - -*) show something more reasonable than (NOTEHERE). - -*) hide messages after timeout. - -*) show route lengths after the fact - *) implement longer recording than "G". -*) Probably T should be G. - -*) sum gps distances for a day - *) allow setting up home altitude, or at least disable auto-calibration *) show time-to-sunset / sunrise? -*) one-second updates when gps is active \ No newline at end of file +*) "myprofile" to read step length + +?) display gps alt + offset to baro + +?) start logging baro pressure + +*) compute climb/descent + +*) switch to compensated compass diff --git a/apps/sixths/metadata.json b/apps/sixths/metadata.json index 585f23170..03a9aa1d9 100644 --- a/apps/sixths/metadata.json +++ b/apps/sixths/metadata.json @@ -1,6 +1,6 @@ { "id": "sixths", "name": "Sixth sense", - "version": "0.05", + "version": "0.10", "description": "Clock for outdoor use with GPS support", "icon": "app.png", "readme": "README.md", diff --git a/apps/sixths/sixths.app.js b/apps/sixths/sixths.app.js index c5fb3b9cf..c8d2e668e 100644 --- a/apps/sixths/sixths.app.js +++ b/apps/sixths/sixths.app.js @@ -1,8 +1,222 @@ // Sixth sense /* eslint-disable no-unused-vars */ -// Options you'll want to edit -const rest_altitude = 354; +/* fmt library v0.2.2 */ +let fmt = { + icon_alt : "\0\x08\x1a\1\x00\x00\x00\x20\x30\x78\x7C\xFE\xFF\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\x00\x00\x00\x00\x00\x00\x00", + icon_m : "\0\x08\x1a\1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\x00\x00\x00\x00\x00\x00\x00", + icon_km : "\0\x08\x1a\1\xC3\xC6\xCC\xD8\xF0\xD8\xCC\xC6\xC3\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\x00\x00\x00\x00\x00\x00\x00", + icon_kph : "\0\x08\x1a\1\xC3\xC6\xCC\xD8\xF0\xD8\xCC\xC6\xC3\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\xFF\x00\xC3\xC3\xFF\xC3\xC3", + icon_c : "\0\x08\x1a\1\x00\x00\x60\x90\x90\x60\x00\x7F\xFF\xC0\xC0\xC0\xC0\xC0\xFF\x7F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + icon_hpa : "\x00\x08\x16\x01\x00\x80\xb0\xc8\x88\x88\x88\x00\xf0\x88\x84\x84\x88\xf0\x80\x8c\x92\x22\x25\x19\x00\x00", + icon_9 : "\x00\x08\x16\x01\x00\x00\x00\x00\x38\x44\x44\x4c\x34\x04\x04\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + icon_10 : "\x00\x08\x16\x01\x00\x08\x18\x28\x08\x08\x08\x00\x00\x18\x24\x24\x24\x24\x18\x00\x00\x00\x00\x00\x00\x00", + + /* 0 .. DD.ddddd + 1 .. DD MM.mmm' + 2 .. DD MM'ss" + */ + geo_mode : 1, + + init: function() {}, + fmtDist: function(km) { + if (km >= 1.0) return km.toFixed(1) + this.icon_km; + return (km*1000).toFixed(0) + this.icon_m; + }, + fmtSteps: function(n) { return this.fmtDist(0.001 * 0.719 * n); }, + fmtAlt: function(m) { return m.toFixed(0) + this.icon_alt; }, + fmtTemp: function(c) { return c.toFixed(1) + this.icon_c; }, + fmtPress: function(p) { + if (p < 900 || p > 1100) + return p.toFixed(0) + this.icon_hpa; + if (p < 1000) { + p -= 900; + return this.icon_9 + this.add0(p.toFixed(0)) + this.icon_hpa; + } + p -= 1000; + return this.icon_10 + this.add0(p.toFixed(0)) + this.icon_hpa; + }, + draw_dot : 1, + add0: function(i) { + if (i > 9) { + return ""+i; + } else { + return "0"+i; + } + }, + fmtTOD: function(now) { + this.draw_dot = !this.draw_dot; + let dot = ":"; + if (!this.draw_dot) + dot = "."; + return now.getHours() + dot + this.add0(now.getMinutes()); + }, + fmtNow: function() { return this.fmtTOD(new Date()); }, + fmtTimeDiff: function(d) { + if (d < 180) + return ""+d.toFixed(0); + d = d/60; + return ""+d.toFixed(0)+"m"; + }, + fmtAngle: function(x) { + switch (this.geo_mode) { + case 0: + return "" + x; + case 1: { + let d = Math.floor(x); + let m = x - d; + m = m*60; + return "" + d + " " + m.toFixed(3) + "'"; + } + case 2: { + let d = Math.floor(x); + let m = x - d; + m = m*60; + let mf = Math.floor(m); + let s = m - mf; + s = s*60; + return "" + d + " " + mf + "'" + s.toFixed(0) + '"'; + } + } + return "bad mode?"; + }, + fmtPos: function(pos) { + let x = pos.lat; + let c = "N"; + if (x<0) { + c = "S"; + x = -x; + } + let s = c+this.fmtAngle(x) + "\n"; + c = "E"; + if (x<0) { + c = "W"; + x = -x; + } + return s + c + this.fmtAngle(x); + }, + fmtFix: function(fix, t) { + if (fix && fix.fix && fix.lat) { + return this.fmtSpeed(fix.speed) + " " + + this.fmtAlt(fix.alt); + } else { + return "N/FIX " + this.fmtTimeDiff(t); + } + }, + fmtSpeed: function(kph) { + return kph.toFixed(1) + this.icon_kph; + }, + radians: function(a) { return a*Math.PI/180; }, + degrees: function(a) { return a*180/Math.PI; }, + // distance between 2 lat and lons, in meters, Mean Earth Radius = 6371km + // https://www.movable-type.co.uk/scripts/latlong.html + // (Equirectangular approximation) + // returns value in meters + distance: function(a,b) { + var x = this.radians(b.lon-a.lon) * Math.cos(this.radians((a.lat+b.lat)/2)); + var y = this.radians(b.lat-a.lat); + return Math.sqrt(x*x + y*y) * 6371000; + }, + // thanks to waypointer + bearing: function(a,b) { + var delta = this.radians(b.lon-a.lon); + var alat = this.radians(a.lat); + var blat = this.radians(b.lat); + var y = Math.sin(delta) * Math.cos(blat); + var x = Math.cos(alat) * Math.sin(blat) - + Math.sin(alat)*Math.cos(blat)*Math.cos(delta); + return Math.round(this.degrees(Math.atan2(y, x))); + }, +}; + +/* gps library v0.1.2 */ +let gps = { + emulator: -1, + init: function(x) { + this.emulator = (process.env.BOARD=="EMSCRIPTEN" + || process.env.BOARD=="EMSCRIPTEN2")?1:0; + }, + state: {}, + on_gps: function(f) { + let fix = this.getGPSFix(); + f(fix); + + /* + "lat": number, // Latitude in degrees + "lon": number, // Longitude in degrees + "alt": number, // altitude in M + "speed": number, // Speed in kph + "course": number, // Course in degrees + "time": Date, // Current Time (or undefined if not known) + "satellites": 7, // Number of satellites + "fix": 1 // NMEA Fix state - 0 is no fix + "hdop": number, // Horizontal Dilution of Precision + */ + this.state.timeout = setTimeout(this.on_gps, 1000, f); + }, + off_gps: function() { + clearTimeout(this.state.timeout); + }, + getGPSFix: function() { + if (!this.emulator) + return Bangle.getGPSFix(); + let fix = {}; + fix.fix = 1; + fix.lat = 50; + fix.lon = 14-(getTime()-this.gps_start) / 1000; /* Go West! */ + fix.alt = 200; + fix.speed = 5; + fix.course = 30; + fix.time = Date(); + fix.satellites = 5; + fix.hdop = 12; + return fix; + }, + gps_start : -1, + start_gps: function() { + Bangle.setGPSPower(1, "libgps"); + this.gps_start = getTime(); + }, + stop_gps: function() { + Bangle.setGPSPower(0, "libgps"); + }, +}; + +/* sun version 0.0.1 */ +let sun = { + SunCalc: null, + lat: 50, + lon: 14, + init: function() { + try { + this.SunCalc = require("suncalc"); // from modules folder + } catch (e) { + print("Require error", e); + } + + print("Have suncalc: ", this.SunCalc); + }, + get_sun_pos: function() { + let d = new Date(); + let sun = this.SunCalc.getPosition(d, this.lat, this.lon); + print(sun.azimuth, sun.altitude); + return sun; + }, + get_sun_time: function() { + let d = new Date(); + let sun = this.SunCalc.getTimes(d, this.lat, this.lon); + print(sun.sunrise, sun.sunset); + return sun; + }, +}; + +sun.init(); +sun.get_sun_pos(); +sun.get_sun_time(); +fmt.init(); +gps.init(); + +var location; const W = g.getWidth(); const H = g.getHeight(); @@ -12,19 +226,28 @@ var buzz = "", /* Set this to transmit morse via vibrations */ inm = "", l = "", /* For incoming morse handling */ in_str = "", note = "", - debug = "v0.04.1", debug2 = "(otherdb)", debug3 = "(short)"; + debug = "v0.5.11", debug2 = "(otherdb)", debug3 = "(short)"; +var note_limit = 0; var mode = 0, mode_time = 0; // 0 .. normal, 1 .. note, 2.. mark name var disp_mode = 0; // 0 .. normal, 1 .. small time +var state = { + gps_limit: 0, // timeout -- when to stop recording + gps_speed_limit: 0, + prev_fix: null, + gps_dist: 0, + + // Marks + cur_mark: null, +}; + // GPS handling var gps_on = 0, // time GPS was turned on last_fix = 0, // time of last fix - last_restart = 0, last_pause = 0, last_fstart = 0; // utime + last_restart = 0, last_pause = 0, // utime + last_fstart = 0; // utime, time of start of last fix var gps_needed = 0, // how long to wait for a fix - gps_limit = 0, // timeout -- when to stop recording - gps_speed_limit = 0; -var prev_fix = null; -var gps_dist = 0; + keep_fix_for = 30; var mark_heading = -1; @@ -34,19 +257,9 @@ var draw_dot = false; var is_level = false; // For altitude handling. -var cur_altitude = 0; +var cur_altitude = -1; var cur_temperature = 0; - -// Marks -var cur_mark = null; - -// Icons - -var icon_alt = "\0\x08\x1a\1\x00\x00\x00\x20\x30\x78\x7C\xFE\xFF\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\x00\x00\x00\x00\x00\x00\x00"; -//var icon_m = "\0\x08\x1a\1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\x00\x00\x00\x00\x00\x00\x00"; -var icon_km = "\0\x08\x1a\1\xC3\xC6\xCC\xD8\xF0\xD8\xCC\xC6\xC3\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\x00\x00\x00\x00\x00\x00\x00"; -var icon_kph = "\0\x08\x1a\1\xC3\xC6\xCC\xD8\xF0\xD8\xCC\xC6\xC3\x00\xC3\xE7\xFF\xDB\xC3\xC3\xC3\xC3\x00\xFF\x00\xC3\xC3\xFF\xC3\xC3"; -var icon_c = "\0\x08\x1a\1\x00\x00\x60\x90\x90\x60\x00\x7F\xFF\xC0\xC0\xC0\xC0\xC0\xFF\x7F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; +var night_pressure = 0; function toMorse(x) { let r = ""; @@ -84,34 +297,28 @@ function gpsPause() { last_restart = 0; last_pause = getTime(); } +function gpsReset() { + state.prev_fix = null; + state.gps_dist = 0; +} function gpsOn() { gps_on = getTime(); gps_needed = 1000; last_fix = 0; - prev_fix = null; - gps_dist = 0; gpsRestart(); } function gpsOff() { Bangle.setGPSPower(0, "sixths"); gps_on = 0; } -function fmtDist(km) { return km.toFixed(1) + icon_km; } -function fmtSteps(n) { return fmtDist(0.001 * 0.719 * n); } -function fmtAlt(m) { return m.toFixed(0) + icon_alt; } -function fmtTimeDiff(d) { - if (d < 180) - return ""+d.toFixed(0); - d = d/60; - return ""+d.toFixed(0)+"m"; -} function gpsHandleFix(fix) { - if (!prev_fix) { - show("GPS acquired", 10); + if (!state.prev_fix) { + showMsg("GPS acquired", 10); doBuzz(" ."); - prev_fix = fix; + state.prev_fix = fix; } - if (1) { + if (0) { + /* Display error between GPS and system time */ let now1 = Date(); let now2 = fix.time; let n1 = now1.getMinutes() * 60 + now1.getSeconds(); @@ -119,42 +326,55 @@ function gpsHandleFix(fix) { debug2 = "te "+(n2-n1)+"s"; } loggps(fix); - let d = calcDistance(fix, prev_fix); + let d = fmt.distance(fix, state.prev_fix); if (d > 30) { - prev_fix = fix; - gps_dist += d/1000; + state.prev_fix = fix; + state.gps_dist += d/1000; } } function gpsHandle() { let msg = ""; + debug2 = "Ne" + gps_needed; + debug3 = "Ke" + keep_fix_for; if (!last_restart) { let d = (getTime()-last_pause); if (last_fix) - msg = "PL"+ fmtTimeDiff(getTime()-last_fix); + msg = "PL"+ fmt.fmtTimeDiff(getTime()-last_fix); else - msg = "PN"+ fmtTimeDiff(getTime()-gps_on); + msg = "PN"+ fmt.fmtTimeDiff(getTime()-gps_on); print("gps on, paused ", d, gps_needed); if (d > gps_needed * 2) { gpsRestart(); } } else { - let fix = Bangle.getGPSFix(); + let fix = gps.getGPSFix(); if (fix && fix.fix && fix.lat) { gpsHandleFix(fix); - msg = fix.speed.toFixed(1) + icon_kph; - print("GPS FIX", msg); + msg = ""; + if (Math.abs(fix.alt - cur_altitude) > 20) + msg += "!"; + if (Math.abs(fix.alt - cur_altitude) > 80) + msg += "!"; + if (Math.abs(fix.alt - cur_altitude) > 320) + msg += "!"; + msg += fmt.fmtSpeed(fix.speed); if (!last_fstart) last_fstart = getTime(); last_fix = getTime(); + keep_fix_for = (last_fstart - last_restart) / 1.5; + if (keep_fix_for < 20) + keep_fix_for = 20; + if (keep_fix_for > 6*60) + keep_fix_for = 6*60; gps_needed = 60; } else { if (last_fix) - msg = "L"+ fmtTimeDiff(getTime()-last_fix); + msg = "L"+ fmt.fmtTimeDiff(getTime()-last_fix); else { - msg = "N"+ fmtTimeDiff(getTime()-gps_on); - if (fix) { + msg = "N"+ fmt.fmtTimeDiff(getTime()-gps_on); + if (0 && fix) { msg += " " + fix.satellites + "sats"; } } @@ -162,50 +382,50 @@ function gpsHandle() { let d = (getTime()-last_restart); let d2 = (getTime()-last_fstart); - print("gps on, restarted ", d, gps_needed, d2, fix.lat); - if (getTime() > gps_speed_limit && - (d > gps_needed || (last_fstart && d2 > 10))) { + print("gps on, restarted ", d, gps_needed, d2); + if (getTime() > state.gps_speed_limit && + ((d > gps_needed && !last_fstart) || (last_fstart && d2 > keep_fix_for))) { gpsPause(); gps_needed = gps_needed * 1.5; print("Pausing, next try", gps_needed); } } - msg += " "+gps_dist.toFixed(1)+icon_km; + msg += " "+fmt.fmtDist(state.gps_dist); return msg; } function markNew() { let r = {}; r.time = getTime(); - r.fix = prev_fix; + r.fix = state.prev_fix; r.steps = Bangle.getHealthStatus("day").steps; - r.gps_dist = gps_dist; + r.gps_dist = state.gps_dist; r.altitude = cur_altitude; r.name = "auto"; return r; } function markHandle() { - let m = cur_mark; + let m = state.cur_mark; let msg = m.name + ">"; if (m.time) { - msg += fmtTimeDiff(getTime()- m.time); + msg += fmt.fmtTimeDiff(getTime()- m.time); } - if (prev_fix && prev_fix.fix && m.fix && m.fix.fix) { - let s = fmtDist(calcDistance(m.fix, prev_fix)/1000) + icon_km; + if (state.prev_fix && state.prev_fix.fix && m.fix && m.fix.fix) { + let s = fmt.fmtDist(fmt.distance(m.fix, state.prev_fix)/1000) + fmt.icon_km; msg += " " + s; debug = "wp>" + s; - mark_heading = 180 + calcBearing(m.fix, prev_fix); + mark_heading = 180 + fmt.bearing(m.fix, state.prev_fix); debug2 = "wp>" + mark_heading; } else { - msg += " w" + fmtDist(gps_dist - m.gps_dist); + msg += " w" + fmt.fmtDist(state.gps_dist - m.gps_dist); } return msg; } function entryDone() { - show(":" + in_str); + showMsg(":" + in_str); doBuzz(" ."); switch (mode) { case 1: logstamp(">" + in_str); break; - case 2: cur_mark.name = in_str; break; + case 2: state.cur_mark.name = in_str; break; } in_str = 0; mode = 0; @@ -225,18 +445,22 @@ function selectWP(i) { if (sel_wp >= waypoints.length) sel_wp = waypoints.length - 1; if (sel_wp < 0) { - show("No WPs", 60); + showMsg("No WPs", 60); } let wp = waypoints[sel_wp]; - cur_mark = {}; - cur_mark.name = wp.name; - cur_mark.gps_dist = 0; /* HACK */ - cur_mark.fix = {}; - cur_mark.fix.fix = 1; - cur_mark.fix.lat = wp.lat; - cur_mark.fix.lon = wp.lon; - show("WP:"+wp.name, 60); - print("Select waypoint: ", cur_mark); + state.cur_mark = {}; + state.cur_mark.name = wp.name; + state.cur_mark.gps_dist = 0; /* HACK */ + state.cur_mark.fix = {}; + state.cur_mark.fix.fix = 1; + state.cur_mark.fix.lat = wp.lat; + state.cur_mark.fix.lon = wp.lon; + showMsg("WP:"+wp.name, 60); + print("Select waypoint: ", state.cur_mark); +} +function ack(cmd) { + showMsg(cmd, 3); + doBuzz(' .'); } function inputHandler(s) { print("Ascii: ", s, s[0], s[1]); @@ -249,7 +473,7 @@ function inputHandler(s) { } if ((mode == 1) || (mode == 2)){ in_str = in_str + s; - show(">"+in_str, 10); + showMsg(">"+in_str, 10); mode_time = getTime(); return; } @@ -262,34 +486,37 @@ function inputHandler(s) { else s = s+(bat/5); doBuzz(toMorse(s)); - show("Bat "+bat+"%", 60); + showMsg("Bat "+bat+"%", 60); break; } - case 'D': selectWP(1); break; - case 'F': gpsOff(); show("GPS off", 3); break; - case 'G': gpsOn(); gps_limit = getTime() + 60*60*4; show("GPS on", 3); break; + case 'D': doBuzz(' .'); selectWP(1); break; + case 'F': gpsOff(); ack("GPS off"); break; + case 'T': gpsOn(); state.gps_limit = getTime() + 60*60*4; ack("GPS on"); break; case 'I': + doBuzz(' .'); disp_mode += 1; if (disp_mode == 2) { disp_mode = 0; } break; case 'L': aload("altimeter.app.js"); break; - case 'M': mode = 2; show("M>", 10); cur_mark = markNew(); mode_time = getTime(); break; - case 'N': mode = 1; show(">", 10); mode_time = getTime(); break; + case 'M': doBuzz(' .'); mode = 2; showMsg("M>", 10); state.cur_mark = markNew(); mode_time = getTime(); break; + case 'N': doBuzz(' .'); mode = 1; showMsg(">", 10); mode_time = getTime(); break; case 'O': aload("orloj.app.js"); break; - case 'R': aload("runplus.app.js"); break; - case 'S': gpsOn(); gps_limit = getTime() + 60*30; gps_speed_limit = gps_limit; show("GPS on", 3); break; - case 'T': { + case 'R': gpsReset(); ack("GPS reset"); break; + case 'P': aload("runplus.app.js"); break; + case 'S': gpsOn(); state.gps_limit = getTime() + 60*30; state.gps_speed_limit = state.gps_limit; ack("GPS speed"); break; + case 'G': { s = ' T'; let d = new Date(); s += d.getHours() % 10; - s += add0(d.getMinutes()); + s += fmt.add0(d.getMinutes()); doBuzz(toMorse(s)); break; } - case 'U': selectWP(-1); break; - case 'Y': doBuzz(buzz); Bangle.resetCompass(); break; + case 'U': doBuzz(' .'); selectWP(-1); break; + case 'Y': ack('Compass reset'); Bangle.resetCompass(); break; + default: doBuzz(' ..'); showMsg("Unknown "+s, 5); break; } } const morseDict = { @@ -389,19 +616,15 @@ function touchHandler(d) { //print(inm, "drag:", d); } -function add0(i) { - if (i > 9) { - return ""+i; - } else { - return "0"+i; - } -} var lastHour = -1, lastMin = -1; function logstamp(s) { - logfile.write("utime=" + getTime() + " " + s + "\n"); + logfile.write("utime=" + getTime() + + " bele=" + cur_altitude + + " batperc=" + E.getBattery() + + " " + s + "\n"); } function loggps(fix) { - logfile.write(fix.lat + " " + fix.lon + " "); + logfile.write(fix.lat + " " + fix.lon + " ele=" + fix.alt + " "); logstamp(""); } function hourly() { @@ -410,15 +633,19 @@ function hourly() { let bat = E.getBattery(); if (bat < 25) { s = ' B'; - show("Bat "+bat+"%", 60); + showMsg("Bat "+bat+"%", 60); } if (is_active) doBuzz(toMorse(s)); //logstamp(""); } -function show(msg, timeout) { +function showMsg(msg, timeout) { + note_limit = getTime() + timeout; note = msg; } + +var prev_step = -1, this_step = -1; + function fivemin() { print("fivemin"); let s = ' B'; @@ -429,7 +656,8 @@ function fivemin() { } catch (e) { print("Altimeter error", e); } - + prev_step = this_step; + this_step = Bangle.getStepCount(); } function every(now) { if ((mode > 0) && (getTime() - mode_time > 10)) { @@ -438,7 +666,7 @@ function every(now) { } mode = 0; } - if (gps_on && getTime() > gps_limit && getTime() > gps_speed_limit) { + if (gps_on && getTime() > state.gps_limit && getTime() > state.gps_speed_limit) { Bangle.setGPSPower(0, "sixths"); gps_on = 0; } @@ -447,38 +675,17 @@ function every(now) { lastHour = now.getHours(); hourly(); } - if (lastMin / 5 != now.getMinutes() / 5) { + if (lastMin / 5 != now.getMinutes() / 5) { // fixme, trunc? lastMin = now.getMinutes(); fivemin(); } - } -function radians(a) { return a*Math.PI/180; } -function degrees(a) { return a*180/Math.PI; } -// distance between 2 lat and lons, in meters, Mean Earth Radius = 6371km -// https://www.movable-type.co.uk/scripts/latlong.html -// (Equirectangular approximation) -function calcDistance(a,b) { - var x = radians(b.lon-a.lon) * Math.cos(radians((a.lat+b.lat)/2)); - var y = radians(b.lat-a.lat); - return Math.sqrt(x*x + y*y) * 6371000; -} -// thanks to waypointer -function calcBearing(a,b){ - var delta = radians(b.lon-a.lon); - var alat = radians(a.lat); - var blat = radians(b.lat); - var y = Math.sin(delta) * Math.cos(blat); - var x = Math.cos(alat)*Math.sin(blat) - - Math.sin(alat)*Math.cos(blat)*Math.cos(delta); - return Math.round(degrees(Math.atan2(y, x))); -} function testBearing() { let p1 = {}, p2 = {}; p1.lat = 40; p2.lat = 50; p1.lon = 14; p2.lon = 14; - print("bearing = ", calcBearing(p1, p2)); + print("bearing = ", fmt.bearing(p1, p2)); } function radA(p) { return p*(Math.PI*2); } @@ -509,9 +716,9 @@ function drawBackground() { drawDot(h, 0.7, 10); } } - if (prev_fix && prev_fix.fix) { + if (state.prev_fix && state.prev_fix.fix) { g.setColor(0.5, 1, 0.5); - drawDot(prev_fix.course, 0.5, 6); + drawDot(state.prev_fix.course, 0.5, 6); } if (mark_heading != -1) { g.setColor(1, 0.5, 0.5); @@ -523,13 +730,66 @@ function drawTime(now) { g.setFont('Vector', 60); else g.setFont('Vector', 26); - g.setFontAlign(1, 1); + g.setFontAlign(1, -1); draw_dot = !draw_dot; let dot = ":"; if (!draw_dot) dot = "."; - g.drawString(now.getHours() + dot + add0(now.getMinutes()), W, 90); + let s = ""; + if (disp_mode == 1) + s = debug; + g.drawString(s + now.getHours() + dot + fmt.add0(now.getMinutes()), W, 28); } + +var base_alt = -1, ext_alt = -1, tot_down = 0, tot_up = 0; + +function walkHandle() { + let msg = ""; + let step = Bangle.getStepCount(); + let cur = cur_altitude; + if (base_alt == -1) { + base_alt = cur; + ext_alt = cur; + } + if (this_step - prev_step > 100 + || 1 + || step - this_step > 100) { + //msg += fmt.fmtSteps((this_step - prev_step) * 12); + + let dir = ext_alt > base_alt; /* 1.. climb */ + if (!dir) dir = -1; + let hyst = 2; + if (Math.abs(cur - base_alt) > hyst) { + if (cur*dir > ext_alt*dir) { + ext_alt = cur; + } + } + let diff = ext_alt - base_alt; + if (cur*dir < (ext_alt - hyst*dir)*dir) { + if (1 == dir) { + tot_up += diff; + } + if (-1 == dir) { + tot_down += -diff; + } + base_alt = ext_alt; + ext_alt = cur; + } + let tmp_down = tot_down, tmp_up = tot_up; + if (1 == dir) { + tmp_up += diff; + } + if (-1 == dir) { + tmp_down += -diff; + } + + msg += " " + fmt.fmtAlt(tmp_down) + " " + fmt.fmtAlt(tmp_up); + + return msg + "\n"; + } + return ""; +} + function draw() { if (disp_mode == 2) { draw_all(); @@ -542,16 +802,6 @@ function draw() { g.setColor(0.25, 1, 1); g.fillPoly([ W/2, 24, W, 80, 0, 80 ]); } - let msg = ""; - if (gps_on) { - msg = gpsHandle(); - } else { - let o = Bangle.getOptions(); - msg = o.seaLevelPressure.toFixed(1) + "hPa"; - if (note != "") { - msg = note; - } - } drawBackground(); let now = new Date(); @@ -562,43 +812,75 @@ function draw() { //let km = 0.001 * 0.719 * Bangle.getHealthStatus("day").steps; - g.setFontAlign(-1, 1); - g.setFont('Vector', 26); + // 33 still fits + g.setFont('Vector', 30); const weekday = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]; - g.drawString(weekday[now.getDay()] + "" + now.getDate() + ". " - + fmtSteps(Bangle.getHealthStatus("day").steps), 10, 115); + let msg = weekday[now.getDay()] + "" + now.getDate() + ". " + + fmt.fmtSteps(Bangle.getHealthStatus("day").steps) + "\n"; - g.drawString(msg, 10, 145); + if (gps_on) { + msg += gpsHandle() + "\n"; + } + + if (state.cur_mark) { + msg += markHandle() + "\n"; + } + + if (note != "") { + if (getTime() > note_limit) + note = ""; + else + msg += note + "\n"; + } + + msg += walkHandle(); if (getTime() - last_active > 15*60) { - let alt_adjust = cur_altitude - rest_altitude; + let alt_adjust = cur_altitude - location.alt; let abs = Math.abs(alt_adjust); print("adj", alt_adjust); let o = Bangle.getOptions(); if (abs > 10 && abs < 150) { let a = 0.01; // FIXME: draw is called often compared to alt reading - if (cur_altitude > rest_altitude) + if (cur_altitude > location.alt) a = -a; o.seaLevelPressure = o.seaLevelPressure + a; Bangle.setOptions(o); } - msg = o.seaLevelPressure.toFixed(1) + "hPa"; + let pr = o.seaLevelPressure; + if (pr) + msg += fmt.fmtPress(pr); + else + msg += "emu?"; } else { - msg = fmtAlt(cur_altitude); + msg += fmt.fmtAlt(cur_altitude); } - msg = msg + " " + cur_temperature.toFixed(1)+icon_c; - if (cur_mark) { - msg = markHandle(); - } - g.drawString(msg, 10, 175); - if (disp_mode == 1) { - g.drawString(debug, 10, 45); - g.drawString(debug2, 10, 65); - g.drawString(debug3, 10, 85); + msg = msg + " " + fmt.fmtTemp(cur_temperature) + "\n"; + + { + let o = Bangle.getOptions(); + let pr = o.seaLevelPressure; + + if (now.getHours() < 6) + night_pressure = pr; + if (night_pressure) + msg += (pr-night_pressure).toFixed(1) + fmt.icon_hpa + " "; + if (pr) + msg += fmt.fmtPress(pr) + "\n"; + } + g.setFontAlign(-1, -1); + if (disp_mode == 0) + g.drawString(msg, 10, 85); + else + g.drawString(msg, 10, 60); + + if (0 && disp_mode == 1) { + g.setFont('Vector', 21); + g.drawString(debug + "\n" + debug2 + "\n" + debug3, 10, 20); } queueDraw(); @@ -611,7 +893,7 @@ function draw_all() { g.setColor(1, 1, 1); g.setFontAlign(-1, 1); let now = new Date(); - g.drawString(now.getHours() + ":" + add0(now.getMinutes()) + ":" + add0(now.getSeconds()), 10, 40); + g.drawString(now.getHours() + ":" + fmt.add0(now.getMinutes()) + ":" + fmt.add0(now.getSeconds()), 10, 40); let acc = Bangle.getAccel(); let ax = 0 + acc.x, ay = 0.75 + acc.y, az = 0.75 + acc.y; @@ -727,7 +1009,8 @@ function lockHandler(locked) { function queueDraw() { let next; - if (getTime() - last_unlocked > 3*60) + if ((getTime() - last_unlocked > 3*60) && + (getTime() > state.gps_limit)) next = 60000; else next = 1000; @@ -754,6 +1037,8 @@ function start() { } draw(); + location = require("Storage").readJSON("mylocation.json",1)||{"lat":50,"lon":14.45,"alt":354,"location":"Woods"}; + state = require("Storage").readJSON("sixths.json",1)||state; loadWPs(); buzzTask(); if (0)