BangleApps/apps/waypointer/app.js

301 lines
7.4 KiB
JavaScript

const scale = g.getWidth()/240;
var pal_by = new Uint16Array([g.getBgColor(),0xFFC0],0,1); // black, yellow
var pal_bw = new Uint16Array([g.getBgColor(),g.getColor()],0,1); // black, white
var pal_bb = new Uint16Array([g.getBgColor(),0x07ff],0,1); // black, blue
// having 3 2 color pallette keeps the memory requirement lower
var buf1 = Graphics.createArrayBuffer(160*scale,160*scale,1, {msb:true});
var buf2 = Graphics.createArrayBuffer(g.getWidth()/3,40*scale,1, {msb:true});
var arrow_img = require("heatshrink").decompress(atob("lEowIPMjAEDngEDvwED/4DCgP/wAEBgf/4AEBg//8AEBh//+AEBj///AEBn///gEBv///wmCAAImCAAIoBFggE/AkaaEABo="));
var settings = Object.assign({
// default values
smoothDirection: true,
}, require('Storage').readJSON("waypointer.json", true) || {});
function flip1(x,y) {
g.drawImage({width:160*scale,height:160*scale,bpp:1,buffer:buf1.buffer, palette:pal_by},x,y);
buf1.clear();
}
function flip2_bw(x,y) {
g.drawImage({width:g.getWidth()/3,height:40*scale,bpp:1,buffer:buf2.buffer, palette:pal_bw},x,y);
buf2.clear();
}
function flip2_bb(x,y) {
g.drawImage({width:g.getWidth()/3,height:40*scale,bpp:1,buffer:buf2.buffer, palette:pal_bb},x,y);
buf2.clear();
}
var candraw = true;
var wp_bearing = 0;
var direction = 0;
var wpindex=0;
var loc = require("locale");
var selected = false;
var previous = {
bs: '',
dst: '',
wp_name: '',
course: 0,
selected: false,
};
// clear the attributes that control the display refresh
function clear_previous() {
previous.bs = '-';
previous.dst = '-';
previous.wp_name = '-';
previous.course = -999;
}
function drawCompass(course) {
if(!candraw) return;
if (Math.abs(previous.course - course) < 9) return; // reduce number of draws due to compass jitter
previous.course = course;
buf1.setColor(1);
buf1.fillCircle(buf1.getWidth()/2,buf1.getHeight()/2,79*scale);
buf1.setColor(0);
buf1.fillCircle(buf1.getWidth()/2,buf1.getHeight()/2,69*scale);
buf1.setColor(1);
buf1.drawImage(arrow_img, buf1.getWidth()/2, buf1.getHeight()/2, {scale:3*scale, rotate:radians(course)} );
flip1(40*scale, Bangle.appRect.y+6*scale);
}
/***** COMPASS CODE ***********/
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;
if (settings.smoothDirection) {
hd = h + delta*(1 + Math.round(s/5));
} else {
hd = h + delta*s;
}
if (hd<0) hd+=360;
if (hd>360)hd-= 360;
return hd;
}
var CALIBDATA = require("Storage").readJSON("magnav.json",1) || {};
function tiltfixread(O,S){
var m = Bangle.getCompass();
if (O === undefined || S === undefined) {
// no valid calibration from magnav, use built in
return m.heading;
}
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 read_compass() {
var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
if (isNaN(d)) return; // built in compass heading can return NaN when uncalibrated
heading = newHeading(d,heading);
direction = wp_bearing - heading;
if (direction < 0) direction += 360;
if (direction > 360) direction -= 360;
drawCompass(direction);
}
/***** END Compass ***********/
var speed = 0;
var satellites = 0;
var wp;
var dist=0;
function radians(a) {
return a*Math.PI/180;
}
function degrees(a) {
var d = a*180/Math.PI;
return (d+360)%360;
}
function bearing(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 distance(a,b){
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);
}
function drawN(){
buf2.setFont("Vector",24*scale);
var bs = wp_bearing.toString();
bs = wp_bearing<10?"00"+bs : wp_bearing<100 ?"0"+bs : bs;
var dst = loc.distance(dist);
// -1=left (default), 0=center, 1=right
// show distance on the left
if (previous.dst !== dst) {
previous.dst = dst;
buf2.setColor(1);
buf2.setFontAlign(-1,-1);
buf2.setFont("Vector", 20*scale);
buf2.drawString(dst,0,0);
flip2_bw(0, g.getHeight()-40*scale);
}
// bearing, place in middle at bottom of compass
if (previous.bs !== bs) {
previous.bs = bs;
buf2.setColor(1);
buf2.setFontAlign(0, -1);
buf2.setFont("Vector",38*scale);
buf2.drawString(bs,40*scale,0);
flip2_bw(g.getWidth()/3, g.getHeight()-40*scale);
}
// waypoint name on right
if (previous.wp_name !== wp.name || previous.selected !== selected) {
previous.selected = selected;
buf2.setColor(1);
buf2.setFontAlign(1,-1); // right, bottom
buf2.setFont("Vector", 20*scale);
buf2.drawString(wp.name, 80*scale, 0);
if (selected)
flip2_bw(g.getWidth()/3*2, g.getHeight()-40*scale);
else
flip2_bb(g.getWidth()/3*2, g.getHeight()-40*scale);
}
}
var savedfix;
function onGPS(fix) {
savedfix = fix;
if (fix!==undefined){
satellites = fix.satellites;
}
if (candraw) {
if (fix!==undefined && fix.fix==1){
dist = distance(fix,wp);
if (isNaN(dist)) dist = 0;
wp_bearing = bearing(fix,wp);
if (isNaN(wp_bearing)) wp_bearing = 0;
drawN();
}
}
}
var intervalRef;
function stopdraw() {
candraw=false;
prev_course = -1;
if(intervalRef) {clearInterval(intervalRef);}
}
function startTimers() {
candraw=true;
intervalRefSec = setInterval(function() {
read_compass();
}, 500);
}
function drawAll(){
g.setColor(1,1,1);
drawN();
drawCompass(direction);
}
function startdraw(){
g.clear();
Bangle.drawWidgets();
startTimers();
candraw=true;
drawAll();
}
function setButtons(){
Bangle.setUI("updown", d=>{
if (d<0) { nextwp(-1); }
else if (d>0) { nextwp(1); }
else { doselect(); }
});
}
Bangle.on('lcdPower',function(on) {
if (on) {
clear_previous();
startdraw();
} else {
stopdraw();
}
});
var waypoints = require("waypoints").load();
wp=waypoints[0];
function nextwp(inc){
if (!selected) return;
wpindex+=inc;
if (wpindex>=waypoints.length) wpindex=0;
if (wpindex<0) wpindex = waypoints.length-1;
wp = waypoints[wpindex];
drawN();
}
function doselect(){
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("waypoints").save(waypoints);
}
selected=!selected;
drawN();
}
Bangle.on('kill',()=>{
Bangle.setCompassPower(0);
Bangle.setGPSPower(0);
});
g.clear();
Bangle.setLCDBrightness(1);
Bangle.loadWidgets();
Bangle.drawWidgets();
// load widgets can turn off GPS
Bangle.setGPSPower(1);
Bangle.setCompassPower(1);
drawAll();
startTimers();
Bangle.on('GPS', onGPS);
setButtons();