update multiclock + initial B2 magnav

pull/915/head
jeffmer 2021-11-19 15:08:45 +00:00
parent 100384f2e8
commit 2fe779d02b
11 changed files with 241 additions and 64 deletions

View File

@ -2639,14 +2639,15 @@
{
"id": "magnav",
"name": "Navigation Compass",
"version": "0.04",
"version": "0.05",
"description": "Compass with linear display as for GPSNAV. Has Tilt compensation and remembers calibration.",
"icon": "magnav.png",
"tags": "tool,outdoors",
"supports": ["BANGLEJS"],
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"magnav.app.js","url":"magnav.min.js"},
{"name":"magnav.app.js","url":"magnav-b1.js","supports":"BANGLEJS"},
{"name":"magnav.app.js","url":"magnav-b2.js","supports":"BANGLEJS2"},
{"name":"magnav.img","url":"magnav-icon.js","evaluate":true}
],
"data": [{"name":"magnav.json"}]
@ -2730,7 +2731,7 @@
{
"id": "multiclock",
"name": "Multi Clock",
"version": "0.08",
"version": "0.09",
"description": "Clock with multiple faces. Switch between faces with BTN1 & BTN3 or swipe left-right. For best display set theme Background 2 to cyan or some other bright colour in settings.",
"icon": "multiclock.png",
"type": "clock",

View File

@ -2,5 +2,6 @@
0.02: Course marker
0.03: Tilt compensation and calibration
0.04: Fix Font size
0.05: Inital portable version

View File

@ -6,19 +6,17 @@ This is a tilt and roll compensated compass with a linear display. The compass w
## Calibration
Correct operation of this app depends critically on calibration. When first run on a Bangle, the app will request calibration. This lasts for 30 seconds during which you should move the watch slowly through figures of 8. It is important that during calibration the watch is fully rotated around each of it axes. If the app does give the correct direction heading or is not stable with respect to tilt and roll - redo the calibration by pressing *BTN3*. Calibration data is recorded in a storage file named `magnav.json`.
Correct operation of this app depends critically on calibration. When first run on a Bangle, the app will request calibration. This lasts for 20 seconds during which you should move the watch slowly through figures of 8. It is important that during calibration the watch is fully rotated around each of it axes. If the app does give the correct direction heading or is not stable with respect to tilt and roll - redo the calibration by pressing *BTN2*. Calibration data is recorded in a storage file named `magnav.json`.
Note: Charging your Bangle due to the magnetic connector clamp seems to require recalibration afterwards for accurate readings.
## Controls
*BTN1* - switches to your selected clock app.
*BTN1* - marks the current heading with a blue circle - see screen shot. This can be used to take a bearing and then follow it..
*BTN2* - switches to the app launcher.
*BTN2* - invokes calibration ( can be cancelled if pressed accidentally).
*BTN3* - invokes calibration ( can be cancelled if pressed accidentally)
*Touch Left* - marks the current heading with a blue circle - see screen shot. This can be used to take a bearing and then follow it.
*Touch Right* - cancels the marker (blue circle not displayed).
*BTN3* - cancels the marker (blue circle not displayed)
## Support

View File

@ -1,10 +0,0 @@
var Yoff=80,pal2color=new Uint16Array([0,65535,2047,50712],0,2),buf=Graphics.createArrayBuffer(240,60,2,{msb:!0});Bangle.setLCDTimeout(30);function flip(b,c){g.drawImage({width:240,height:60,bpp:2,buffer:b.buffer,palette:pal2color},0,c);b.clear()}var labels="N NE E SE S SW W NW".split(" "),brg=null;
function drawCompass(b){buf.setColor(1);buf.setFont("Vector",24);var c=b-90;0>c&&(c+=360);buf.fillRect(28,45,212,49);var a=30,d=15-c%15;15>d?a+=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],a-8,0),buf.fillRect(a-2,25,a+2,45)):0==f%45?(buf.drawString(labels[Math.floor(f/45)%8],a-12,0),buf.fillRect(a-2,30,a+2,45)):0==f%15&&buf.fillRect(a,35,a+1,45);a+=15}brg&&(b=brg-b,180<b&&(b-=360),-180>b&&(b+=360),b+=120,30>b&&(b=14),210<b&&(b=226),buf.setColor(2),
buf.fillCircle(b,40,8));flip(buf,Yoff)}var heading=0;function newHeading(b,c){var a=Math.abs(b-c),d=b>c?1:-1;180<=a&&(a=360-a,d=-d);if(2>a)return c;a=c+d*(1+Math.round(a/5));0>a&&(a+=360);360<a&&(a-=360);return a}var candraw=!1,CALIBDATA=require("Storage").readJSON("magnav.json",1)||null;
function tiltfixread(b,c){Date.now();var a=Bangle.getCompass(),d=Bangle.getAccel();a.dx=(a.x-b.x)*c.x;a.dy=(a.y-b.y)*c.y;a.dz=(a.z-b.z)*c.z;var e=Math.atan(-d.x/-d.z),f=Math.cos(e);e=Math.sin(e);d=Math.atan(-d.y/(-d.x*e-d.z*f));var k=Math.sin(d);a=180*Math.atan2(a.dz*e-a.dx*f,a.dy*Math.cos(d)+a.dx*e*k+a.dz*f*k)/Math.PI;0>a&&(a+=360);return a}
function reading(){var b=tiltfixread(CALIBDATA.offset,CALIBDATA.scale);heading=newHeading(b,heading);drawCompass(heading);buf.setColor(1);buf.setFont("6x8",2);buf.setFontAlign(-1,-1);buf.drawString("o",170,0);buf.setFont("Vector",54);b=Math.round(heading);var c=b.toString();buf.drawString(10>b?"00"+c:100>b?"0"+c:c,70,10);flip(buf,Yoff+80)}
function calibrate(){var b=-32E3,c=-32E3,a=-32E3,d=32E3,e=32E3,f=32E3,k=setInterval(function(){var h=Bangle.getCompass();b=h.x>b?h.x:b;c=h.y>c?h.y:c;a=h.z>a?h.z:a;d=h.x<d?h.x:d;e=h.y<e?h.y:e;f=h.z<f?h.z:f},100);return new Promise(function(h){setTimeout(function(){k&&clearInterval(k);var m=(b-d)/2,n=(c-e)/2,p=(a-f)/2,l=(m+n+p)/3;h({offset:{x:(b+d)/2,y:(c+e)/2,z:(a+f)/2},scale:{x:l/m,y:l/n,z:l/p}})},1E4)})}
function docalibrate(b,c){function a(d){d?(buf.setColor(1),buf.setFont("Vector",24),buf.setFontAlign(0,-1),buf.drawString("Fig 8s to",120,0),buf.drawString("Calibrate",120,26),flip(buf,Yoff),calibrate().then(function(e){require("Storage").write("magnav.json",e);CALIBDATA=e;startdraw();setButtons()})):(startdraw(),setTimeout(setButtons,1E3))}void 0===c&&(c=!1);stopdraw();clearWatch();c?E.showAlert("takes 10 seconds","Calibrate").then(a.bind(null,!0)):E.showPrompt("takes 10 seconds",{title:"Calibrate",
buttons:{Start:!0,Cancel:!1}}).then(a)}Bangle.on("touch",function(b){candraw&&(1==b&&(brg=heading),2==b&&(brg=null))});var intervalRef;function startdraw(){g.clear();g.setColor(1,.5,.5);g.fillPoly([120,Yoff+60,110,Yoff+80,130,Yoff+80]);g.setColor(1,1,1);Bangle.drawWidgets();candraw=!0;intervalRef=setInterval(reading,200)}function stopdraw(){candraw=!1;intervalRef&&clearInterval(intervalRef)}
function setButtons(){setWatch(function(){load()},BTN1,{repeat:!1,edge:"falling"});setWatch(Bangle.showLauncher,BTN2,{repeat:!1,edge:"falling"});setWatch(docalibrate,BTN3,{repeat:!1,edge:"falling"})}var SCREENACCESS={withApp:!0,request:function(){this.withApp=!1;stopdraw();clearWatch()},release:function(){this.withApp=!0;startdraw();setButtons()}};Bangle.on("lcdPower",function(b){SCREENACCESS.withApp&&(b?startdraw():stopdraw())});Bangle.on("kill",function(){Bangle.setCompassPower(0)});Bangle.loadWidgets();
Bangle.setCompassPower(1);CALIBDATA?(startdraw(),setButtons()):docalibrate({},!0);

