1
0
Fork 0

Merge pull request #3713 from pavelmachek/m_44_spacer

spacer: version 0.10
master
Rob Pilling 2025-01-12 15:04:41 +00:00 committed by GitHub
commit 133532ef8c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 287 additions and 116 deletions

View File

@ -1 +1,2 @@
0.01: attempt to import
0.10: major updates, display satellites, display per-system satellite info

View File

@ -4,5 +4,17 @@ Compare GPS with Baido and Glonass
This turns GNSS receiver into mode with all three systems enabled, and
displays debug info from all of them. Click into top left corner to
switch navigation systems.
switch navigation systems. Clicks in bottom half of screen switch info
pages.
GNSS acquisition has few phases, and this software is assuming you are
not doing a cold start. GNSS fix needs 4 or 5 satellites with
reasonable signal strength, and then holding the same place for 40 or
so seconds.
Uxx -- satellites are known but not being received
S1..S4 -- not enough satellites being decoded (5 needed)
XXdB -- this is strength of 5th strongest satellite

View File

@ -1,2 +1,2 @@
require("heatshrink").decompress(atob("mEwgIQNgQFEj/gAof+jgECgeAAIIFBgwCBuACBhgCEjAOEAoQ6CmAhCDwItDoEB4AFCsEBFgUEkEDG4XEJYcL8gFCgUP+gxCAoP8DIIFBhfsiEIAoMJAogCBAoYlBiBMBAoUwrA0B////ALECI0QAocgAolgApVADolAHYnAAomAAoqdBAoKVBMoRvCOIQDCRIIFBYwKVBAoKqC4AFBVQVggTRDn0CYgQcBN4LpDV4T7IAooAJA="))
require("heatshrink").decompress(atob("mEwhHXAH4A/AH4A/AGMAF34v/F34EBAAIvrFwQxnEoIsFGEyNHF9ZgNGrheMF4guJHDAvLEhCdbLyLLdL5ImFfRo6SLpjGOCgw5TdZRhKHwhEPFxjOJCwwOGF6zOJBxpjFAB4TKR6aEJDpgwKd5rzUYBrkVMxwtVF7CYHeBxGRF5LwMR9YvdPiJfjBhYv/F/4v/F/4v/F/4v/F/4v/F/4vfAH4Ad"))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,6 +1,6 @@
{ "id": "spacer",
"name": "Space Race",
"version": "0.01",
"version": "0.10",
"description": "Compare GPS with Baido and Glonass",
"icon": "app.png",
"readme": "README.md",

View File

@ -1,20 +1,6 @@
/* Space race */
/*
Performance Assessment of GNSS Signals in
terms of Time to First Fix for
Cold, Warm and Hot Start
Matteo Paonni, Marco Anghileri, Stefan Wallner, José-Ángel Ávila-Rodríguez, Bernd Eissfeller
Institute of Geodesy and Navigation, University FAF Munich, Germany
=> 22 to 26 dB -- long time / no fix / ...
=> 26db + -- basically strength no longer matters
apps/assistedgps/custom.html
https://github.com/espruino/EspruinoDocs/blob/master/info/Bangle.js2%20Technical.md#gps
/*
gsa mi rika 2d/3d fix, a taky pdop/vdop/hdop
CFG-NAVX z CASIC_en -- umoznuje nastavit chodec / auto / letadlo
@ -68,12 +54,12 @@ let ui = {
touchHandler: function(d) {
let x = Math.floor(d.x);
let y = Math.floor(d.y);
if (d.b != 1 || this.last_b != 0) {
this.last_b = d.b;
return;
}
print("touch", x, y, this.h, this.w);
if ((x<this.w/2) && (y<this.y2/2))
@ -122,62 +108,22 @@ let ui = {
},
};
/* sky library v0.2.3
needs ui */
let fix = {}; /* Global for sky library */
/* sky library v0.1.0 */
let sky = {
let skys = {
sats: [],
snum: 0,
sats_used: 0,
sky_start: -1,
this_usable: 0,
drawGrid: function() {
g.setColor(0,0,0);
ui.radLine(0, 1, 0.5, 1);
ui.radLine(0.25, 1, 0.75, 1);
ui.radCircle(0.5);
ui.radCircle(1.0);
reset: function() {
this.snum = 0;
this.sats = [];
this.sats_used = 0;
},
snrLim: 28,
drawSat: function(s) {
let a = s.azi / 360;
let e = ((90 - s.ele) / 90);
let x = ui.radX(a, e);
let y = ui.radY(a, e);
if (s.snr == 0)
g.setColor(1, 0.25, 0.25);
else if (s.snr < this.snrLim)
g.setColor(0.25, 0.5, 0.25);
else
g.setColor(0, 0, 0);
g.drawString(s.id, x, y);
},
// Should correspond to view from below.
// https://in-the-sky.org//satmap_radar.php?year=2023&month=10&day=24&skin=1
decorate: function() {},
drawSats: function(sats) {
if (ui.display != 0)
return;
g.reset()
.setColor(1, 1, 1)
.fillRect(0, ui.wi, ui.w, ui.y2)
.setFont("Vector", 20)
.setFontAlign(0, 0);
this.drawGrid();
sats.forEach(s => this.drawSat(s));
if (fix && fix.fix && fix.lat) {
g.setColor(0, 0, 0)
.setFontAlign(-1, 1);
g.drawString(fix.satellites + "/" + fix.hdop, 5, ui.y2);
}
this.decorate();
},
parseSats: function(s) {
let view = 1 * s[3];
let k = Math.min(4, view - this.snum);
@ -193,24 +139,186 @@ let sky = {
}
},
snrSort: function() {
return this.sats.slice(0, this.snum).sort((a, b) => b.snr - a.snr);
},
getSatSNR: function(n) { /* Get n-th strongest sat */
if (n <= 0 || n > this.sats.length)
return -1;
// Sort the satellites by snr in descending order
let sortedSats = this.snrSort();
// Return the SNR of the n-th strongest satellite
return sortedSats[n - 1].snr;
},
qualest: function() {
// Sort the satellites by snr in descending order
let sortedSats = this.snrSort();
if (sortedSats[4] && sortedSats[4].snr) {
return "" + sortedSats[4].snr + "dB";
}
for (let i=4; i>=0; i--) {
if (sortedSats[i] && sortedSats[i].snr)
return "S" + (i+1);
}
return "U" + this.snum;
},
satVisibility: [],
trackSatelliteVisibility: function() {
const threshold = this.snrLim; // SNR threshold
const now = getTime();
let newVisibility = [];
//this.satVisibility = [];
for (let i = 0; i < this.snum; i++) {
let sat = this.sats[i];
let existingSat = this.satVisibility[sat.id];
if (sat.snr >= threshold) {
if (!existingSat) {
// New satellite starts visibility
newVisibility[sat.id] = { start: now, visible: true };
} else
newVisibility[sat.id] = this.satVisibility[sat.id];
}
}
this.satVisibility = newVisibility;
},
getnthLowestStartTimeSat: function(n) {
// Collect all satellites from visibility
let satellites = Object.values(this.satVisibility);
// Ensure we have at least 5 satellites
if (satellites.length < n)
return -1;
// Sort satellites by start time in ascending order
satellites.sort((a, b) => a.start - b.start);
// Return the satellite with the 5th lowest start time
return satellites[n-1]; // 0-based index, so 5th is index 4
},
goodest: function () {
let s = this.getnthLowestStartTimeSat(5);
if (s==-1)
return "";
let t = getTime() - s.start;
return "" + t + "s";
},
summary: function () {
let s = this.goodest();
if (s != "")
return s;
return this.qualest();
},
onEnd: function () {
this.trackSatelliteVisibility();
if (this.sats_used < 5)
this.sky_start = getTime();
this.reset();
},
};
function deepCopy(obj) {
if (obj === null || typeof obj !== "object") {
return obj; // Return primitive values as-is
}
if (Array.isArray(obj)) {
return obj.map(deepCopy); // Handle arrays recursively
}
const copy = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]); // Recursively copy properties
}
}
return copy;
}
let sky = {
this_usable: 0,
debug: 0,
all: skys, /* Sattelites from all systems */
split: 1,
init: function () {
if (this.split) {
this.s_gp = deepCopy(skys);
this.s_gl = deepCopy(skys);
this.s_bd = deepCopy(skys);
}
},
drawGrid: function() {
g.setColor(0,0,0);
ui.radLine(0, 1, 0.5, 1);
ui.radLine(0.25, 1, 0.75, 1);
ui.radCircle(0.5);
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);
let x = ui.radX(a, e);
let y = ui.radY(a, e);
if (s.snr == 0)
g.setColor(1, 0.25, 0.25);
else if (s.snr < this.snrLim)
g.setColor(0.25, 0.5, 0.25);
else
g.setColor(0, 0, 0);
g.drawString(s.id, x, y);
},
// Should correspond to view from below.
// https://in-the-sky.org//satmap_radar.php?year=2023&month=10&day=24&skin=1
decorate: function() {},
drawSats: function(sats) {
g.reset()
.setColor(1, 1, 1)
.fillRect(0, ui.wi, ui.w, ui.y2)
.setFont("Vector", 20)
.setFontAlign(0, 0);
this.drawGrid();
sats.forEach(s => this.drawSat(s));
if (fix && fix.fix && fix.lat) {
g.setColor(0, 0, 0)
.setFontAlign(-1, 1);
g.drawString(fix.satellites + "/" + fix.hdop, 5, ui.y2);
}
this.decorate();
},
old_msg: {},
msg: {},
tof: function(v) { let i = (1*v); return i.toFixed(0); },
fmtSys: function(sys) {
if (sys && sys.sent !== undefined && sys.d23 !== undefined)
return sys.sent + "." + sys.d23 + "D "+ this.tof(sys.pdop) + " " + this.tof(sys.vdop) + "\n";
else
return "(no data)\n";
tof: function(v, n) { let i = (1*v); return i.toFixed(n); },
tof0: function(v) { return this.tof(v, 0); },
tof1: function(v) { return this.tof(v, 1); },
fmtSys: function(sys, sats) {
if (!sys.sent)
return " off\n";
let r = sys.sent + " ";
// r+= sys.d23 + "D ";
if (sats)
// r += sats.sats_used + "/" + sats.snum;
r += sats.summary();
return r + "\n";
},
display: function() {
if (ui.display != 1)
return;
drawRace: function() {
let m = this.old_msg;
let msg = "" + this.tof(m.time) + "\n" +
"q" + m.quality + " " + m.in_view + " " + m.hdop + "\n" +
"gp"+ this.fmtSys(m.gp) +
"bd" + this.fmtSys(m.bd) +
"gl" + this.fmtSys(m.gl);
let msg = "gmt" + this.tof0(m.time) + "\n" +
"q" + m.quality + " S" + m.in_view + " h" + this.tof0(m.hdop) + "m\n" +
/* "v" + this.tof0(m.vdop) + "m " + "p" + this.tof0(m.pdop) + "m\n" + */
this.all.summary() + "\n" +
"gp"+ this.fmtSys(m.gp, this.s_gp) +
"bd" + this.fmtSys(m.bd, this.s_bd) +
"gl" + this.fmtSys(m.gl, this.s_gl);
if (this.msg.finished != 1)
msg += "!";
g.reset().clear().setFont("Vector", 30)
@ -218,37 +326,68 @@ let sky = {
.setFontAlign(-1, -1)
.drawString(msg, 0, 0);
},
drawEstimates: function() {
/*
Performance Assessment of GNSS Signals in terms of Time to
First Fix for Cold, Warm and Hot Start Matteo Paonni, Marco Anghileri,
Stefan Wallner, José-Ángel Ávila-Rodríguez, Bernd Eissfeller Institute
of Geodesy and Navigation, University FAF Munich, Germany
=> 22 to 26 dB -- long time / no fix / ...
=> 26db + -- basically strength no longer matters
*/
let r = this.all.qualest();
let r1 = this.all.goodest();
print(r, r1, this.old_msg.hdop, this.old_msg.quality);
ui.drawMsg(r + "\n" + r1 + "\n" + this.old_msg.hdop + "-" + this.old_msg.quality + "d\n" + (getTime() - this.all.sky_start));
},
onMessageEnd: function() {},
messageEnd: function() {
this.old_msg = this.msg;
this.msg = {};
this.msg.gp = {};
this.msg.bd = {};
this.msg.gl = {};
this.onMessageEnd();
//print(this.sats);
this.all.onEnd();
if (this.split) {
this.s_gp.onEnd();
this.s_gl.onEnd();
this.s_bd.onEnd();
}
},
parseRaw: function(msg, lost) {
//print(msg);
if (lost) print("## data lost");
let s = msg.split(",");
// print(getTime(), s[0]);
//return;
let cmd = s[0].slice(3);
//print("cmd", cmd);
if (cmd === "RMC") {
/* Repeat of position/speed/course */
this.messageEnd();
return;
}
if (cmd === "GGA") {
this.old_msg = this.msg;
this.msg = {};
this.msg.time = s[1];
this.msg.quality = s[6];
this.msg.in_view = s[7];
this.msg.hdop = s[8];
this.msg.gp = {};
this.msg.bd = {};
this.msg.gl = {};
print("-----------------------------------------------");
print("GGA Time", s[1], "fix quality", s[4], "sats in view ", s[5]);
this.drawSats(this.sats);
if (this.sats_used < 5)
this.sky_start = getTime();
this.snum = 0;
this.sats = [];
this.sats_used = 0;
if (this.debug > 0) {
print("-----------------------------------------------");
print("GGA Time", s[1], "fix quality", s[4], "sats in view ", s[5]);
}
return;
}
if (cmd === "GLL") return; /* Position lat/lon */
if (cmd === "GSA") {
/*
$GNGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5,4*04
0 1 2 15 16 17 18
*/
$GNGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5,4*04
0 1 2 15 16 17 18
*/
/* Satelites used, fix type! INTERESTING */
let sys = s[18];
let add = {};
@ -256,41 +395,48 @@ let sky = {
add.pdop = s[15];
add.hdop = s[16];
add.vdop = s[17];
sys = sys[0];
/* FIXME -- should really add to the sentence */
if (sys == 1) { this.msg.gp = add; }
else if (sys == 2) { this.msg.gl = add; }
else if (sys == 4) { this.msg.bd = add; }
else print("GSA Unknown system\n");
print(msg);
else {
print("GSA Unknown system -- ", sys, "\n");
print(msg);
}
return;
}
if (s[0] === "$GPGSV") {
print("Have gps sentences", s[1], "/", s[2]);
this.parseSats(s);
if (this.debug > 0)
print("Have gps sentences", s[1], "/", s[2]);
this.all.parseSats(s);
if (this.split)
this.s_gp.parseSats(s);
this.msg.gp.sent = ""+s[2];
return;
}
if (s[0] === "$BDGSV") {
print("Have baidu sentences", s[1], "/", s[2]);
this.parseSats(s);
if (this.debug > 0)
print("Have baidu sentences", s[1], "/", s[2]);
this.all.parseSats(s);
if (this.split)
this.s_bd.parseSats(s);
this.msg.bd.sent = ""+s[2];
return;
}
if (s[0] === "$GLGSV") {
print("Have glonass sentences", s[1], "/", s[2]);
this.parseSats(s);
if (this.debug > 0)
print("Have glonass sentences", s[1], "/", s[2]);
this.all.parseSats(s);
if (this.split)
this.s_gl.parseSats(s);
this.msg.gl.sent = ""+s[2];
return;
}
if (cmd === "RMC") return; /* Repeat of position/speed/course */
if (cmd === "VTG") return; /* Speeds in knots/kph */
if (cmd === "ZDA") return; /* Time + timezone */
if (cmd === "TXT") {
this.msg.finished = 1;
return; /* Misc text? antena open */
}
if (cmd === "TXT") return; /* Misc text? antena open */
print(msg);
},
casic_cmd: function (cmd) {
@ -314,20 +460,32 @@ let sky = {
function start() {
Bangle.setGPSPower(1);
Bangle.on('GPS-raw', function(a, b) { sky.parseRaw(a, b); });
Bangle.on('GPS-raw', (a, b) => sky.parseRaw(a, b));
setTimeout(function() {
Bangle.removeAllListeners('GPS-raw');
}, 1000000);
setInterval(function() { sky.display(); }, 1000);
}
function onMessage() {
/* quality.updateGps(); /* FIXME -- for skyspy
if (ui.display == 4)
sky.drawEstimates();
*/
if (ui.display == 0)
sky.drawSats(sky.all.sats);
if (ui.display == 1)
sky.drawRace();
}
// CASIC_CMD("$PCAS06,0"); /* Query product information */
setTimeout(() => sky.casic_cmd("$PCAS04,7"), 1000); /* Enable gps + beidou + glonass */
setTimeout(() => sky.casic_cmd("$PCAS03,1,1,1,1,1,1,1,1"), 1000); /* Enable gps + beidou + glonass */
setTimeout(() => sky.casic_cmd("$PCAS03,1,1,1,1,1,1,1,1"), 1500); /* Enable all messages */
//setTimeout(() => sky.casic_cmd("$PCAS10,2"), 1200); /* 2: cold start, 1 warm start, 0: hot start */
ui.init();
ui.topLeft = () => sky.selectSpace();
Bangle.on("drag", (b) => ui.touchHandler(b));
sky.onMessageEnd = onMessage;
sky.init();
start();