1
0
Fork 0

added Moving Arrow Compass

master
hughbarney 2021-02-23 23:54:52 +00:00
parent 5186c0d87c
commit aa02f99164
4 changed files with 218 additions and 0 deletions

37
apps/arrow/README.md Normal file
View File

@ -0,0 +1,37 @@
# Moving Arrow Compass
A variation of jeffmer's Navigation Compass. The compass points
North and shows the current heading.
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.*
![](screenshot.jpg)
## 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`.
It is also worth noting that the presence of the magnetic charging
clamps will require the compass to be recalibrated after every
charge.
## Controls
*BTN1* - switches to your selected clock app.
*BTN2* - switches to the app launcher.
*BTN3* - invokes calibration ( can be cancelled if pressed accidentally)

180
apps/arrow/app.js Normal file
View File

@ -0,0 +1,180 @@
var pal1color = new Uint16Array([0x0000,0xFFC0],0,1);
var pal2color = new Uint16Array([0x0000,0xffff],0,1);
var buf1 = Graphics.createArrayBuffer(160,160,1,{msb:true});
var buf2 = Graphics.createArrayBuffer(80,40,1,{msb:true});
var bearing=0; // always point north
var heading = 0;
var candraw = false;
var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null;
Bangle.setLCDTimeout(30);
function flip1(x,y) {
g.drawImage({width:160,height:160,bpp:1,buffer:buf1.buffer, palette:pal1color},x,y);
buf1.clear();
}
function flip2(x,y) {
g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal2color},x,y);
buf2.clear();
}
function radians(d) {
return (d*Math.PI) / 180;
}
function drawCompass(course) {
if(!candraw) return;
var img = require("heatshrink").decompress(atob("lEowIPMjAEDngEDvwED/4DCgP/wAEBgf/4AEBg//8AEBh//+AEBj///AEBn///gEBv///wmCAAImCAAIoBFggE/AkaaEABo="));
buf1.setColor(1);
buf1.fillCircle(80,80,79,79);
buf1.setColor(0);
buf1.fillCircle(80,80,69,69);
buf1.setColor(1);
buf1.drawImage(img, 80, 80, {scale:3, rotate:radians(course)} );
flip1(40, 30);
}
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;
}
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);
var dir = bearing - heading;
if (dir < 0) dir += 360;
if (dir > 360) dir -= 360;
drawCompass(dir); // we want compass to show us where to go
buf2.setColor(1);
buf2.setFontAlign(-1,-1);
buf2.setFont("Vector",38);
var course = Math.round(heading);
var cs = course.toString();
cs = course<10?"00"+cs : course<100 ?"0"+cs : cs;
buf2.drawString(cs,0,0);
flip2(90, 200);
}
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) {
buf1.setColor(1);
buf1.setFont("Vector", 30);
buf1.setFontAlign(0,-1);
buf1.drawString("Figure 8s",80, 40);
buf1.drawString("to",80, 80);
buf1.drawString("Calibrate",80, 120);
flip1(40,40);
calibrate().then((r)=>{
require("Storage").write("magnav.json",r);
Bangle.buzz();
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);
}
var intervalRef;
function startdraw(){
g.clear();
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"});
}
Bangle.on('lcdPower',function(on) {
if (on) {
startdraw();
} else {
stopdraw();
}
});
Bangle.on('kill',()=>{Bangle.setCompassPower(0);});
Bangle.loadWidgets();
Bangle.setCompassPower(1);
startdraw();
setButtons();

BIN
apps/arrow/arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

1
apps/arrow/icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mUywIebg/4AocP//AAoUf//+BYgMDh/+j/8Dol/wEAgYFBg/wgEBFIV+AQIVCh4fBnwFBgISBj8AhgJCh+Ag4BB4ED8ED+ASCAYJDBnkAvkAIYIWBjw8B/EB8AcBn//gF4DwJdBAQMA/EP738FYM8g/nz+A+EPgHx8YKBgfAjF4sAKBHIItBBQJMBFoJEBHII1BIQIDCvAUCAYYUBHIIDBMIXACgQpBRAIUBMIIrBDAIWCVYaiBTYQJCn4FBQgIIBEYKrDQ4MBVYUf8CQCCoP/w6DBAAKIBAocHAoIwBBgb5DDoYAZA="))