View File

@ -1,4 +1,3 @@
const Yoff = 80;
var pal2color = new Uint16Array([0x0000,0xffff,0x07ff,0xC618],0,2);
var buf = Graphics.createArrayBuffer(240,60,2,{msb:true});
@ -13,6 +12,7 @@ const labels = ["N","NE","E","SE","S","SW","W","NW"];
var brg=null;
function drawCompass(course) {
"ram"
buf.setColor(1);
buf.setFont("Vector",24);
var start = course-90;
@ -63,7 +63,7 @@ var candraw = false;
var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null;
function tiltfixread(O,S){
var start = Date.now();
"ram"
var m = Bangle.getCompass();
var g = Bangle.getAccel();
m.dx =(m.x-O.x)*S.x; m.dy=(m.y-O.y)*S.y; m.dz=(m.z-O.z)*S.z;
@ -117,13 +117,20 @@ function calibrate(){
var avg = (delta.x+delta.y+delta.z)/3;
var scale = {x:avg/delta.x, y:avg/delta.y, z:avg/delta.z};
resolve({offset:offset,scale:scale});
},30000);
},20000);
});
}
function docalibrate(e,first){
var calibrating=false;
function docalibrate(first){
calibrating=true;
const title = "Calibrate";
const msg = "takes 30 seconds";
const msg = "takes 20 seconds";
function restart() {
calibrating=false;
setButtons();
startdraw();
}
function action(b){
if (b) {
buf.setColor(1);
@ -134,30 +141,20 @@ function docalibrate(e,first){
flip(buf,Yoff);
calibrate().then((r)=>{
require("Storage").write("magnav.json",r);
CALIBDATA = r;
startdraw();
setButtons();
restart()
});
} else {
startdraw();
setTimeout(setButtons,1000);
}
restart()
}
}
if (first===undefined) first=false;
stopdraw();
clearWatch();
if (first)
E.showAlert(msg,title).then(action.bind(null,true));
else
E.showPrompt(msg,{title:title,buttons:{"Start":true,"Cancel":false}}).then(action);
}
Bangle.on('touch', function(b) {
if(!candraw) return;
if(b==1) brg=heading;
if(b==2) brg=null;
});
var intervalRef;
function startdraw(){
@ -176,29 +173,17 @@ function stopdraw() {
}
function setButtons(){
setWatch(()=>{load();}, BTN1, {repeat:false,edge:"falling"});
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
setWatch(docalibrate, BTN3, {repeat:false,edge:"falling"});
function actions(v){
if (!v) docalibrate(false);
else if (v==1) brg=null;
else brg=heading;
}
Bangle.setUI("updown",actions);
}
var SCREENACCESS = {
withApp:true,
request:function(){
this.withApp=false;
stopdraw();
clearWatch();
},
release:function(){
this.withApp=true;
startdraw();
setButtons();
}
};
Bangle.on('lcdPower',function(on) {
if (!SCREENACCESS.withApp) return;
if (on) {
startdraw();
if (!calibrating) startdraw();
} else {
stopdraw();
}
@ -209,7 +194,7 @@ Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
Bangle.loadWidgets();
Bangle.setCompassPower(1);
if (!CALIBDATA)
docalibrate({},true);
docalibrate(true);
else {
startdraw();
setButtons();
@ -217,4 +202,3 @@ else {

9
apps/magnav/magnav_b1.min.js vendored Normal file
View File

@ -0,0 +1,9 @@
var Yoff=80,pal2color=new Uint16Array([0,65535,2047,50712],0,2),buf=Graphics.createArrayBuffer(240,60,2,{msb:!0});Bangle.setLCDTimeout(30);function flip(b,c){g.drawImage({width:240,height:60,bpp:2,buffer:b.buffer,palette:pal2color},0,c);b.clear()}var labels="N NE E SE S SW W NW".split(" "),brg=null;
function drawCompass(b){"ram";buf.setColor(1);buf.setFont("Vector",24);var c=b-90;0>c&&(c+=360);buf.fillRect(28,45,212,49);var a=30,d=15-c%15;15>d?a+=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],a-8,0),buf.fillRect(a-2,25,a+2,45)):0==f%45?(buf.drawString(labels[Math.floor(f/45)%8],a-12,0),buf.fillRect(a-2,30,a+2,45)):0==f%15&&buf.fillRect(a,35,a+1,45);a+=15}brg&&(b=brg-b,180<b&&(b-=360),-180>b&&(b+=360),b+=120,30>b&&(b=14),210<b&&(b=226),buf.setColor(2),
buf.fillCircle(b,40,8));flip(buf,Yoff)}var heading=0;function newHeading(b,c){var a=Math.abs(b-c),d=b>c?1:-1;180<=a&&(a=360-a,d=-d);if(2>a)return c;a=c+d*(1+Math.round(a/5));0>a&&(a+=360);360<a&&(a-=360);return a}var candraw=!1,CALIBDATA=require("Storage").readJSON("magnav.json",1)||null;
function tiltfixread(b,c){"ram";var a=Bangle.getCompass(),d=Bangle.getAccel();a.dx=(a.x-b.x)*c.x;a.dy=(a.y-b.y)*c.y;a.dz=(a.z-b.z)*c.z;var e=Math.atan(-d.x/-d.z),f=Math.cos(e);e=Math.sin(e);d=Math.atan(-d.y/(-d.x*e-d.z*f));var k=Math.sin(d);a=180*Math.atan2(a.dz*e-a.dx*f,a.dy*Math.cos(d)+a.dx*e*k+a.dz*f*k)/Math.PI;0>a&&(a+=360);return a}
function reading(){var b=tiltfixread(CALIBDATA.offset,CALIBDATA.scale);heading=newHeading(b,heading);drawCompass(heading);buf.setColor(1);buf.setFont("6x8",2);buf.setFontAlign(-1,-1);buf.drawString("o",170,0);buf.setFont("Vector",54);b=Math.round(heading);var c=b.toString();buf.drawString(10>b?"00"+c:100>b?"0"+c:c,70,10);flip(buf,Yoff+80)}
function calibrate(){var b=-32E3,c=-32E3,a=-32E3,d=32E3,e=32E3,f=32E3,k=setInterval(function(){var h=Bangle.getCompass();b=h.x>b?h.x:b;c=h.y>c?h.y:c;a=h.z>a?h.z:a;d=h.x<d?h.x:d;e=h.y<e?h.y:e;f=h.z<f?h.z:f},100);return new Promise(function(h){setTimeout(function(){k&&clearInterval(k);var m=(b-d)/2,n=(c-e)/2,p=(a-f)/2,l=(m+n+p)/3;h({offset:{x:(b+d)/2,y:(c+e)/2,z:(a+f)/2},scale:{x:l/m,y:l/n,z:l/p}})},2E4)})}var calibrating=!1;
function docalibrate(b){function c(a){a?(buf.setColor(1),buf.setFont("Vector",24),buf.setFontAlign(0,-1),buf.drawString("Fig 8s to",120,0),buf.drawString("Calibrate",120,26),flip(buf,Yoff),calibrate().then(function(d){require("Storage").write("magnav.json",d);calibrating=!1;setButtons();startdraw()})):(calibrating=!1,setButtons(),startdraw())}calibrating=!0;void 0===b&&(b=!1);stopdraw();b?E.showAlert("takes 20 seconds","Calibrate").then(c.bind(null,!0)):E.showPrompt("takes 20 seconds",{title:"Calibrate",
buttons:{Start:!0,Cancel:!1}}).then(c)}var intervalRef;function startdraw(){g.clear();g.setColor(1,.5,.5);g.fillPoly([120,Yoff+60,110,Yoff+80,130,Yoff+80]);g.setColor(1,1,1);Bangle.drawWidgets();candraw=!0;intervalRef=setInterval(reading,200)}function stopdraw(){candraw=!1;intervalRef&&clearInterval(intervalRef)}function setButtons(){Bangle.setUI("updown",function(b){b?brg=1==b?null:heading:docalibrate(!1)})}Bangle.on("lcdPower",function(b){b?calibrating||startdraw():stopdraw()});
Bangle.on("kill",function(){Bangle.setCompassPower(0)});Bangle.loadWidgets();Bangle.setCompassPower(1);CALIBDATA?(startdraw(),setButtons()):docalibrate(!0);

191
apps/magnav/magnav_b2.js Normal file
View File

@ -0,0 +1,191 @@
const Ypos = 40;
const labels = ["N","NE","E","SE","S","SW","W","NW"];
var brg=null;
function drawCompass(course) {
"ram"
g.setColor(g.theme.fg);
g.setFont("Vector",18);
var start = course-90;
if (start<0) start+=360;
g.fillRect(16,Ypos+45,160,Ypos+49);
var xpos = 16;
var frag = 15 - start%15;
if (frag<15) xpos+=Math.floor((frag*4)/5); else frag = 0;
for (var i=frag;i<=180-frag;i+=15){
var res = start + i;
if (res%90==0) {
g.drawString(labels[Math.floor(res/45)%8],xpos-6,Ypos+6);
g.fillRect(xpos-2,Ypos+25,xpos+2,Ypos+45);
} else if (res%45==0) {
g.drawString(labels[Math.floor(res/45)%8],xpos-9,Ypos+6);
g.fillRect(xpos-2,Ypos+30,xpos+2,Ypos+45);
} else if (res%15==0) {
g.fillRect(xpos,Ypos+35,xpos+1,Ypos+45);
}
xpos+=12;
}
if (brg) {
var bpos = brg - course;
if (bpos>180) bpos -=360;
if (bpos<-180) bpos +=360;
bpos= Math.floor((bpos*4)/5)+88;
if (bpos<16) bpos = 8;
if (bpos>160) bpos = 170;
g.setColor(g.theme.fg2);
g.fillCircle(bpos,Ypos+45,6);
}
}
var heading = 0;
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 candraw = false;
var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null;
function tiltfixread(O,S){
"ram"
var m = Bangle.getCompass();
var g = Bangle.getAccel();
m.dx =(m.x-O.x)*S.x; m.dy=(m.y-O.y)*S.y; m.dz=(m.z-O.z)*S.z;
var d = Math.atan2(-m.dx,m.dy)*180/Math.PI;
if (d<0) d+=360;
var phi = Math.atan(-g.x/-g.z);
var cosphi = Math.cos(phi), sinphi = Math.sin(phi);
var theta = Math.atan(-g.y/(-g.x*sinphi-g.z*cosphi));
var costheta = Math.cos(theta), sintheta = Math.sin(theta);
var xh = m.dy*costheta + m.dx*sinphi*sintheta + m.dz*cosphi*sintheta;
var yh = m.dz*sinphi - m.dx*cosphi;
var psi = Math.atan2(yh,xh)*180/Math.PI;
if (psi<0) psi+=360;
return psi;
}
// Note actual mag is 360-m, error in firmware
function reading() {
"ram"
g.clearRect(0,24,175,175);
var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
heading = newHeading(d,heading);
drawCompass(heading);
g.setColor(g.theme.fg);
g.setFont("6x8",2);
g.setFontAlign(-1,-1);
g.drawString("o",120,Ypos+80);
g.setFont("Vector",40);
var course = Math.round(heading);
var cs = course.toString();
cs = course<10?"00"+cs : course<100 ?"0"+cs : cs;
g.drawString(cs,50,Ypos+90);
g.setColor(g.theme.fg2);
g.fillPoly([88,Ypos+60,78,Ypos+80,98,Ypos+80]);
g.setColor(g.theme.fg);
g.flip();
}
function calibrate(){
var max={x:-32000, y:-32000, z:-32000},
min={x:32000, y:32000, z:32000};
var ref = setInterval(()=>{
var m = Bangle.getCompass();
max.x = m.x>max.x?m.x:max.x;
max.y = m.y>max.y?m.y:max.y;
max.z = m.z>max.z?m.z:max.z;
min.x = m.x<min.x?m.x:min.x;
min.y = m.y<min.y?m.y:min.y;
min.z = m.z<min.z?m.z:min.z;
}, 100);
return new Promise((resolve) => {
setTimeout(()=>{
if(ref) clearInterval(ref);
var offset = {x:(max.x+min.x)/2,y:(max.y+min.y)/2,z:(max.z+min.z)/2};
var delta = {x:(max.x-min.x)/2,y:(max.y-min.y)/2,z:(max.z-min.z)/2};
var avg = (delta.x+delta.y+delta.z)/3;
var scale = {x:avg/delta.x, y:avg/delta.y, z:avg/delta.z};
resolve({offset:offset,scale:scale});
},20000);
});
}
var calibrating=false;
function docalibrate(first){
calibrating=true;
const title = "Calibrate";
const msg = "takes 20 seconds";
function restart() {
calibrating=false;
setButtons();
startdraw();
}
function action(b){
if (b) {
g.clearRect(0,24,175,175);
g.setColor(g.theme.fg);
g.setFont("Vector",18);
g.setFontAlign(0,-1);
g.drawString("Fig 8s to",88,Ypos);
g.drawString("Calibrate",88,Ypos+18);
g.flip();
calibrate().then((r)=>{
require("Storage").write("magnav.json",r);
restart();
});
} else {
restart();
}
}
if (first===undefined) first=false;
stopdraw();
if (first)
E.showAlert(msg,title).then(action.bind(null,true));
else
E.showPrompt(msg,{title:title,buttons:{"Start":true,"Cancel":false}}).then(action);
}
var intervalRef;
function startdraw(){
g.clear(1);
Bangle.drawWidgets();
candraw = true;
intervalRef = setInterval(reading,200);
}
function stopdraw() {
candraw=false;
if(intervalRef) {clearInterval(intervalRef);}
}
function setButtons(){
function actions(v){
if (!v) docalibrate(false);
else if (v==1) brg=null;
else brg=heading;
}
Bangle.setUI("updown",actions);
}
Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
Bangle.loadWidgets();
Bangle.setCompassPower(1);
if (!CALIBDATA)
docalibrate(true);
else {
startdraw();
setButtons();
}

View File

@ -6,6 +6,7 @@
0.06: add minute tick for efficiency and nifty A clock
0.07: compatible with Bang;e.js 2
0.08: fix minute tick bug
0.09: use setUI clockupdown for controls + fix small display bug in nifty face

View File

@ -5,7 +5,9 @@ This is a clock app that supports multiple clock faces. The user can switch betw
## Controls
Swipe left and right on both the Bangle and Bangle 2 switch between faces. BTN1 & BTH3 also switch faces on the Bangle.
Uses `setUI("clockupdown")`
BTN1 & BTH3 switch faces on the Bangle.
Touch upper right and lower right quadrant switch faces on the Bangle 2.
## Adding a new face
Clock faces are described in javascript storage files named `name.face.js`. For example, the Analog Clock Face is described in `ana.face.js`. These files have the following structure:

View File

@ -67,7 +67,7 @@ function setButtons(){
startdraw();
}
}
Bangle.setUI("leftright", newFace);
Bangle.setUI("clockupdown", newFace);
}
E.on('kill',()=>{

View File

@ -28,7 +28,7 @@
var now = new Date();
const hour = d02(now.getHours() - (is12Hour && now.getHours() > 12 ? 12 : 0));
const minutes = d02(now.getMinutes());
const day = d02(now.getDay());
const day = d02(now.getDate());
const month = d02(now.getMonth() + 1);
const year = now.getFullYear();
const month2 = locale.month(now, 3);