2024-12-02 18:52:55 +00:00
|
|
|
/* Space race */
|
2024-11-27 20:45:54 +00:00
|
|
|
|
2025-01-05 17:53:17 +00:00
|
|
|
/*
|
2024-11-27 20:45:54 +00:00
|
|
|
gsa mi rika 2d/3d fix, a taky pdop/vdop/hdop
|
|
|
|
|
|
|
|
CFG-NAVX z CASIC_en -- umoznuje nastavit chodec / auto / letadlo
|
|
|
|
|
|
|
|
MON-VER -- vrati version stringy
|
|
|
|
|
|
|
|
CFG-PMS z gpssetup to zda se neumi?
|
|
|
|
|
|
|
|
2.11.5 CFG-RATE (0x06 0x04)
|
|
|
|
*/
|
|
|
|
|
2024-12-02 18:52:55 +00:00
|
|
|
/* ui library 0.1.4 */
|
2024-11-27 20:45:54 +00:00
|
|
|
let ui = {
|
|
|
|
display: 0,
|
|
|
|
numScreens: 2,
|
|
|
|
drawMsg: function(msg) {
|
|
|
|
g.reset().setFont("Vector", 35)
|
|
|
|
.setColor(1, 1, 1)
|
|
|
|
.fillRect(0, this.wi, this.w, this.y2)
|
|
|
|
.setColor(0, 0, 0)
|
|
|
|
.drawString(msg, 5, 30)
|
|
|
|
.flip();
|
|
|
|
},
|
|
|
|
drawBusy: function() {
|
|
|
|
this.drawMsg("\n.oO busy");
|
|
|
|
},
|
|
|
|
nextScreen: function() {
|
|
|
|
print("nextS");
|
|
|
|
this.display = this.display + 1;
|
|
|
|
if (this.display == this.numScreens)
|
|
|
|
this.display = 0;
|
|
|
|
this.drawBusy();
|
|
|
|
},
|
|
|
|
prevScreen: function() {
|
|
|
|
print("prevS");
|
|
|
|
this.display = this.display - 1;
|
|
|
|
if (this.display < 0)
|
|
|
|
this.display = this.numScreens - 1;
|
|
|
|
this.drawBusy();
|
|
|
|
},
|
|
|
|
onSwipe: function(dir) {
|
|
|
|
this.nextScreen();
|
|
|
|
},
|
|
|
|
wi: 24,
|
|
|
|
y2: 176,
|
|
|
|
h: 152,
|
|
|
|
w: 176,
|
|
|
|
last_b: 0,
|
|
|
|
topLeft: function() { this.drawMsg("Unimpl"); },
|
|
|
|
topRight: function() { this.drawMsg("Unimpl"); },
|
|
|
|
touchHandler: function(d) {
|
|
|
|
let x = Math.floor(d.x);
|
|
|
|
let y = Math.floor(d.y);
|
2025-01-05 17:53:17 +00:00
|
|
|
|
2024-11-27 20:45:54 +00:00
|
|
|
if (d.b != 1 || this.last_b != 0) {
|
|
|
|
this.last_b = d.b;
|
|
|
|
return;
|
|
|
|
}
|
2025-01-05 17:53:17 +00:00
|
|
|
|
2024-11-27 20:45:54 +00:00
|
|
|
print("touch", x, y, this.h, this.w);
|
|
|
|
|
|
|
|
if ((x<this.w/2) && (y<this.y2/2))
|
|
|
|
this.topLeft();
|
|
|
|
if ((x>this.w/2) && (y<this.y2/2))
|
|
|
|
this.topRight();
|
|
|
|
if ((x<this.w/2) && (y>this.y2/2)) {
|
|
|
|
print("prev");
|
|
|
|
this.prevScreen();
|
|
|
|
}
|
|
|
|
if ((x>this.w/2) && (y>this.y2/2)) {
|
|
|
|
print("next");
|
|
|
|
this.nextScreen();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
init: function() {
|
|
|
|
this.h = this.y2 - this.wi;
|
|
|
|
this.drawBusy();
|
2024-12-02 18:52:55 +00:00
|
|
|
},
|
|
|
|
/* radial angle -- convert 0..1 to 0..2pi */
|
|
|
|
radA: function(p) { return p*(Math.PI*2); },
|
|
|
|
/* radial distance -- convert 0..1 to something that fits on screen */
|
|
|
|
radD: function(d) { return d*(ui.h/2); },
|
|
|
|
|
|
|
|
/* given angle/distance, get X coordinate */
|
|
|
|
radX: function(p, d) {
|
|
|
|
let a = this.radA(p);
|
|
|
|
return this.w/2 + Math.sin(a)*this.radD(d);
|
|
|
|
},
|
|
|
|
/* given angle/distance, get Y coordinate */
|
|
|
|
radY: function(p, d) {
|
|
|
|
let a = this.radA(p);
|
|
|
|
return this.h/2 - Math.cos(a)*this.radD(d) + this.wi;
|
|
|
|
},
|
|
|
|
radLine: function(a1, d1, a2, d2) {
|
|
|
|
g.drawLine(this.radX(a1, d1), this.radY(a1, d1), this.radX(a2, d2), this.radY(a2, d2));
|
|
|
|
},
|
|
|
|
radCircle: function(d) {
|
|
|
|
g.drawCircle(this.radX(0, 0), this.radY(0, 0), this.radD(d));
|
|
|
|
if (1)
|
|
|
|
return;
|
|
|
|
let step = 0.05;
|
|
|
|
for (let i = 0; i < 1; i += 0.05) {
|
|
|
|
this.radLine(i - step, d, i, d);
|
|
|
|
}
|
|
|
|
},
|
2024-11-27 20:45:54 +00:00
|
|
|
};
|
|
|
|
|
2024-12-02 18:52:55 +00:00
|
|
|
let fix = {}; /* Global for sky library */
|
|
|
|
|
2025-01-04 22:47:29 +00:00
|
|
|
/* sky library v0.2.2 */
|
2024-12-02 18:52:55 +00:00
|
|
|
let sky = {
|
|
|
|
sats: [],
|
|
|
|
snum: 0,
|
|
|
|
sats_used: 0,
|
|
|
|
sky_start: -1,
|
|
|
|
this_usable: 0,
|
2025-01-04 22:47:29 +00:00
|
|
|
debug: 0,
|
2024-12-02 18:52:55 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
},
|
|
|
|
|
2025-01-04 22:47:29 +00:00
|
|
|
/* 18.. don't get reliable fix in 40s */
|
|
|
|
snrLim: 22,
|
2024-12-02 18:52:55 +00:00
|
|
|
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)
|
2025-01-05 17:53:17 +00:00
|
|
|
g.setColor(1, 0.25, 0.25);
|
2024-12-02 18:52:55 +00:00
|
|
|
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)
|
2025-01-04 22:47:29 +00:00
|
|
|
.setFontAlign(-1, 1);
|
2024-12-02 18:52:55 +00:00
|
|
|
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);
|
|
|
|
for (let i = 4, j = 0; j < k; j++) {
|
|
|
|
let sat = { id: s[i++], ele: 1 * s[i++], azi: 1 * s[i++], snr: s[i++] };
|
|
|
|
if (sat.snr === "")
|
|
|
|
sat.snr = 0;
|
|
|
|
if (sat.snr >= this.snrLim) {
|
|
|
|
this.sats_used++;
|
|
|
|
print(sat.id, sat.snr);
|
|
|
|
}
|
|
|
|
this.sats[this.snum++] = sat;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2024-11-27 20:45:54 +00:00
|
|
|
old_msg: {},
|
|
|
|
msg: {},
|
|
|
|
tof: function(v) { let i = (1*v); return i.toFixed(0); },
|
|
|
|
fmtSys: function(sys) {
|
2025-01-04 22:47:29 +00:00
|
|
|
return sys.sent + "." + sys.d23 + "D "+ this.tof(sys.pdop) + " " + this.tof(sys.vdop) + "\n";
|
2024-11-27 20:45:54 +00:00
|
|
|
},
|
2025-01-05 17:48:51 +00:00
|
|
|
drawRace: function() {
|
2024-11-27 20:45:54 +00:00
|
|
|
let m = this.old_msg;
|
2025-01-05 17:53:17 +00:00
|
|
|
let msg = "" + this.tof(m.time) + "\n" +
|
2024-11-27 20:45:54 +00:00
|
|
|
"q" + m.quality + " " + m.in_view + " " + m.hdop + "\n" +
|
|
|
|
"gp"+ this.fmtSys(m.gp) +
|
|
|
|
"bd" + this.fmtSys(m.bd) +
|
|
|
|
"gl" + this.fmtSys(m.gl);
|
|
|
|
if (this.msg.finished != 1)
|
|
|
|
msg += "!";
|
|
|
|
g.reset().clear().setFont("Vector", 30)
|
|
|
|
.setColor(0, 0, 0)
|
|
|
|
.setFontAlign(-1, -1)
|
|
|
|
.drawString(msg, 0, 0);
|
|
|
|
},
|
2025-01-04 22:47:29 +00:00
|
|
|
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;
|
2025-01-05 17:53:17 +00:00
|
|
|
|
2025-01-04 22:47:29 +00:00
|
|
|
// 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 (i=4; i>=0; i--) {
|
|
|
|
if (sortedSats[i] && sortedSats[i].snr)
|
|
|
|
return "S" + (i+1);
|
|
|
|
}
|
|
|
|
return "nil";
|
|
|
|
},
|
|
|
|
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 };
|
2025-01-05 17:53:17 +00:00
|
|
|
} else
|
2025-01-04 22:47:29 +00:00
|
|
|
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 "none";
|
|
|
|
let t = getTime() - s.start;
|
|
|
|
return "" + t;
|
|
|
|
},
|
2025-01-05 17:48:51 +00:00
|
|
|
drawEstimates: function() {
|
2025-01-05 17:53:17 +00:00
|
|
|
/*
|
2025-01-05 17:52:30 +00:00
|
|
|
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
|
|
|
|
*/
|
2025-01-05 17:48:51 +00:00
|
|
|
let r = this.qualest();
|
|
|
|
let r1 = this.goodest();
|
|
|
|
print(r, r1, this.old_msg.hdop, this.old_msg.quality);
|
2025-01-05 17:52:30 +00:00
|
|
|
ui.drawMsg(r + "\n" + r1 + "\n" + this.old_msg.hdop + "-" + this.old_msg.quality + "d\n" + (getTime() - this.sky_start));
|
2025-01-05 17:48:51 +00:00
|
|
|
},
|
|
|
|
onMessageEnd: function() {
|
|
|
|
/* quality.updateGps(); /* FIXME -- for skyspy */
|
|
|
|
if (ui.display == 0)
|
|
|
|
this.drawSats(this.sats);
|
|
|
|
if (ui.display == 1)
|
|
|
|
this.drawRace();
|
|
|
|
if (ui.display == 4)
|
|
|
|
this.drawEstimates();
|
|
|
|
},
|
2025-01-04 22:47:29 +00:00
|
|
|
messageEnd: function() {
|
|
|
|
this.old_msg = this.msg;
|
|
|
|
this.msg = {};
|
|
|
|
this.msg.gp = {};
|
|
|
|
this.msg.bd = {};
|
|
|
|
this.msg.gl = {};
|
2025-01-05 17:48:51 +00:00
|
|
|
this.onMessageEnd();
|
2025-01-04 22:47:29 +00:00
|
|
|
this.trackSatelliteVisibility();
|
|
|
|
//print(this.sats);
|
|
|
|
if (this.sats_used < 5)
|
|
|
|
this.sky_start = getTime();
|
|
|
|
this.snum = 0;
|
|
|
|
this.sats = [];
|
|
|
|
this.sats_used = 0;
|
|
|
|
},
|
2024-12-02 18:52:55 +00:00
|
|
|
parseRaw: function(msg, lost) {
|
|
|
|
if (lost) print("## data lost");
|
2024-11-27 20:45:54 +00:00
|
|
|
let s = msg.split(",");
|
2025-01-04 22:47:29 +00:00
|
|
|
// print(getTime(), s[0]);
|
|
|
|
//return;
|
2024-11-27 20:45:54 +00:00
|
|
|
let cmd = s[0].slice(3);
|
|
|
|
//print("cmd", cmd);
|
2025-01-05 17:48:51 +00:00
|
|
|
if (cmd === "TXT") { // FIXME: we want to end on some more common message */
|
2025-01-04 22:47:29 +00:00
|
|
|
this.messageEnd();
|
|
|
|
return;
|
|
|
|
}
|
2024-11-27 20:45:54 +00:00
|
|
|
if (cmd === "GGA") {
|
|
|
|
this.msg.time = s[1];
|
|
|
|
this.msg.quality = s[6];
|
|
|
|
this.msg.in_view = s[7];
|
|
|
|
this.msg.hdop = s[8];
|
2025-01-04 22:47:29 +00:00
|
|
|
|
|
|
|
if (this.debug > 0) {
|
|
|
|
print("-----------------------------------------------");
|
|
|
|
print("GGA Time", s[1], "fix quality", s[4], "sats in view ", s[5]);
|
|
|
|
}
|
2024-11-27 20:45:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cmd === "GLL") return; /* Position lat/lon */
|
|
|
|
if (cmd === "GSA") {
|
|
|
|
/*
|
2025-01-04 22:47:29 +00:00
|
|
|
$GNGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5,4*04
|
2025-01-05 17:53:17 +00:00
|
|
|
0 1 2 15 16 17 18
|
2025-01-04 22:47:29 +00:00
|
|
|
*/
|
2024-11-27 20:45:54 +00:00
|
|
|
/* Satelites used, fix type! INTERESTING */
|
|
|
|
let sys = s[18];
|
|
|
|
let add = {};
|
|
|
|
add.d23 = s[2];
|
|
|
|
add.pdop = s[15];
|
|
|
|
add.hdop = s[16];
|
|
|
|
add.vdop = s[17];
|
2025-01-04 22:47:29 +00:00
|
|
|
sys = sys[0];
|
2024-11-27 20:45:54 +00:00
|
|
|
/* 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; }
|
2025-01-04 22:47:29 +00:00
|
|
|
else {
|
|
|
|
print("GSA Unknown system -- ", sys, "\n");
|
|
|
|
print(msg);
|
|
|
|
}
|
2024-11-27 20:45:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (s[0] === "$GPGSV") {
|
2025-01-04 22:47:29 +00:00
|
|
|
if (this.debug > 0)
|
|
|
|
print("Have gps sentences", s[1], "/", s[2]);
|
2024-12-02 18:52:55 +00:00
|
|
|
this.parseSats(s);
|
2024-11-27 20:45:54 +00:00
|
|
|
this.msg.gp.sent = ""+s[2];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (s[0] === "$BDGSV") {
|
2025-01-04 22:47:29 +00:00
|
|
|
if (this.debug > 0)
|
|
|
|
print("Have baidu sentences", s[1], "/", s[2]);
|
2024-12-02 18:52:55 +00:00
|
|
|
this.parseSats(s);
|
2024-11-27 20:45:54 +00:00
|
|
|
this.msg.bd.sent = ""+s[2];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (s[0] === "$GLGSV") {
|
2025-01-04 22:47:29 +00:00
|
|
|
if (this.debug > 0)
|
|
|
|
print("Have glonass sentences", s[1], "/", s[2]);
|
2024-12-02 18:52:55 +00:00
|
|
|
this.parseSats(s);
|
2024-11-27 20:45:54 +00:00
|
|
|
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;
|
2025-01-05 17:53:17 +00:00
|
|
|
return; /* Misc text? antena open */
|
2024-11-27 20:45:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
print(msg);
|
|
|
|
},
|
|
|
|
casic_cmd: function (cmd) {
|
|
|
|
var cs = 0;
|
|
|
|
for (var i=1;i<cmd.length;i++)
|
|
|
|
cs = cs ^ cmd.charCodeAt(i);
|
|
|
|
Serial1.println(cmd+"*"+cs.toString(16).toUpperCase().padStart(2, '0'));
|
|
|
|
},
|
|
|
|
sys: 3,
|
|
|
|
selectSpace: function () {
|
|
|
|
this.sys += 1;
|
|
|
|
if (this.sys == 4)
|
|
|
|
this.sys = 0;
|
2025-01-04 22:47:29 +00:00
|
|
|
val = 7;
|
2024-11-27 20:45:54 +00:00
|
|
|
if (this.sys)
|
|
|
|
val = 1 << (this.sys - 1);
|
|
|
|
this.casic_cmd("$PCAS04,"+val);
|
|
|
|
ui.drawMsg("Sys "+val);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function start() {
|
|
|
|
Bangle.setGPSPower(1);
|
2024-12-02 18:52:55 +00:00
|
|
|
Bangle.on('GPS-raw', function(a, b) { sky.parseRaw(a, b); });
|
2024-11-27 20:45:54 +00:00
|
|
|
setTimeout(function() {
|
|
|
|
Bangle.removeAllListeners('GPS-raw');
|
|
|
|
}, 1000000);
|
|
|
|
}
|
|
|
|
|
|
|
|
// CASIC_CMD("$PCAS06,0"); /* Query product information */
|
2024-12-02 18:52:55 +00:00
|
|
|
setTimeout(() => sky.casic_cmd("$PCAS04,7"), 1000); /* Enable gps + beidou + glonass */
|
2025-01-04 22:47:29 +00:00
|
|
|
setTimeout(() => sky.casic_cmd("$PCAS03,1,1,1,1,1,1,1,1"), 1500); /* Enable all messages */
|
2024-12-02 18:52:55 +00:00
|
|
|
|
|
|
|
//setTimeout(() => sky.casic_cmd("$PCAS10,2"), 1200); /* 2: cold start, 1 warm start, 0: hot start */
|
2024-11-27 20:45:54 +00:00
|
|
|
|
|
|
|
ui.init();
|
2024-12-02 18:52:55 +00:00
|
|
|
ui.topLeft = () => sky.selectSpace();
|
2024-11-27 20:45:54 +00:00
|
|
|
Bangle.on("drag", (b) => ui.touchHandler(b));
|
|
|
|
start();
|