From 9f80ef122139189ab19ac750aba0bf593be0019d Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Mon, 12 May 2025 13:09:48 +0200 Subject: [PATCH] skyspy 0.11: Display glyph of GPS status This adds another screen with graphical representation of GPS status, that can be used to quickly estimate when fix will be available and if user needs to move to get it --- apps/skyspy/ChangeLog | 1 + apps/skyspy/metadata.json | 2 +- apps/skyspy/skyspy.app.js | 193 +++++++++++++++++++++++++------------- 3 files changed, 131 insertions(+), 65 deletions(-) diff --git a/apps/skyspy/ChangeLog b/apps/skyspy/ChangeLog index a67665086..01e4d6aa0 100644 --- a/apps/skyspy/ChangeLog +++ b/apps/skyspy/ChangeLog @@ -3,3 +3,4 @@ 0.03: big rewrite, adding time-adjust and altitude-adjust functionality 0.04: more refactoring, fix lat/lon display 0.10: add screens with time to fixes and estimate of time to fix +0.11: introduce graphical gps status diff --git a/apps/skyspy/metadata.json b/apps/skyspy/metadata.json index 628ce6a32..b19518392 100644 --- a/apps/skyspy/metadata.json +++ b/apps/skyspy/metadata.json @@ -1,6 +1,6 @@ { "id": "skyspy", "name": "Sky Spy", - "version": "0.10", + "version": "0.11", "description": "Application for debugging GPS problems", "icon": "app.png", "readme": "README.md", diff --git a/apps/skyspy/skyspy.app.js b/apps/skyspy/skyspy.app.js index 81dbbe696..80ddaaa5d 100644 --- a/apps/skyspy/skyspy.app.js +++ b/apps/skyspy/skyspy.app.js @@ -200,10 +200,16 @@ let gps = { }, }; -/* ui library 0.1.5 */ +/* ui library 0.1.5, dirty, bad, revert! */ let ui = { display: 0, numScreens: 2, + clear: function() { + g.reset() + .setColor(g.theme.bg) + .fillRect(0, this.wi, this.w, this.y2) + .setColor(g.theme.fg); + }, drawMsg: function(msg) { g.reset().setFont("Vector", 35) .setColor(g.theme.bg) @@ -296,11 +302,7 @@ let ui = { }, }; -function log2(x) { - return Math.log(x) / Math.log(2); -} - -/* pie library v0.0.1 */ +/* pie library v0.0.4 */ let pie = { radians: function(a) { return a*Math.PI/180; }, @@ -310,77 +312,123 @@ let pie = { points.push(centerX, centerY); // Start at the center // Step through angles to create points on the arc - for (let angle = startAngle; angle <= endAngle; angle += 5) { - const x = centerX + Math.cos(this.radians(angle)) * radius; - const y = centerY + Math.sin(this.radians(angle)) * radius; + for (let angle = startAngle; angle <= endAngle; angle += 15) { + const x = centerX + Math.sin(this.radians(angle)) * radius; + const y = centerY - Math.cos(this.radians(angle)) * radius; points.push(x, y); } // Add the final point at the end angle - points.push(centerX + Math.cos(this.radians(endAngle)) * radius); - points.push(centerY + Math.sin(this.radians(endAngle)) * radius); + points.push(centerX + Math.sin(this.radians(endAngle)) * radius); + points.push(centerY - Math.cos(this.radians(endAngle)) * radius); g.fillPoly(points); // Draw the arc as a polygon }, // Function to draw the pie chart drawPieChart1: function(g, centerX, centerY, radius, data, colors) { - let startAngle = 0; + let startAngle = data[0]; // Loop through the data to draw each segment - for (let i = 0; i < data.length; i++) { + for (let i = 1; i < data.length; i++) { const angle = data[i]; // Get the angle for the current section - const endAngle = startAngle + angle; // Calculate the end angle + const endAngle = angle; // Calculate the end angle g.setColor(colors[i]); // Set the fill color this.fillArc(g, centerX, centerY, radius, startAngle, endAngle, colors[i]); // Draw the arc - startAngle = endAngle; // Update the start angle for the next segment + startAngle = angle; // Update the start angle for the next segment } - - g.flip(); // Update the screen }, - altDelta: function(centerX, centerY, radius, altitude, altChange) { - // Altitude range and mapping to a logarithmic scale - //const altitudeMin = -1000, altitudeMax = 1000; - const altitudeLog = log2(Math.abs(altitude) + 1) * Math.sign(altitude); // Logarithmic scaling - const altitudeAngle = E.clip((altitudeLog - log2(1)) / (log2(1001) - log2(1)), -1, 1) * 180; - - // Altitude Change (linear scale) - const altChangeMin = -30, altChangeMax = 30; - const altChangeAngle = E.clip((altChange - altChangeMin) / (altChangeMax - altChangeMin), 0, 1) * 360; - - this.twoPie(centerX, centerY, radius, altitudeAngle, altChangeAngle); - }, - - twoPie: function(centerX, centerY, radius, altitudeAngle, altChangeAngle) { - // Outer Ring (Altitude Change) - Draw a segment based on altitude change - - g.setColor(0, 0, 0.5); // Set a color for the outer ring - this.fillArc(g, - centerX, centerY, - radius, // Define the thickness of the outer ring - 0, altChangeAngle // Draw based on altitude change - ); - - // Inner Ring (Altitude) - Draw a segment based on altitude angle - const innerRadius = radius * 0.6; // Inner ring size - g.setColor(0, 0.5, 0); // Set a color for the inner ring - this.fillArc(g, - centerX, centerY, - innerRadius, // Define thickness of inner ring - 0, altitudeAngle // Draw based on altitude - ); - - // Draw the baseline circle for reference - g.setColor(0, 0, 0); // Gray for baseline - g.drawCircle(centerX, centerY, innerRadius); // Inner circle (reference) - g.drawCircle(centerX, centerY, radius); // Outer circle (reference) - - // Render the chart - g.flip(); - } }; +let gpsg = { + cx: ui.w/2, + cy: ui.wi+ui.h/2, + s: ui.h/2 - 1, + sats: 4, /* Number of sats with good enough snr */ + sats_bad: 0, /* Sattelites visible but with low snr */ + view_t: getTime(), /* When sky became visible */ + start_t: getTime(), /* When we started acquiring fix */ + dalt: 30, /* Altitude error between barometer and gps */ + fix: {}, + + init : function() { + }, + drawCorner(h, v) { + let cx = this.cx; + let cy = this.cy; + let s = this.s; + let st = 48; + let a = [cx+h*s, cy+v*s, cx+h*s - h*st, cy+v*s, cx+h*s, cy+v*s - v*st]; + g.fillPoly(a); + }, + clamp: function(low, v, high) { + if (v < low) + v = low; + if (v > high) + v = high; + return [ low, v, high ]; + }, + draw : function() { + let cx = this.cx; + let cy = this.cy; + let s = this.s; + ui.clear(); + g.fillCircle(cx, cy, s); + if (!this.fix.fix) + this.drawCorner(1, -1); + if (this.fix.hdop > 10) + this.drawCorner(1, 1); + if (this.fix.satellites < 4) + this.drawCorner(-1, 1); + if (this.sats < 4) + this.drawCorner(-1, -1); + + g.setColor(1, 1, 1); + let t = getTime(); + if (this.fix.fix) { /* Have speed */ + let data = this.clamp(210, 210 + (360*this.fix.speed) / 20, 210+360); + let colors = [ "ign", "#000", "#fff" ]; + pie.drawPieChart1(g, cx, cy, s * 1, data, colors); + } else { + let data = this.clamp(0, (360*(t - this.start_t)) / 600, 360); + let colors = [ "ign", "#888", "#000" ]; + pie.drawPieChart1(g, cx, cy, s * 1, data, colors); + } + if (this.fix.fix) { + let data = this.clamp(90, 90 + (360*this.dalt) / 200, 90+360); + let colors = [ "ign", "#000", "#fff" ]; + pie.drawPieChart1(g, cx, cy, s * 0.6, data, colors); + } else { /* Still waiting for fix */ + let data = this.clamp(0, (360*(t - this.view_t)) / 120, 360); + let colors = [ "ign", "#888", "#000" ]; + pie.drawPieChart1(g, cx, cy, s * 0.6, data, colors); + } + if (this.fix.fix) { + let slice = 360 / 8; + let sats = this.fix.satellites; + let data = this.clamp( 0, slice * sats, 360 ); + let colors = [ "ign", "#fff", "#000" ]; + pie.drawPieChart1(g, cx, cy, s * 0.3, data, colors); + } else { + let slice = 360 / 8; + let sats = this.sats; + let red = sats + this.sats_bad; + if (sats > 8) + sats = 8; + if (red > 8) + red = 8; + let data = [ 0, slice * sats, slice * red, 360 ]; + let colors = [ "ign", "#888", "#800", "#000" ]; + pie.drawPieChart1(g, cx, cy, s * 0.3, data, colors); + } + }, +}; + +function log2(x) { + return Math.log(x) / Math.log(2); +} + var debug = 0; var cur_altitude; var adj_time = 0, adj_alt = 0; @@ -500,9 +548,17 @@ let quality = { let t = getTime(); //print(t, this.fix_start); msg = "St: " + fmt.fmtTimeDiff(t-gps.gps_start) + "\n"; - msg += "Sky: " + fmt.fmtTimeDiff(t-sky.all.sky_start) + "\n"; + msg += "Sky: " + fmt.fmtTimeDiff(t-skys.sky_start) + "\n"; msg += "2D: " + fmt.fmtTimeDiff(t-quality.fix_start) + "\n"; msg += "3D: " + fmt.fmtTimeDiff(t-quality.f3d_start) + "\n"; + } else if (ui.display == 5) { + gpsg.start_t = gps.gps_start; + gpsg.view_t = skys.sky_start; + gpsg.sats = skys.sats_used; + gpsg.sats_bad = skys.sats_weak; + gpsg.fix = fix; + gpsg.dalt = Math.abs(adelta); + gpsg.draw(); } quality.step++; if (quality.step == 10) { @@ -524,12 +580,18 @@ let skys = { sats: [], snum: 0, sats_used: 0, + sats_weak: 0, sky_start: -1, + /* 18.. don't get reliable fix in 40s */ + /* 25.. seems to be good limit for clear sky. ~60 seconds. + .. maybe better 26? */ + snrLim: 25, reset: function() { this.snum = 0; this.sats = []; this.sats_used = 0; + this.sats_weak = 0; }, parseSats: function(s) { let view = 1 * s[3]; @@ -542,6 +604,9 @@ let skys = { this.sats_used++; print(sat.id, sat.snr); } + if (sat.snr > 0) { + this.sats_weak++; + } this.sats[this.snum++] = sat; } }, @@ -619,7 +684,7 @@ let skys = { }, onEnd: function () { this.trackSatelliteVisibility(); - if (this.sats_used < 5) + if (this.sats_used < 4) this.sky_start = getTime(); this.reset(); }, @@ -647,7 +712,7 @@ let sky = { this_usable: 0, debug: 0, all: skys, /* Sattelites from all systems */ - split: 1, + split: 0, init: function () { if (this.split) { @@ -665,8 +730,6 @@ let sky = { ui.radCircle(1.0); }, - /* 18.. don't get reliable fix in 40s */ - snrLim: 22, drawSat: function(s) { let a = s.azi / 360; let e = ((90 - s.ele) / 90); @@ -675,7 +738,7 @@ let sky = { if (s.snr == 0) g.setColor(1, 0.25, 0.25); - else if (s.snr < this.snrLim) + else if (s.snr < this.all.snrLim) g.setColor(0.25, 0.5, 0.25); else g.setColor(0, 0, 0); @@ -885,18 +948,20 @@ function onMessage() { ui.init(); -ui.numScreens = 5; +ui.numScreens = 6; /* 0.. sat drawing 1.. position, basic data 2.. fix quality esitmation 3.. times from ... 4.. time to fix experiment + 5.. gps graph */ gps.init(); quality.resetAlt(); fmt.init(); sky.onMessageEnd = onMessage; sky.init(); +gpsg.init(); sky.decorate = () => { let p = 15;