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
master
Pavel Machek 2025-05-12 13:09:48 +02:00
parent 6cc628f173
commit 9f80ef1221
3 changed files with 131 additions and 65 deletions

View File

@ -3,3 +3,4 @@
0.03: big rewrite, adding time-adjust and altitude-adjust functionality 0.03: big rewrite, adding time-adjust and altitude-adjust functionality
0.04: more refactoring, fix lat/lon display 0.04: more refactoring, fix lat/lon display
0.10: add screens with time to fixes and estimate of time to fix 0.10: add screens with time to fixes and estimate of time to fix
0.11: introduce graphical gps status

View File

@ -1,6 +1,6 @@
{ "id": "skyspy", { "id": "skyspy",
"name": "Sky Spy", "name": "Sky Spy",
"version": "0.10", "version": "0.11",
"description": "Application for debugging GPS problems", "description": "Application for debugging GPS problems",
"icon": "app.png", "icon": "app.png",
"readme": "README.md", "readme": "README.md",

View File

@ -200,10 +200,16 @@ let gps = {
}, },
}; };
/* ui library 0.1.5 */ /* ui library 0.1.5, dirty, bad, revert! */
let ui = { let ui = {
display: 0, display: 0,
numScreens: 2, 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) { drawMsg: function(msg) {
g.reset().setFont("Vector", 35) g.reset().setFont("Vector", 35)
.setColor(g.theme.bg) .setColor(g.theme.bg)
@ -296,11 +302,7 @@ let ui = {
}, },
}; };
function log2(x) { /* pie library v0.0.4 */
return Math.log(x) / Math.log(2);
}
/* pie library v0.0.1 */
let pie = { let pie = {
radians: function(a) { return a*Math.PI/180; }, radians: function(a) { return a*Math.PI/180; },
@ -310,77 +312,123 @@ let pie = {
points.push(centerX, centerY); // Start at the center points.push(centerX, centerY); // Start at the center
// Step through angles to create points on the arc // Step through angles to create points on the arc
for (let angle = startAngle; angle <= endAngle; angle += 5) { for (let angle = startAngle; angle <= endAngle; angle += 15) {
const x = centerX + Math.cos(this.radians(angle)) * radius; const x = centerX + Math.sin(this.radians(angle)) * radius;
const y = centerY + Math.sin(this.radians(angle)) * radius; const y = centerY - Math.cos(this.radians(angle)) * radius;
points.push(x, y); points.push(x, y);
} }
// Add the final point at the end angle // Add the final point at the end angle
points.push(centerX + Math.cos(this.radians(endAngle)) * radius); points.push(centerX + Math.sin(this.radians(endAngle)) * radius);
points.push(centerY + Math.sin(this.radians(endAngle)) * radius); points.push(centerY - Math.cos(this.radians(endAngle)) * radius);
g.fillPoly(points); // Draw the arc as a polygon g.fillPoly(points); // Draw the arc as a polygon
}, },
// Function to draw the pie chart // Function to draw the pie chart
drawPieChart1: function(g, centerX, centerY, radius, data, colors) { drawPieChart1: function(g, centerX, centerY, radius, data, colors) {
let startAngle = 0; let startAngle = data[0];
// Loop through the data to draw each segment // 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 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 g.setColor(colors[i]); // Set the fill color
this.fillArc(g, centerX, centerY, radius, startAngle, endAngle, colors[i]); // Draw the arc 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 debug = 0;
var cur_altitude; var cur_altitude;
var adj_time = 0, adj_alt = 0; var adj_time = 0, adj_alt = 0;
@ -500,9 +548,17 @@ let quality = {
let t = getTime(); let t = getTime();
//print(t, this.fix_start); //print(t, this.fix_start);
msg = "St: " + fmt.fmtTimeDiff(t-gps.gps_start) + "\n"; 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 += "2D: " + fmt.fmtTimeDiff(t-quality.fix_start) + "\n";
msg += "3D: " + fmt.fmtTimeDiff(t-quality.f3d_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++; quality.step++;
if (quality.step == 10) { if (quality.step == 10) {
@ -524,12 +580,18 @@ let skys = {
sats: [], sats: [],
snum: 0, snum: 0,
sats_used: 0, sats_used: 0,
sats_weak: 0,
sky_start: -1, 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() { reset: function() {
this.snum = 0; this.snum = 0;
this.sats = []; this.sats = [];
this.sats_used = 0; this.sats_used = 0;
this.sats_weak = 0;
}, },
parseSats: function(s) { parseSats: function(s) {
let view = 1 * s[3]; let view = 1 * s[3];
@ -542,6 +604,9 @@ let skys = {
this.sats_used++; this.sats_used++;
print(sat.id, sat.snr); print(sat.id, sat.snr);
} }
if (sat.snr > 0) {
this.sats_weak++;
}
this.sats[this.snum++] = sat; this.sats[this.snum++] = sat;
} }
}, },
@ -619,7 +684,7 @@ let skys = {
}, },
onEnd: function () { onEnd: function () {
this.trackSatelliteVisibility(); this.trackSatelliteVisibility();
if (this.sats_used < 5) if (this.sats_used < 4)
this.sky_start = getTime(); this.sky_start = getTime();
this.reset(); this.reset();
}, },
@ -647,7 +712,7 @@ let sky = {
this_usable: 0, this_usable: 0,
debug: 0, debug: 0,
all: skys, /* Sattelites from all systems */ all: skys, /* Sattelites from all systems */
split: 1, split: 0,
init: function () { init: function () {
if (this.split) { if (this.split) {
@ -665,8 +730,6 @@ let sky = {
ui.radCircle(1.0); ui.radCircle(1.0);
}, },
/* 18.. don't get reliable fix in 40s */
snrLim: 22,
drawSat: function(s) { drawSat: function(s) {
let a = s.azi / 360; let a = s.azi / 360;
let e = ((90 - s.ele) / 90); let e = ((90 - s.ele) / 90);
@ -675,7 +738,7 @@ let sky = {
if (s.snr == 0) if (s.snr == 0)
g.setColor(1, 0.25, 0.25); 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); g.setColor(0.25, 0.5, 0.25);
else else
g.setColor(0, 0, 0); g.setColor(0, 0, 0);
@ -885,18 +948,20 @@ function onMessage() {
ui.init(); ui.init();
ui.numScreens = 5; ui.numScreens = 6;
/* 0.. sat drawing /* 0.. sat drawing
1.. position, basic data 1.. position, basic data
2.. fix quality esitmation 2.. fix quality esitmation
3.. times from ... 3.. times from ...
4.. time to fix experiment 4.. time to fix experiment
5.. gps graph
*/ */
gps.init(); gps.init();
quality.resetAlt(); quality.resetAlt();
fmt.init(); fmt.init();
sky.onMessageEnd = onMessage; sky.onMessageEnd = onMessage;
sky.init(); sky.init();
gpsg.init();
sky.decorate = () => { sky.decorate = () => {
let p = 15; let p = 15;