Add files via upload

pull/322/head
jeffmer 2020-04-18 14:39:36 +01:00 committed by GitHub
parent 37e26d6139
commit ed51586dd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 314 additions and 0 deletions

66
apps/gpsnav/README.md Normal file
View File

@ -0,0 +1,66 @@
## gpsnav - navigate to waypoints
The app is aimed at small boat navigation although it can also be used to mark the location of your car, bicycle etc and then get directions back to it. Please note that it would be foolish in the extreme to rely on this as your only boat navigation aid!
The app displays direction of travel (course), speed, direction to waypoint (bearing) and distance to waypoint. The screen shot below is before the app has got a GPS fix.
![](first_screen.jpg)
The large digits are the course and speed. The top of the display is a linear compass which displays the direction of travel when a fix is received and you are moving. The blue text is the name of the current waypoint. NONE means that there is no waypoint set and so bearing and distance will remain at 0. To select a waypoint, press BTN2 (middle) and wait for the blue text to turn white. Then use BTN1 and BTN3 to select a waypoint. The waypoint choice is fixed by pressing BTN2 again. In the screen shot below a waypoint giving the location of Stone Henge has been selected.
![](waypoint_screen.jpg)
The display shows that Stone Henge is 108.75Km from the location where I made the screenshot and the direction is 255 degrees - approximately west. The display shows that I am currently moving approximately north - albeit slowly!. The position of the blue circle indicates that I need to turn left to get on course to Stone Henge. When the circle and red triangle line up you are on course and course will equal bearing.
### Marking Waypoints
The app lets you mark your current location as follows. There are vacant slots in the waypoint file which can be allocated a location. In the distributed waypoint file these are labelled WP0 to WP4. Select one of these - WP2 is shown below.
![](select_screen.jpg)
Bearing and distance are both zero as WP1 has currently no GPS location associated with it. To mark the location, press BTN2.
![](marked_screen.jpg)
The app indicates that WP2 is now marked by adding the prefix @ to it's name. The distance should be small as shown in the screen shot as you have just marked your current location.
### Waypoint JSON file
When the app is loaded from the app loader, a file named waypoints.json is loaded along with the javascript etc. The file has the following contents:
~~~
[
{
"mark":0,
"name":"NONE"
},
{
"mark":1,
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"mark":1,
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]
~~~
The file contains the initial NONE waypoint which is useful if you just want to display course and speed. The next two entries are waypoints to No 10 Downing Street and to Stone Henge - obtained from Google Maps. The last five entries are entries which can be *marked*.
You add and delete entries using the Web IDE to load and then save the file from and to watch storage. The app itself does not limit the number of entries although it does load the entire file into RAM which will obviously limit this.
I plan to release an accompanying watch app to edit waypoint files in the near future and a way to download your own waypoint file using the app loader.

1
apps/gpsnav/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwhC/AFmACysDC9+IC6szC/8AgUgLwYXBPAgLDAA8kC5MyC5cyogXHmYiDURMkDAMzC4JgBmcyoAXMGANCC4YDBkgXMHwVEC4hQDC5kyF4kjJ4QAMOgMjC4eCohGNMARbCC4ODkilLAAQSBCYJ3EmYVLhAWCCgQaCAAUwCpowCFwYADIRAYHC4wZFRQIAGnAhJXgwAFxAYHwC9JFwiQCFhIZISAQwDX5sCoQTCDYUjUpAAFglElAXDmS9JAAtEoUyC4ckkbvMC4QQBC4YeBC5sEB4IXEkgfBJBkEH4QXCCYMkoQXMHwcIC4ZQCUpYMDC4oiBC5YEDC40AkCRNAAIXBCJ4X2URgAJhAXvCyoA/ACoA="))

224
apps/gpsnav/app.js Normal file
View File

@ -0,0 +1,224 @@
const Yoff = 40;
var pal2color = new Uint16Array([0x0000,0xffff,0x07ff,0xC618],0,2);
var buf = Graphics.createArrayBuffer(240,50,2,{msb:true});
function flip(b,y) {
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"];
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 (wpindex!=0) {
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);
}
//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;
}
var course =0;
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);
}
var selected = false;
function drawN(){
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);
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);
flip(buf,Yoff+70);
buf.setColor(1);
buf.setFont("Vector",20);
var bs = brg.toString();
bs = brg<10?"00"+bs : brg<100 ?"0"+bs : bs;
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(bs,60,0);
if (dist<1000)
buf.drawString(dist.toString()+"m",60,30);
else
buf.drawString((dist/1000).toFixed(2)+"Km",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(fix) {
savedfix = fix;
if (fix!==undefined){
course = isNaN(fix.course) ? course : Math.round(fix.course);
speed = isNaN(fix.speed) ? speed : fix.speed;
satellites = fix.satellites;
}
if (Bangle.isLCDOn()) {
if (fix!==undefined && fix.fix==1){
dist = distance(fix,wp);
if (isNaN(dist)) dist = 0;
brg = bearing(fix,wp);
if (isNaN(brg)) brg = 0;
}
drawN();
}
}
var intervalRef;
function clearTimers() {
if(intervalRef) {clearInterval(intervalRef);}
}
function startTimers() {
intervalRefSec = setInterval(function() {
newHeading(course,heading);
if (course!=heading) drawCompass(heading);
},200);
}
Bangle.on('lcdPower',function(on) {
if (on) {
g.clear();
Bangle.drawWidgets();
startTimers();
drawAll();
}else {
clearTimers();
}
});
function drawAll(){
g.setColor(1,0.5,0.5);
g.fillPoly([120,Yoff+50,110,Yoff+70,130,Yoff+70]);
g.setColor(1,1,1);
drawN();
drawCompass(heading);
}
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
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 && waypoints[wpindex].mark===undefined && savedfix.fix) {
waypoints[wpindex] ={mark:1, 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();
// load widgets can turn off GPS
Bangle.setGPSPower(1);
drawAll();
startTimers();
Bangle.on('GPS', onGPS);
// Toggle selected
setWatch(nextwp.bind(null,-1), BTN1, {repeat:true,edge:"falling"});
setWatch(doselect, BTN2, {repeat:true,edge:"falling"});
setWatch(nextwp.bind(null,1), BTN3, {repeat:true,edge:"falling"});

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
apps/gpsnav/gpsnav.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
apps/gpsnav/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -0,0 +1,23 @@
[
{
"mark":0,
"name":"NONE"
},
{
"mark":1,
"name":"No10",
"lat":51.5032,
"lon":-0.1269
},
{
"mark":1,
"name":"Stone",
"lat":51.1788,
"lon":-1.8260
},
{ "name":"WP0" },
{ "name":"WP1" },
{ "name":"WP2" },
{ "name":"WP3" },
{ "name":"WP4" }
]