mirror of https://github.com/espruino/BangleApps
Add files via upload
parent
3dc60b24a4
commit
2ea54d5863
|
@ -0,0 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Course marker
|
||||
0.03: Tilt compensation and calibration
|
||||
|
|
@ -1 +1,26 @@
|
|||
dummy
|
||||
# Navigation Compass
|
||||
|
||||
This is a tilt and roll compensated compass with a linear display. The compass will display the same direction that it shows when flat as when it is tilted (rotation around the W-S axis) or rolled (rotation around the N-S) axis. *Even with compensation, it would be beyond foolish to rely solely on this app for any serious navigational purpose.*
|
||||
|
||||

|
||||
|
||||
## 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`.
|
||||
|
||||
## Controls
|
||||
|
||||
*BTN1* - switches to your selected clock app.
|
||||
|
||||
*BTN2* - switches to the app launcher.
|
||||
|
||||
*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).
|
||||
|
||||
|
||||
## Support
|
||||
|
||||
Please report bugs etc. by raising an issue [here](https://github.com/jeffmer/JeffsBangleAppsDev).
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/ACMBiIACiAWQCoYADCyEimf/mczkQYOBwMj/4ABC4MzmQYMLYMvCwQXDDARjKFogXFDAQuJiQWEC4szkIwIIooXHGBAuHC4wwIFwXcp4XKGA8Rn//7vMAYIXImYXFIwc97nNDAQXHJAsBj4RB/owBDAQXHmIXERofz7vcDAQXHMApeCAAPdGAIYBC45gFUok9GAXM4hgIXpBgBGAQYIPAcBibTEC4IwC4fzPBIXGJAk/C5amCJAnM74VBVQwXKVIPMbAQXRMAIXEJAoXLnpdDC5Z3FMAPTB4LhCC6ApEC5bXEDAwwBDwjvDgAXHCQgwFC4kRKoQAFGAgXDiIXEl4XKSIkyC4ioHC4qsCOwh4KRYoFCkIXEMBIXEn5eGMBQXGLwpIKC4hGIGBIWFFw4wJFxwwCkYXJFxIwCJIoWFFxIwCGIgWEFxQYEkTRDkQWODAYAFCxxjDAARbLAH4AFA=="))
|
|
@ -0,0 +1,220 @@
|
|||
|
||||
const Yoff = 80;
|
||||
var pal2color = new Uint16Array([0x0000,0xffff,0x07ff,0xC618],0,2);
|
||||
var buf = Graphics.createArrayBuffer(240,50,2,{msb:true});
|
||||
Bangle.setLCDTimeout(30);
|
||||
|
||||
function flip(b,y) {
|
||||
g.drawImage({width:240,height:50,bpp:2,buffer:b.buffer, palette:pal2color},0,y);
|
||||
b.clear();
|
||||
}
|
||||
|
||||
const labels = ["N","NE","E","SE","S","SW","W","NW"];
|
||||
var brg=null;
|
||||
|
||||
function drawCompass(course) {
|
||||
buf.setColor(1);
|
||||
buf.setFont("Vector",16);
|
||||
var start = course-90;
|
||||
if (start<0) start+=360;
|
||||
buf.fillRect(28,45,212,49);
|
||||
var xpos = 30;
|
||||
var frag = 15 - start%15;
|
||||
if (frag<15) xpos+=frag; else frag = 0;
|
||||
for (var i=frag;i<=180-frag;i+=15){
|
||||
var res = start + i;
|
||||
if (res%90==0) {
|
||||
buf.drawString(labels[Math.floor(res/45)%8],xpos-8,0);
|
||||
buf.fillRect(xpos-2,25,xpos+2,45);
|
||||
} else if (res%45==0) {
|
||||
buf.drawString(labels[Math.floor(res/45)%8],xpos-12,0);
|
||||
buf.fillRect(xpos-2,30,xpos+2,45);
|
||||
} else if (res%15==0) {
|
||||
buf.fillRect(xpos,35,xpos+1,45);
|
||||
}
|
||||
xpos+=15;
|
||||
}
|
||||
if (brg) {
|
||||
var bpos = brg - course;
|
||||
if (bpos>180) bpos -=360;
|
||||
if (bpos<-180) bpos +=360;
|
||||
bpos+=120;
|
||||
if (bpos<30) bpos = 14;
|
||||
if (bpos>210) bpos = 226;
|
||||
buf.setColor(2);
|
||||
buf.fillCircle(bpos,40,8);
|
||||
}
|
||||
flip(buf,Yoff);
|
||||
}
|
||||
|
||||
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){
|
||||
var start = Date.now();
|
||||
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() {
|
||||
var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
|
||||
heading = newHeading(d,heading);
|
||||
drawCompass(heading);
|
||||
buf.setColor(1);
|
||||
buf.setFont("6x8",2);
|
||||
buf.setFontAlign(-1,-1);
|
||||
buf.drawString("o",170,0);
|
||||
buf.setFont("Vector",40);
|
||||
var course = Math.round(heading);
|
||||
var cs = course.toString();
|
||||
cs = course<10?"00"+cs : course<100 ?"0"+cs : cs;
|
||||
buf.drawString(cs,70,10);
|
||||
flip(buf,Yoff+80);
|
||||
}
|
||||
|
||||
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});
|
||||
},30000);
|
||||
});
|
||||
}
|
||||
|
||||
function docalibrate(e,first){
|
||||
const title = "Calibrate";
|
||||
const msg = "takes 30 seconds";
|
||||
function action(b){
|
||||
if (b) {
|
||||
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((r)=>{
|
||||
require("Storage").write("magnav.json",r);
|
||||
CALIBDATA = r;
|
||||
startdraw();
|
||||
setButtons();
|
||||
});
|
||||
} else {
|
||||
startdraw();
|
||||
setTimeout(setButtons,1000);
|
||||
}
|
||||
}
|
||||
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(){
|
||||
g.clear();
|
||||
g.setColor(1,0.5,0.5);
|
||||
g.fillPoly([120,Yoff+50,110,Yoff+70,130,Yoff+70]);
|
||||
g.setColor(1,1,1);
|
||||
Bangle.drawWidgets();
|
||||
candraw = true;
|
||||
intervalRef = setInterval(reading,200);
|
||||
}
|
||||
|
||||
function stopdraw() {
|
||||
candraw=false;
|
||||
if(intervalRef) {clearInterval(intervalRef);}
|
||||
}
|
||||
|
||||
function setButtons(){
|
||||
setWatch(()=>{load();}, BTN1, {repeat:false,edge:"falling"});
|
||||
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
|
||||
setWatch(docalibrate, BTN3, {repeat:false,edge:"falling"});
|
||||
}
|
||||
|
||||
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();
|
||||
} else {
|
||||
stopdraw();
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.setCompassPower(1);
|
||||
if (!CALIBDATA)
|
||||
docalibrate({},true);
|
||||
else {
|
||||
startdraw();
|
||||
setButtons();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
var Yoff=80,pal2color=new Uint16Array([0,65535,2047,50712],0,2),buf=Graphics.createArrayBuffer(240,50,2,{msb:!0});Bangle.setLCDTimeout(30);function flip(b,c){g.drawImage({width:240,height:50,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",16);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",40);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}})},3E4)})}
|
||||
function docalibrate(b,c){function a(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(a){require("Storage").write("magnav.json",a);CALIBDATA=a;startdraw();setButtons()})):(startdraw(),setTimeout(setButtons,1E3))}void 0===c&&(c=!1);stopdraw();clearWatch();c?E.showAlert("takes 30 seconds","Calibrate").then(a.bind(null,!0)):E.showPrompt("takes 30 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+50,110,Yoff+70,130,Yoff+70]);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);
|
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
Loading…
Reference in New Issue