forked from FOSS/BangleApps
commit
133532ef8c
|
@ -1 +1,2 @@
|
|||
0.01: attempt to import
|
||||
0.10: major updates, display satellites, display per-system satellite info
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 |
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue