jeffmer / JeffsBangleAppsDev

My development copy of Bangle Apps
MIT License
8 stars 2 forks source link

gpsnav takes a while to signal the direction, maybe a hybrid approach using the compass would be better #8

Open hughbarney opened 3 years ago

hughbarney commented 3 years ago

Hi Jeff, great work on the GPSNAV App. I checked it out tonight. I found it a little sluggish in terms of trying to figure out which direction to travel in. I had the GPS configured in SuperE power mode so should have been getting an update once per second from the GPS. The compass part is very slow to react and I was wondering if a hybrid approach would be better where the GPS tells the software the current position; you work out the bearing to the waypoint using current position lat and lon and then use the compass to show if you are going in the correct direction or not while you wait for the next fix from the GPS. I tried the your compass app out and found it much more responsive to movement. Really interested to know what you think.

jeffmer commented 3 years ago

Hi, Yes, the issue is that for the GPS to give out a reliable direction, you have to be moving. So it’s not the update rate that determines the response but the speed you are moving at. I have someone who uses the app in a plane and there it works well because the direction is reliable due to moving quickly. It also works well in a car. If you stop and try to decide which direction to move in, it does not work, as at that point the gps cannot tell which direction you are moving in. - can be frustrating. The direction with GPS is independent of the orientation of the watch which is clearly not the case with the magnetic compass. It would be fairly simple as you suggest to feed the gps generated bearing to the magnetic compass. The compass already has the facility for setting a bearing.

jeffmer commented 3 years ago

However, the problem with the compass is that you have to recalibrate it every time you charge the watch as the magnetic clamps changes the surrounding magnetic fields for the sensor. These then decay over time and you really need to calibrate every time you want accurate readings.

hughbarney commented 3 years ago

Thanks for the response Jeff.

I have managed to get a hybrid version working ! I have to confess I reused a lot of your code from magnav and gpsnav, which is a tribute to your code and development work and I will give you full credit for it if / when I get to doing a release for my app. My use case for the GPS is navigating while walking. I did an experiment and found that unless you walk really fast more often than no the course value from the GPS fix is just Nan. I then looked it up in the UBLOX datasheet and the course value is not accurate unless you are moving faster than 8m/s - thats 28kph. There is a low speed course setting for the GPS but that will be a whole lot more of experimenting to work out that.

I have noticed even when calibrating the magnetometer it often aligns more to 20degrees west. Real shame about the magnetic charging cable, really spoils the use case.

I have managed to navigate reasonably well using my hybrid app, but still testing.

On Wed, Feb 17, 2021 at 9:01 PM jeffmer notifications@github.com wrote:

However, the problem with the compass is that you have to recalibrate it every time you charge the phone as the magnetic clamps changes the surrounding magnetic fields for the sensor. These then decay over time and you really need to calibrate every time you want accurate readings.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/jeffmer/JeffsBangleAppsDev/issues/8#issuecomment-780850137, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA3WLELQRI4O4TPN4NXMWULS7QVBJANCNFSM4XY76KGQ .

jeffmer commented 3 years ago

Will be interested to see the hybrid app. My real problem with the app is - when I tried to use it on a boat last summer - the screen is not really readable in daylight. I have got versions of gpsnav and magnav working on an SMAQ3 with a reflective screen visible in bright sunlight. Hovever, the GPS antenna is not as good as the one on the Bangle and it gives wildly inaccurate readings at times. As you say, the compass is not very accurate at the best of times. BTW, I have versions of magnav and gpsnav that run as multiclock faces so that you can switch very quickly between these and the GPS stays on. They rely on you previously setting waypoints and calibration using the app versions.

Do you have a URL for the ublox darasheet?

hughbarney commented 3 years ago

Here's my latest code. I just replace gpsnav.js with the code below if you want to give a quick try. Be interested if you spot any obvious mistakes or improvements.

First calibrate the compass using magnav. I have two waypoints at the top and bottom of our back lane and can navigate backwards and forwards to them. The compass has to be used as a guide. Quite often it can be 20-30 degress off.

I would be interested to know what the code below does and how it works ?

var SCREENACCESS = { withApp:true, request:function(){ this.withApp=false; stopdraw(); clearWatch(); }, release:function(){ this.withApp=true; startdraw(); setButtons(); } }

I am not sure what is going in newHeading() and tilFixRead() is rocket science- I will take your word for it !

On My Code - One problem that I have noticed is that the buttons can become unresponsive after a few minutes and take a while to respond. It seems to be when the GPS is on that this occurs. Not sure I have seen the same issue on your app.

/////////// LATEST CODE IN PROGRESS

var pal1color = new Uint16Array([0x0000,0xFFC0],0,1); var pal2color = new Uint16Array([0x0000,0xffff,0x07ff,0xC618],0,2);

var buf1 = Graphics.createArrayBuffer(160,160,1,{msb:true}); var buf2 = Graphics.createArrayBuffer(80,40,2,{msb:true});

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:2,buffer:buf2.buffer, palette:pal2color},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 things that control the display function clear_previous() { previous.bs = '-'; previous.dst = '-'; previous.wp_name = '-'; };

function drawCompass(course) { if(!candraw) return; if (Math.abs(previous.course - course) < 2) return; previous.course = course;

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); }

/* 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 = h + delta*(1 + Math.round(s/5)); if (hd<0) hd+=360; if (hd>360)hd-= 360; return hd; }

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.xsinphi-g.zcosphi)); var costheta = Math.cos(theta), sintheta = Math.sin(theta); var xh = m.dycostheta + m.dxsinphisintheta + m.dzcosphisintheta; var yh = m.dzsinphi - m.dxcosphi; 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() { //if (savedfix === undefined || !savedfix.fix) return; var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale); 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(xx + yy) 6371000); }

function drawN(){ buf2.setFont("Vector",24); 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", 24); buf2.drawString(dst,0,0); flip2(0, 200); }

// 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); buf2.drawString(bs,40,0); flip2(80, 200); }

// waypoint name on right if (previous.wp_name !== wp.name || previous.selected !== selected) { previous.selected = selected; buf2.setColor(selected?1:2); buf2.setFontAlign(1,-1); // right, bottom buf2.setFont("Vector", 20); buf2.drawString(wp.name, 80, 0); flip2(160, 200); } }

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(); }, 400); }

function drawAll(){ g.setColor(1,1,1); drawN(); drawCompass(direction); }

function startdraw(){ g.clear(); Bangle.drawWidgets(); startTimers(); candraw=true; drawAll(); }

function setButtons(){ 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"}); }

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 { clear_previous(); stopdraw(); } });

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 && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) { waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon}; wp = waypoints[wpindex]; require("Storage").writeJSON("waypoints.json", 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();

On Mon, Feb 22, 2021 at 8:38 PM jeffmer notifications@github.com wrote:

Will be interested to see the hybrid app. My real problem with the app is

  • when I tried to use it on a boat last summer - the screen is not really readable in daylight. I have got versions of gpsnav and magnav working on an SMAQ3 with a reflective screen visible in bright sunlight. Hovever, the GPS antenna is not as good as the one on the Bangle and it gives wildly inaccurate readings at times. As you say, the compass is not very accurate at the best of times. BTW, I have versions of magnav and gpsnav that run as multiclock faces so that you can switch very quickly between these and the GPS stays on. They rely on you previously setting waypoints and calibration using the app versions.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/jeffmer/JeffsBangleAppsDev/issues/8#issuecomment-783659021, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA3WLENSPUL3C4JURRWMTF3TAK6CTANCNFSM4XY76KGQ .

jeffmer commented 3 years ago

Thanks for the code. The SCREENACCESS code is related to my ANCS widget - see http://forum.espruino.com/comments/15251491/. It means a widget can safely grab the screen and controls and then hand them back to the app. From what I remember, new heading is doing the smooth animation so that the compass does not jerk between headings but displays some intermediate steps. I think that gpsnav is sometimes slow to respond to buttons if there is a lot of animation going on.