Merge remote-tracking branch 'origin/develop' into develop

pull/607/head
OmegaRogue 2020-12-09 14:53:03 +01:00
commit dbf68a8208
25 changed files with 392 additions and 161 deletions

View File

@ -394,6 +394,18 @@ It should also add `app.json` to `data`, to make sure it is cleaned up when the
},
```
## Modules
You can include any of [Espruino's modules](https://www.espruino.com/Modules) as
normal with `require("modulename")`. If you want to develop your own module for your
app(s) then you can do that too. Just add the module into the `modules` folder
then you can use it from your app as normal.
You won't be able to develop apps using your own modules with the IDE,
so instead we'd recommend you write your module to a Storage File called
`modulename` on Bangle.js. You can then develop your app as normal on Bangle.js
from the IDE.
## Coding hints
- use `g.setFont(.., size)` to multiply the font size, eg ("6x8",3) : "18x24"

View File

@ -65,7 +65,7 @@
{ "id": "locale",
"name": "Languages",
"icon": "locale.png",
"version":"0.07",
"version":"0.08",
"description": "Translations for different countries",
"tags": "tool,system,locale,translate",
"type": "locale",
@ -139,9 +139,10 @@
{ "id": "gbridge",
"name": "Gadgetbridge",
"icon": "app.png",
"version":"0.17",
"version":"0.18",
"description": "The default notification handler for Gadgetbridge notifications from Android",
"tags": "tool,system,android,widget",
"readme": "README.md",
"type":"widget",
"dependencies": { "notify":"type" },
"storage": [
@ -362,14 +363,15 @@
{ "id": "gpsrec",
"name": "GPS Recorder",
"icon": "app.png",
"version":"0.13",
"version":"0.16",
"interface": "interface.html",
"description": "Application that allows you to record a GPS track. Can run in background",
"tags": "tool,outdoors,gps,widget",
"storage": [
{"name":"gpsrec.app.js","url":"app.js"},
{"name":"gpsrec.img","url":"app-icon.js","evaluate":true},
{"name":"gpsrec.wid.js","url":"widget.js"}
{"name":"gpsrec.wid.js","url":"widget.js"},
{"name":"gpsrec.settings.js","url":"settings.js"}
],
"data": [
{"name":"gpsrec.json"},
@ -379,13 +381,13 @@
{ "id": "gpsnav",
"name": "GPS Navigation",
"icon": "icon.png",
"version":"0.04",
"version":"0.05",
"description": "Displays GPS Course and Speed, + Directions to waypoint and waypoint recording, now with waypoint editor",
"tags": "tool,outdoors,gps",
"readme": "README.md",
"interface":"waypoints.html",
"storage": [
{"name":"gpsnav.app.js","url":"app.js"},
{"name":"gpsnav.app.js","url":"app.min.js"},
{"name":"waypoints.json","url":"waypoints.json","evaluate":false},
{"name":"gpsnav.img","url":"app-icon.js","evaluate":true}
]
@ -941,12 +943,12 @@
]
},
{ "id": "assistedgps",
"name": "Assisted GPS Update",
"name": "Assisted GPS Update (AGPS)",
"icon": "app.png",
"version":"0.01",
"description": "Downloads assisted GPS data to Bangle.js for faster GPS startup and more accurate fixes",
"custom": "custom.html",
"tags": "tool,outdoors",
"tags": "tool,outdoors,agps",
"type": "RAM",
"storage": [ ]
},
@ -1325,11 +1327,12 @@
"name": "OpenStreetMap",
"shortName":"OpenStMap",
"icon": "app.png",
"version":"0.03",
"version":"0.05",
"description": "[BETA] Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are",
"tags": "outdoors,gps",
"custom": "custom.html",
"storage": [
{"name":"openstmap","url":"openstmap.js"},
{"name":"openstmap.app.js","url":"app.js"},
{"name":"openstmap.img","url":"app-icon.js","evaluate":true}
]
@ -2290,7 +2293,7 @@
"name": "File manager",
"shortName":"FileManager",
"icon": "icons8-filing-cabinet-48.png",
"version":"0.01",
"version":"0.02",
"description": "Simple file manager, allows user to examine watch storage and display, load or delete individual files",
"tags": "tools",
"readme": "README.md",
@ -2473,6 +2476,6 @@
"storage": [
{"name":"gmeter.app.js","url":"app.js"},
{"name":"gmeter.img","url":"app-icon.js","evaluate":true}
]
]
}
]

View File

@ -84,15 +84,13 @@
var chunkSize = 128;
var js = "\x10Bangle.setGPSPower(1);\n"; // turn GPS on
//js += `\x10Bangle.on('GPS-raw',function (d) { if (d.startsWith("\\xB5\\x62\\x05\\x01")) Terminal.println("GPS ACK"); else if (d.startsWith("\\xB5\\x62\\x05\\x00")) Terminal.println("GPS NACK"); })\n`;
js += "\x10E.showMessage('Uploading...','AGPS');function p(n) {g.fillRect(0,g.getHeight()-80,g.getWidth()*n,g.getHeight()-60);}";
//js += "\x10var t=getTime()+1;while(t>getTime());\n"; // wait 1 sec
js += `\x10Serial1.write(atob("${btoa(String.fromCharCode.apply(null,UBX_MGA_INI_TIME_UTC()))}"))\n`; // set GPS time
for (var i=0;i<bin.length;i+=chunkSize) {
var chunk = bin.substr(i,chunkSize);
js += `\x10p(${Math.round(100*i/bin.length)/100});Serial1.write(atob("${btoa(chunk)}"))\n`;
js += `\x10Serial1.write(atob("${btoa(chunk)}"))\n`;
}
js += "\x10p(1);\n"; // finish progress bar
return js;
}

View File

@ -1 +1,2 @@
0.01: New app!
0.02: Improve handling of large amounts of files (fix #579)

View File

@ -10,12 +10,12 @@ function delete_file(fn) {
E.showPrompt("Delete\n"+fn+"?", {buttons: {"No":false, "Yes":true}}).then(function(v) {
if (v) {
if (fn.charCodeAt(fn.length-1)==1) {
var fh = STOR.open(fn.substr(0, fn.length-1), "w");
var fh = STOR.open(fn.substr(0, fn.length-1), "r");
fh.erase();
}
else STOR.erase(fn);
}
}).then(function() { files=get_pruned_file_list(); }).then(drawMenu);
}).then(function() { filed=[];files=get_pruned_file_list(); }).then(drawMenu);
}
function get_length(fn) {
@ -90,10 +90,13 @@ function drawMenu() {
}
function get_pruned_file_list() {
var fl = STOR.list(/^[^\.]/);
// get storagefile list
var sf = STOR.list(/\1$/).map(s=>s.slice(0,-1));
var sffilter = f=>!sf.includes(f.slice(0,-1)) || f.endsWith("\1");
// get files - put '.' last
var fl = STOR.list(/^[^\.]/).filter(sffilter);
fl.sort();
fl = fl.concat(STOR.list(/^\./));
fl = fl.filter(f => (f.charCodeAt(f.length-1)>31 || f.charCodeAt(f.length-1)<2));
fl = fl.concat(STOR.list(/^\./).filter(sffilter).sort());
return fl;
}

View File

@ -17,3 +17,4 @@
0.16: Handle dismissing notifications on the phone
Nicer display of alarm clock notifications
0.17: Modified music notification for updated 'notify' library
0.18: Added reporting of step count and HRM (new Gadgetbridges can now log this)

54
apps/gbridge/README.md Normal file
View File

@ -0,0 +1,54 @@
Gadgetbridge
=============
This widget allows your Bangle.js to communicate with the Gadgetbridge app on an Android phone.
Download the [latest Gadgetbridge for Android here](https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/).
This app supports:
* Displaying Notifications
* Song display and control
* Call answering
* Find My Phone / Find My Bangle
* Activity reporting
You can also add [the weather widget](https://banglejs.com/apps/#weather)
Notifications
-------------
By default a notification at the top of the screen is displayed. If you'd like a fullscreen notification
(which will involve leaving the current app) then install [Fullscreen Notifications](https://banglejs.com/apps/#notifyfs)
Song display and control
------------------------
When the Song Display notification is showing on the screen and a song is playing, you
can swipe left or right on the screen to go to the next or previous song.
Find My Phone
-------------
Go to `Settings`, `App/Widget Settings`, `Gadgetbridge`, `Find Phone`, `On`
If in range and connected your phone should start ringing.
Find My Bangle
-------------
Onyour phone `Settings`, `App/Widget Settings`, `Gadgetbridge`, `Find Phone`, `On`
If in range and connected your phone should start ringing.
Activity reporting
------------------
You'll need a Gadgetbridge release *after* version 0.50.0 for Actvity Reporting to be enabled.
By default heart rate isn't reported, but it can be enabled from `Settings`, `App/Widget Settings`, `Gadgetbridge`, `Record HRM`

View File

@ -12,7 +12,7 @@
function updateSetting(setting, value) {
let settings = require('Storage').readJSON("gbridge.json", true) || {};
settings[setting] = value
require('Storage').write('gbridge.json', settings);
require('Storage').writeJSON('gbridge.json', settings);
}
function setIcon(visible) {
updateSetting('showIcon', visible);
@ -30,6 +30,11 @@
onchange: setIcon
},
"Find Phone" : function() { E.showMenu(findPhone); },
"Record HRM" : {
value: settings().hrm,
format: v => v?"Yes":"No",
onchange: v => updateSetting('hrm', v)
},
"< Back" : back,
};

View File

@ -1,4 +1,5 @@
(() => {
// Music handling
const state = {
music: "stop",
@ -10,13 +11,17 @@
scrollPos: 0
};
// activity reporting
var currentSteps = 0, lastSentSteps=0;
var activityInterval;
var hrmTimeout;
function settings() {
let settings = require('Storage').readJSON("gbridge.json", true) || {};
if (!("showIcon" in settings)) {
settings.showIcon = true;
}
return settings
return settings;
}
function gbSend(message) {
@ -143,6 +148,45 @@
setTimeout(_=>Bangle.beep(), 1000);
},2000);
}
function handleActivityEvent(event) {
var s = settings();
// handle setting activity interval
if (s.activityInterval===undefined ||
s.activityInterval<30)
s.activityInterval = 3*60; // 3 minutes default
if (event.int) {
if (event.int<30) event.int = 30; // min 30 secs
s.activityInterval = event.int;
require('Storage').writeJSON("gbridge.json", s);
}
// set up interval/HRM to handle activity data
var interval = s.activityInterval;
var realtime = event.hrm || event.stp;
if (activityInterval)
clearInterval(activityInterval);
activityInterval = undefined;
if (s.hrm) Bangle.setHRMPower(1);
if (s.hrm) {
if (realtime) {
// if realtime reporting, leave HRM on and use that to trigger events
hrmTimeout = undefined;
} else {
// else trigger it manually every so often
hrmTimeout = 5;
activityInterval = setInterval(function() {
hrmTimeout = 5;
Bangle.setHRMPower(1);
}, interval*1000);
}
} else {
// no HRM - manually push data
if (realtime) interval=10;
activityInterval = setInterval(function() {
sendActivity(-1);
}, interval*1000);
}
}
var _GB = global.GB;
global.GB = (event) => {
@ -163,6 +207,9 @@
case "find":
handleFindEvent(event);
break;
case "act":
handleActivityEvent(event);
break;
}
if(_GB)setTimeout(_GB,0,event);
};
@ -201,14 +248,39 @@
}
}
WIDGETS["gbridgew"] = {area: "tl", width: 24, draw: draw, reload: reload};
reload();
function sendBattery() {
gbSend({ t: "status", bat: E.getBattery() });
}
// Send a summary of activity to Gadgetbridge
function sendActivity(hrm) {
var steps = currentSteps - lastSentSteps;
lastSentSteps = 0;
gbSend({ t: "act", stp: steps, hrm:hrm });
}
// Battery monitor
NRF.on("connect", () => setTimeout(sendBattery, 2000));
setInterval(sendBattery, 10*60*1000);
sendBattery();
// Activity monitor
Bangle.on("step", s => {
if (!lastSentSteps)
lastSentSteps = s-1;
currentSteps = s;
});
Bangle.on('HRM',function(hrm) {
var ok = hrm.confidence>80;
if (hrmTimeout!==undefined) hrmTimeout--;
if (ok || hrmTimeout<=0) {
if (hrmTimeout!==undefined)
Bangle.setHRMPower(0);
sendActivity(hrm.confidence>20 ? hrm.bpm : -1);
}
});
handleActivityEvent({}); // kicks off activity reporting
// Finally add widget
WIDGETS["gbridgew"] = {area: "tl", width: 24, draw: draw, reload: reload};
reload();
})();

View File

@ -2,3 +2,5 @@
0.02: Add SCREENACCESS interface
0.03: Add Waypoint Editor
0.04: Fix great circle formula
0.05: Use locale for speed and distance + fix Vector font sizes

View File

@ -4,18 +4,19 @@ var buf = Graphics.createArrayBuffer(240,50,2,{msb:true});
var candraw = true;
function flip(b,y) {
g.drawImage({width:240,height:50,bpp:2,buffer:b.buffer, palette:pal2color},0,y);
b.clear();
g.drawImage({width:240,height:50,bpp:2,buffer:b.buffer, palette:pal2color},0,y);
b.clear();
}
var brg=0;
var wpindex=0;
const labels = ["N","NE","E","SE","S","SW","W","NW"];
var loc = require("locale");
function drawCompass(course) {
if (!candraw) return;
buf.setColor(1);
buf.setFont("Vector",16);
buf.setFont("Vector",24);
var start = course-90;
if (start<0) start+=360;
buf.fillRect(28,45,212,49);
@ -44,27 +45,21 @@ function drawCompass(course) {
if (bpos>210) bpos = 226;
buf.setColor(2);
buf.fillCircle(bpos,40,8);
}
}
flip(buf,Yoff);
}
//displayed heading
var heading = 0;
function newHeading(m,h){
var s = Math.abs(m - h);
var delta = 1;
if (s<2) return h;
if (m > h){
if (s >= 180) { delta = -1; s = 360 - s;}
} else if (m <= h){
if (s < 180) delta = -1;
else s = 360 -s;
}
delta = delta * (1 + Math.round(s/15));
heading+=delta;
if (heading<0) heading += 360;
if (heading>360) heading -= 360;
return heading;
function newHeading(m,h){
var s = Math.abs(m - h);
var delta = (m>h)?1:-1;
if (s>=180){s=360-s; delta = -delta;}
if (s<2) return h;
var hd = h + delta*(1 + Math.round(s/5));
if (hd<0) hd+=360;
if (hd>360)hd-= 360;
return hd;
}
var course =0;
@ -93,27 +88,28 @@ function bearing(a,b){
}
function distance(a,b){
var dsigma = Math.acos(Math.sin(radians(a.lat))*Math.sin(radians(b.lat))+Math.cos(radians(a.lat))*Math.cos(radians(b.lat))*Math.cos(radians(a.lon-b.lon)));
return Math.round(dsigma*6371000);
var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2));
var y = radians(b.lat-a.lat);
return Math.round(Math.sqrt(x*x + y*y) * 6371000);
}
var selected = false;
function drawN(){
var txt = loc.speed(speed);
buf.setColor(1);
buf.setFont("6x8",2);
buf.drawString("o",100,0);
buf.setFont("6x8",1);
buf.drawString("kph",220,40);
buf.setFont("Vector",40);
buf.drawString(txt.substring(txt.length-3),220,40);
buf.setFont("Vector",48);
var cs = course.toString();
cs = course<10?"00"+cs : course<100 ?"0"+cs : cs;
buf.drawString(cs,10,0);
var txt = (speed<10) ? speed.toFixed(1) : Math.round(speed);
buf.drawString(txt,140,4);
buf.drawString(txt.substring(0,txt.length-3),140,4);
flip(buf,Yoff+70);
buf.setColor(1);
buf.setFont("Vector",20);
buf.setFont("Vector",24);
var bs = brg.toString();
bs = brg<10?"00"+bs : brg<100 ?"0"+bs : bs;
buf.setColor(3);
@ -123,10 +119,7 @@ function drawN(){
buf.drawString(wp.name,140,0);
buf.setColor(1);
buf.drawString(bs,60,0);
if (dist<1000)
buf.drawString(dist.toString()+"m",60,30);
else
buf.drawString((dist/1000).toFixed(2)+"Km",60,30);
buf.drawString(loc.distance(dist),60,30);
flip(buf,Yoff+130);
g.setFont("6x8",1);
g.setColor(0,0,0);
@ -165,7 +158,7 @@ function stopdraw() {
function startTimers() {
candraw=true;
intervalRefSec = setInterval(function() {
newHeading(course,heading);
heading = newHeading(course,heading);
if (course!=heading) drawCompass(heading);
},200);
}
@ -192,17 +185,17 @@ function setButtons(){
}
var SCREENACCESS = {
withApp:true,
request:function(){
this.withApp=false;
stopdraw();
clearWatch();
},
release:function(){
this.withApp=true;
startdraw();
setButtons();
}
withApp:true,
request:function(){
this.withApp=false;
stopdraw();
clearWatch();
},
release:function(){
this.withApp=true;
startdraw();
setButtons();
}
}
Bangle.on('lcdPower',function(on) {
@ -227,10 +220,10 @@ function nextwp(inc){
}
function doselect(){
if (selected && waypoints[wpindex].lat===undefined && savedfix.fix) {
waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon};
wp = waypoints[wpindex];
require("Storage").writeJSON("waypoints.json", waypoints);
if (selected && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) {
waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon};
wp = waypoints[wpindex];
require("Storage").writeJSON("waypoints.json", waypoints);
}
selected=!selected;
drawN();

11
apps/gpsnav/app.min.js vendored Normal file
View File

@ -0,0 +1,11 @@
var Yoff=40,pal2color=new Uint16Array([0,65535,2047,50712],0,2),buf=Graphics.createArrayBuffer(240,50,2,{msb:!0}),candraw=!0;function flip(a,c){g.drawImage({width:240,height:50,bpp:2,buffer:a.buffer,palette:pal2color},0,c);a.clear()}var brg=0,wpindex=0,labels="N NE E SE S SW W NW".split(" "),loc=require("locale");
function drawCompass(a){if(candraw){buf.setColor(1);buf.setFont("Vector",24);var c=a-90;0>c&&(c+=360);buf.fillRect(28,45,212,49);var b=30,d=15-c%15;15>d?b+=d:d=0;for(var e=d;e<=180-d;e+=15){var f=c+e;0==f%90?(buf.drawString(labels[Math.floor(f/45)%8],b-8,0),buf.fillRect(b-2,25,b+2,45)):0==f%45?(buf.drawString(labels[Math.floor(f/45)%8],b-12,0),buf.fillRect(b-2,30,b+2,45)):0==f%15&&buf.fillRect(b,35,b+1,45);b+=15}0!=wpindex&&(a=brg-a,180<a&&(a-=360),-180>a&&(a+=360),a+=120,30>a&&(a=14),210<a&&(a=226),
buf.setColor(2),buf.fillCircle(a,40,8));flip(buf,Yoff)}}var heading=0;function newHeading(a,c){var b=Math.abs(a-c),d=a>c?1:-1;180<=b&&(b=360-b,d=-d);if(2>b)return c;b=c+d*(1+Math.round(b/5));0>b&&(b+=360);360<b&&(b-=360);return b}var course=0,speed=0,satellites=0,wp,dist=0;function radians(a){return a*Math.PI/180}function degrees(a){return(180*a/Math.PI+360)%360}
function bearing(a,c){var b=radians(c.lon-a.lon),d=radians(a.lat),e=radians(c.lat);return Math.round(degrees(Math.atan2(Math.sin(b)*Math.cos(e),Math.cos(d)*Math.sin(e)-Math.sin(d)*Math.cos(e)*Math.cos(b))))}function distance(a,c){var b=radians(a.lon-c.lon)*Math.cos(radians((a.lat+c.lat)/2)),d=radians(c.lat-a.lat);return Math.round(6371E3*Math.sqrt(b*b+d*d))}var selected=!1;
function drawN(){var a=loc.speed(speed);buf.setColor(1);buf.setFont("6x8",2);buf.drawString("o",100,0);buf.setFont("6x8",1);buf.drawString(a.substring(a.length-3),220,40);buf.setFont("Vector",48);var c=course.toString();c=10>course?"00"+c:100>course?"0"+c:c;buf.drawString(c,10,0);buf.drawString(a.substring(0,a.length-3),140,4);flip(buf,Yoff+70);buf.setColor(1);buf.setFont("Vector",24);a=brg.toString();a=10>brg?"00"+a:100>brg?"0"+a:a;buf.setColor(3);buf.drawString("Brg: ",0,0);buf.drawString("Dist: ",
0,30);buf.setColor(selected?1:2);buf.drawString(wp.name,140,0);buf.setColor(1);buf.drawString(a,60,0);buf.drawString(loc.distance(dist),60,30);flip(buf,Yoff+130);g.setFont("6x8",1);g.setColor(0,0,0);g.fillRect(10,230,60,239);g.setColor(1,1,1);g.drawString("Sats "+satellites.toString(),10,230)}var savedfix;
function onGPS(a){savedfix=a;void 0!==a&&(course=isNaN(a.course)?course:Math.round(a.course),speed=isNaN(a.speed)?speed:a.speed,satellites=a.satellites);candraw&&(void 0!==a&&1==a.fix&&(dist=distance(a,wp),isNaN(dist)&&(dist=0),brg=bearing(a,wp),isNaN(brg)&&(brg=0)),drawN())}var intervalRef;function stopdraw(){candraw=!1;intervalRef&&clearInterval(intervalRef)}
function startTimers(){candraw=!0;intervalRefSec=setInterval(function(){heading=newHeading(course,heading);course!=heading&&drawCompass(heading)},200)}function drawAll(){g.setColor(1,.5,.5);g.fillPoly([120,Yoff+50,110,Yoff+70,130,Yoff+70]);g.setColor(1,1,1);drawN();drawCompass(heading)}function startdraw(){g.clear();Bangle.drawWidgets();startTimers();drawAll()}
function setButtons(){setWatch(nextwp.bind(null,-1),BTN1,{repeat:!0,edge:"falling"});setWatch(doselect,BTN2,{repeat:!0,edge:"falling"});setWatch(nextwp.bind(null,1),BTN3,{repeat:!0,edge:"falling"})}var SCREENACCESS={withApp:!0,request:function(){this.withApp=!1;stopdraw();clearWatch()},release:function(){this.withApp=!0;startdraw();setButtons()}};Bangle.on("lcdPower",function(a){SCREENACCESS.withApp&&(a?startdraw():stopdraw())});var waypoints=require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
wp=waypoints[0];function nextwp(a){selected&&(wpindex+=a,wpindex>=waypoints.length&&(wpindex=0),0>wpindex&&(wpindex=waypoints.length-1),wp=waypoints[wpindex],drawN())}function doselect(){selected&&0!=wpindex&&void 0===waypoints[wpindex].lat&&savedfix.fix&&(waypoints[wpindex]={name:"@"+wp.name,lat:savedfix.lat,lon:savedfix.lon},wp=waypoints[wpindex],require("Storage").writeJSON("waypoints.json",waypoints));selected=!selected;drawN()}g.clear();Bangle.setLCDBrightness(1);Bangle.loadWidgets();Bangle.drawWidgets();
Bangle.setGPSPower(1);drawAll();startTimers();Bangle.on("GPS",onGPS);setButtons();

View File

@ -15,3 +15,6 @@
0.12: Add option to plot on top of OpenStreetMap tiles (when they are installed on the watch)
0.13: Increase GPS recording accuracy by one decimal place
Ensure default time period is 10
0.14: Now use the openstmap lib for map plotting
0.15: Add plotTrack method to allow current track to be plotted on a map (#395)
Add gpsrec app to Settings menu

View File

@ -2,7 +2,10 @@ Bangle.loadWidgets();
Bangle.drawWidgets();
var settings = require("Storage").readJSON("gpsrec.json",1)||{};
var qOpenStMap = (require("Storage").list("openstmap.json")>0);
var osm;
try { // if it's installed, use the OpenStreetMap module
osm = require("openstmap");
} catch (e) {}
function getFN(n) {
return ".gpsrc"+n.toString(36);
@ -134,7 +137,7 @@ function viewTrack(n, info) {
info.qOSTM = false;
plotTrack(info);
};
if (qOpenStMap)
if (osm)
menu['Plot OpenStMap'] = function() {
info.qOSTM = true;
plotTrack(info);
@ -161,49 +164,22 @@ function viewTrack(n, info) {
return E.showMenu(menu);
}
function drawopenstmap(lat, lon, map) {
var s = require("Storage");
var cx = g.getWidth()/2;
var cy = g.getHeight()/2;
var p = Bangle.project({lat:lat,lon:lon});
var ix = (p.x-map.center.x)*4096/map.scale + (map.imgx/2) - cx;
var iy = (map.center.y-p.y)*4096/map.scale + (map.imgy/2) - cy;
var tx = 0|(ix/map.tilesize);
var ty = 0|(iy/map.tilesize);
var ox = (tx*map.tilesize)-ix;
var oy = (ty*map.tilesize)-iy;
for (var x=ox,ttx=tx;x<g.getWidth();x+=map.tilesize,ttx++) {
for (var y=oy,tty=ty;y<g.getHeight();y+=map.tilesize,tty++) {
var img = s.read("openstmap-"+ttx+"-"+tty+".img");
if (img) g.drawImage(img,x,y);
else g.clearRect(x,y,x+map.tilesize-1,y+map.tilesize-1);
}
}
}
function plotTrack(info) {
"ram"
function radians(a) {
return a*Math.PI/180;
function distance(lat1,long1,lat2,long2) { "ram"
var x = (long1-long2) * Math.cos((lat1+lat2)*Math.PI/360);
var y = lat2 - lat1;
return Math.sqrt(x*x + y*y) * 6371000 * Math.PI / 180;
}
function distance(lat1,long1,lat2,long2){
var x = radians(long1-long2) * Math.cos(radians((lat1+lat2)/2));
var y = radians(lat2-lat1);
return Math.sqrt(x*x + y*y) * 6371000;
}
function getMapXY(mylat, mylon) {
if (info.qOSTM) {
var q = Bangle.project({lat:mylat,lon:mylon});
var p = Bangle.project({lat:clat,lon:clon});
var ix = (q.x-p.x)*4096/map.scale + cx;
var iy = cy - (q.y-p.y)*4096/map.scale;
return {x:ix, y:iy};
}
else {
var ix = 30 + Math.round((long-info.minLong)*info.lfactor*info.scale);
// Function to convert lat/lon to XY
var getMapXY;
if (info.qOSTM) {
getMapXY = osm.latLonToXY.bind(osm);
} else {
getMapXY = function(lat, lon) { "ram"
var ix = 30 + Math.round((long - info.minLong)*info.lfactor*info.scale);
var iy = 210 - Math.round((lat - info.minLat)*info.scale);
return {x:ix, y:iy};
}
@ -225,13 +201,10 @@ function plotTrack(info) {
g.setColor(1,1,1);
g.drawString("N",2,40);
g.setColor(1,1,1);
}
else {
var map = s.readJSON("openstmap.json");
map.center = Bangle.project({lat:map.lat,lon:map.lon});
var clat = (info.minLat+info.maxLat)/2;
var clon = (info.minLong+info.maxLong)/2;
drawopenstmap(clat, clon, map);
} else {
osm.lat = (info.minLat+info.maxLat)/2;
osm.lon = (info.minLong+info.maxLong)/2;
osm.draw();
g.setColor(0, 0, 0);
}
g.drawString(asTime(info.duration),10,220);
@ -258,7 +231,7 @@ function plotTrack(info) {
long = +c[2];
mp = getMapXY(lat, long);
g.lineTo(mp.x,mp.y);
if (info.qOSTM) g.fillCircle(mp.x, mp.y, 1);
if (info.qOSTM) g.fillCircle(mp.x,mp.y,2); // make the track more visible
var d = distance(olat,olong,lat,long);
if (!isNaN(d)) dist+=d;
olat = lat;

4
apps/gpsrec/settings.js Normal file
View File

@ -0,0 +1,4 @@
(function(back) {
// just go right to our app - we need all the memory
load("gpsrec.app.js");
})();

View File

@ -66,6 +66,24 @@
WIDGETS["gpsrec"]={area:"tl",width:24,draw:draw,reload:function() {
reload();
Bangle.drawWidgets(); // relayout all widgets
},plotTrack:function(m) { // m=instance of openstmap module
settings = require("Storage").readJSON("gpsrec.json",1)||{};
settings.file |= 0;
var n = settings.file.toString(36);
var f = require("Storage").open(".gpsrc"+n,"r");
var l = f.readLine(f);
if (l===undefined) return;
var c = l.split(",");
var mp = m.latLonToXY(+c[1], +c[2]);
g.moveTo(mp.x,mp.y);
l = f.readLine(f);
while(l!==undefined) {
c = l.split(",");
mp = m.latLonToXY(+c[1], +c[2]);
g.lineTo(mp.x,mp.y);
g.fillCircle(mp.x,mp.y,2); // make the track more visible
l = f.readLine(f);
}
}};
// load settings, set correct widget width
reload();

View File

@ -7,3 +7,4 @@
0.06: Remove translations if not required
Ensure 'on' is always supplied for translations
0.07: Improve handling of non-ASCII characters (fix #469)
0.08: Added Mavigation units and en_NAV

View File

@ -96,6 +96,8 @@ exports = { name : "en_GB", currencySym:"£",
// If we have a 2 char ISO country code, use it to get the unicode flag
if (localeParts[1] && localeParts[1].length==2)
icon = localeParts[1].toUpperCase().replace(/./g, char => String.fromCodePoint(char.charCodeAt(0)+127397) )+" ";
if (localeParts[1]=="NAV")
icon = "&#9973;&#9992;&#65039; ";
return `<option value="${l}">${icon}${l}</option>`
}).join("\n");

View File

@ -4,13 +4,15 @@ const distanceUnits = { // how many meters per X?
"yd": 0.9144,
"mi": 1609.34,
"km": 1000,
"kmi": 1000
"kmi": 1000,
"nm": 1852
};
const speedUnits = { // how many kph per X?
"kmh": 1,
"kph": 1,
"km/h": 1,
"mph": 1.60934
"mph": 1.60934,
"kts": 1.852
};
/*
@ -87,7 +89,7 @@ var locales = {
currency_symbol: "Rs.",
currency_first: true,
int_curr_symbol: "INR",
speed: 'kms',
speed: 'kmh',
distance: { "0": "m", "1": "km" },
temperature: '°C',
ampm: { 0: "am", 1: "pm" },
@ -99,6 +101,24 @@ var locales = {
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
// No translation for english...
},
"en_NAV": { // navigation units nautical miles and knots
lang: "en_NAV",
decimal_point: ".",
thousands_sep: ",",
currency_symbol: "£", currency_first: true,
int_curr_symbol: "GBP",
speed: 'kts',
distance: { "0": "m", "1": "nm" },
temperature: '°C',
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
datePattern: { 0: "%b %d %Y", 1: "%d/%m/%Y" }, // Feb 28 2020" // "01/03/2020"(short)
abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
month: "January,February,March,April,May,June,July,August,September,October,November,December",
abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
// No translation for english...
},
"de_DE": {
lang: "de_DE",
decimal_point: ",",

View File

@ -1,3 +1,5 @@
0.01: New App!
0.02: Fix marker position, color, and map scaling
0.03: Show widgets (mainly so we can use the GPS recorder widget)
0.04: Move map rendering to a module (fix #396)
0.05: Show currently active gpsrec GPS trace (fix #395)

View File

@ -1,62 +1,37 @@
var s = require("Storage");
var map = s.readJSON("openstmap.json");
var m = require("openstmap");
var HASWIDGETS = true;
var y1,y2;
map.center = Bangle.project({lat:map.lat,lon:map.lon});
var lat = map.lat, lon = map.lon;
var fix = {};
function redraw() {
var cx = g.getWidth()/2;
var cy = g.getHeight()/2;
var p = Bangle.project({lat:lat,lon:lon});
var ix = (p.x-map.center.x)*4096/map.scale + (map.imgx/2) - cx;
var iy = (map.center.y-p.y)*4096/map.scale + (map.imgy/2) - cy;
//console.log(ix,iy);
var tx = 0|(ix/map.tilesize);
var ty = 0|(iy/map.tilesize);
var ox = (tx*map.tilesize)-ix;
var oy = (ty*map.tilesize)-iy;
g.setClipRect(0,y1,g.getWidth()-1,y2);
for (var x=ox,ttx=tx;x<g.getWidth();x+=map.tilesize,ttx++) {
for (var y=oy,tty=ty;y<g.getHeight();y+=map.tilesize,tty++) {
var img = s.read("openstmap-"+ttx+"-"+tty+".img");
if (img) g.drawImage(img,x,y);
else {
g.clearRect(x,y,x+map.tilesize-1,y+map.tilesize-1);
g.drawLine(x,y,x+map.tilesize-1,y+map.tilesize-1);
g.drawLine(x,y+map.tilesize-1,x+map.tilesize-1,y);
}
}
}
m.draw();
drawMarker();
if (WIDGETS["gpsrec"] && WIDGETS["gpsrec"].plotTrack) {
g.setColor(0.75,0.2,0);
WIDGETS["gpsrec"].plotTrack(m);
}
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
}
function drawMarker() {
if (!fix.fix) return;
var p = Bangle.project({lat:lat,lon:lon});
var q = Bangle.project(fix);
var cx = g.getWidth()/2;
var cy = g.getHeight()/2;
var ix = (q.x-p.x)*4096/map.scale + cx;
var iy = cy - (q.y-p.y)*4096/map.scale;
var p = m.latLonToXY(fix.lat, fix.lon);
g.setColor(1,0,0);
g.fillRect(ix-2,iy-2,ix+2,iy+2);
g.fillRect(p.x-2, p.y-2, p.x+2, p.y+2);
}
var fix;
Bangle.on('GPS',function(f) {
fix=f;
g.clearRect(0,0,240,8);
g.clearRect(0,y1,240,y1+8);
g.setColor(1,1,1);
g.setFont("6x8");
g.setFontAlign(0,0);
var txt = fix.satellites+" satellites";
if (!fix.fix)
txt += " - NO FIX";
g.drawString(txt,120,4);
g.drawString(txt,120,y1 + 4);
drawMarker();
});
Bangle.setGPSPower(1);
@ -76,7 +51,7 @@ redraw();
setWatch(function() {
if (!fix.fix) return;
lat = fix.lat;
lon = fix.lon;
m.lat = fix.lat;
m.lon = fix.lon;
redraw();
}, BTN2, {repeat:true});

View File

@ -0,0 +1,66 @@
/* OpenStreetMap plotting module.
Usage:
var m = require("openstmap");
// m.lat/lon are now the center of the loaded map
m.draw(); // draw centered on the middle of the loaded map
// plot gps position on map
Bangle.on('GPS',function(f) {
if (!f.fix) return;
var p = m.latLonToXY(fix.lat, fix.lon);
g.fillRect(p.x-2, p.y-2, p.x+2, p.y+2);
});
// recenter and redraw map!
function center() {
m.lat = fix.lat;
m.lon = fix.lon;
m.draw();
}
*/
var map = require("Storage").readJSON("openstmap.json");
map.center = Bangle.project({lat:map.lat,lon:map.lon});
exports.map = map;
exports.lat = map.lat; // actual position of middle of screen
exports.lon = map.lon; // actual position of middle of screen
var m = exports;
exports.draw = function() {
var s = require("Storage");
var cx = g.getWidth()/2;
var cy = g.getHeight()/2;
var p = Bangle.project({lat:m.lat,lon:m.lon});
var ix = (p.x-map.center.x)*4096/map.scale + (map.imgx/2) - cx;
var iy = (map.center.y-p.y)*4096/map.scale + (map.imgy/2) - cy;
//console.log(ix,iy);
var tx = 0|(ix/map.tilesize);
var ty = 0|(iy/map.tilesize);
var ox = (tx*map.tilesize)-ix;
var oy = (ty*map.tilesize)-iy;
for (var x=ox,ttx=tx;x<g.getWidth();x+=map.tilesize,ttx++) {
for (var y=oy,tty=ty;y<g.getHeight();y+=map.tilesize,tty++) {
var img = s.read("openstmap-"+ttx+"-"+tty+".img");
if (img) g.drawImage(img,x,y);
else {
g.clearRect(x,y,x+map.tilesize-1,y+map.tilesize-1);
g.drawLine(x,y,x+map.tilesize-1,y+map.tilesize-1);
g.drawLine(x,y+map.tilesize-1,x+map.tilesize-1,y);
}
}
}
};
exports.latLonToXY = function(lat, lon) {
var p = Bangle.project({lat:m.lat,lon:m.lon});
var q = Bangle.project({lat:lat, lon:lon});
var cx = g.getWidth()/2;
var cy = g.getHeight()/2;
return {
x : (q.x-p.x)*4096/map.scale + cx,
y : cy - (q.y-p.y)*4096/map.scale
};
};

2
core

@ -1 +1 @@
Subproject commit 0389671ba6678a12c9f35644ffb96c190bbe0278
Subproject commit 8bfdeebf705ced95699dcbbccfa05a99e7d3f4a9

9
modules/README.md Normal file
View File

@ -0,0 +1,9 @@
App Modules
===========
These are modules used by apps - you can use them with:
```
var testmodule = require("testmodule");
testmodule.test()
```

3
modules/testmodule.js Normal file
View File

@ -0,0 +1,3 @@
exports.test = function() {
console.log("Hello world!");
};