Merge branch 'experimental'
commit
8241944b7c
119
coverage.cc
119
coverage.cc
|
@ -8,39 +8,77 @@
|
|||
#include "fmt/printf.h"
|
||||
#include <fstream>
|
||||
|
||||
#include <eigen3/Eigen/Dense>
|
||||
using Eigen::MatrixXd;
|
||||
|
||||
|
||||
using namespace std;
|
||||
extern GetterSetter<map<int, GalileoMessage::Almanac>> g_galileoalmakeeper;
|
||||
extern GetterSetter<svstats_t> g_statskeeper;
|
||||
|
||||
xDOP getDOP(Point& us, vector<Point> sats)
|
||||
{
|
||||
xDOP ret;
|
||||
if(sats.size() < 4) {
|
||||
return ret;
|
||||
}
|
||||
MatrixXd G(sats.size(), 4); // 4 columns
|
||||
|
||||
covmap_t emitCoverage()
|
||||
// (x1 - x)/R1 (y1 -y)/R1 (z1 - z)/R1 -1
|
||||
|
||||
for(size_t n = 0 ; n < sats.size(); ++n) {
|
||||
const auto& s = sats[n];
|
||||
auto R = Vector(us, s).length();
|
||||
|
||||
G(n, 0) = (s.x - us.x)/R;
|
||||
G(n, 1) = (s.y - us.y)/R;
|
||||
G(n, 2) = (s.z - us.z)/R;
|
||||
G(n, 3) = -1;
|
||||
}
|
||||
|
||||
// cout<<"Matrix: "<<endl;
|
||||
// cout<<G<<endl;
|
||||
MatrixXd Q = (G.transpose() * G).inverse();
|
||||
|
||||
auto [lambda, phi] = getLongLat(us.x, us.y, us.z);
|
||||
|
||||
// https://gssc.esa.int/navipedia/index.php/Positioning_Error
|
||||
Eigen::Matrix3d Renu;
|
||||
Renu <<
|
||||
(-sin(lambda)) , (-sin(phi)*cos(lambda)) , (cos(phi)*cos(lambda)),
|
||||
(cos(lambda)) , (-sin(phi)*sin(lambda)) , (cos(phi)*sin(lambda)),
|
||||
(0.0) , (cos(phi)) , (sin(phi));
|
||||
|
||||
Eigen::Matrix3d Qxyz;
|
||||
for(int x=0; x<3; ++x) // feels like there should be a better way for this, but not sure
|
||||
for(int y=0; y<3; ++y)
|
||||
Qxyz(x,y) = Q(x,y);
|
||||
|
||||
Eigen::Matrix3d Qenu = Renu.transpose()*Qxyz*Renu;
|
||||
// if(Qenu(0,0) < 0 || Qenu(1,1) < 0 || Qenu(2,2) < 0)
|
||||
// cout << "Original: \n"<<Qxyz<<"\nRotated: \n"<<Qenu<<endl;
|
||||
|
||||
ret.pdop = sqrt(Q(0,0) + Q(1,1) + Q(2,2)); // already squared
|
||||
// ret.pdop = sqrt(Qenu(0,0) + Qenu(1,1) + Qenu(2,2)); // already squared
|
||||
|
||||
ret.tdop = sqrt(Q(3,3));
|
||||
ret.gdop = sqrt(ret.pdop*ret.pdop + ret.tdop*ret.tdop);
|
||||
if(Qenu(0,0) >=0 && Qenu(1,1) >=0)
|
||||
ret.hdop = sqrt(Qenu(0,0) + Qenu(1,1));
|
||||
if(Qenu(2,2)>=0)
|
||||
ret.vdop = sqrt(Qenu(2,2));
|
||||
return ret;
|
||||
};
|
||||
|
||||
covmap_t emitCoverage(const vector<Point>& sats)
|
||||
{
|
||||
covmap_t ret;
|
||||
ofstream cmap("covmap.csv");
|
||||
cmap<<"latitude longitude count5 count10 count20"<<endl;
|
||||
map<int, Point> sats;
|
||||
auto galileoalma = g_galileoalmakeeper.get();
|
||||
auto svstats = g_statskeeper.get();
|
||||
auto pseudoTow = (time(0) - 820368000) % (7*86400);
|
||||
// cout<<"pseudoTow "<<pseudoTow<<endl;
|
||||
for(const auto &g : galileoalma) {
|
||||
Point sat;
|
||||
getCoordinates(pseudoTow, g.second, &sat);
|
||||
|
||||
if(g.first < 0)
|
||||
continue;
|
||||
if(svstats[{2,g.first,1}].completeIOD() && svstats[{2,g.first,1}].liveIOD().sisa == 255) {
|
||||
// cout<<g.first<<" NAPA!"<<endl;
|
||||
continue;
|
||||
}
|
||||
sats[g.first]=sat;
|
||||
}
|
||||
double R = 6371000;
|
||||
for(double latitude = 90 ; latitude > -90; latitude-=0.5) { // north-south
|
||||
for(double latitude = 90 ; latitude > -90; latitude-=2) { // north-south
|
||||
double phi = M_PI* latitude / 180;
|
||||
double longsteps = 1 + 360.0 * cos(phi);
|
||||
double step = 180.0 / longsteps;
|
||||
vector<tuple<double, int, int, int>> latvect;
|
||||
double step = 4*180.0 / longsteps;
|
||||
vector<tuple<double, int, int, int, double, double, double, double, double, double,double, double, double>> latvect;
|
||||
for(double longitude = -180; longitude < 180; longitude += step) { // east - west
|
||||
Point p;
|
||||
// phi = latitude, lambda = longitude
|
||||
|
@ -51,28 +89,45 @@ covmap_t emitCoverage()
|
|||
p.z = R * sin(phi);
|
||||
|
||||
if(longitude == -180) {
|
||||
auto longlat = getLongLat(p.x, p.y, p.z);
|
||||
// auto longlat = getLongLat(p.x, p.y, p.z);
|
||||
// cout<<fmt::sprintf("%3.0f ", 180.0*longlat.second/M_PI);
|
||||
}
|
||||
|
||||
|
||||
int numsats5=0, numsats10=0, numsats20=0;
|
||||
vector<Point> satposs5, satposs10, satposs20;
|
||||
for(const auto& s : sats) {
|
||||
// double getElevationDeg(const Point& sat, const Point& our);
|
||||
double elev = getElevationDeg(s.second, p);
|
||||
if(elev > 5.0)
|
||||
double elev = getElevationDeg(s, p);
|
||||
if(elev > 5.0) {
|
||||
satposs5.push_back(s);
|
||||
numsats5++;
|
||||
if(elev > 10.0)
|
||||
}
|
||||
if(elev > 10.0) {
|
||||
satposs10.push_back(s);
|
||||
numsats10++;
|
||||
if(elev > 20.0)
|
||||
}
|
||||
if(elev > 20.0) {
|
||||
satposs20.push_back(s);
|
||||
numsats20++;
|
||||
}
|
||||
}
|
||||
if(numsats20 < 4)
|
||||
latvect.push_back(make_tuple(longitude, numsats5, numsats10, numsats20));
|
||||
latvect.push_back(make_tuple(longitude,
|
||||
numsats5, numsats10, numsats20,
|
||||
getDOP(p, satposs5).pdop,
|
||||
getDOP(p, satposs10).pdop,
|
||||
getDOP(p, satposs20).pdop,
|
||||
getDOP(p, satposs5).hdop,
|
||||
getDOP(p, satposs10).hdop,
|
||||
getDOP(p, satposs20).hdop,
|
||||
getDOP(p, satposs5).vdop,
|
||||
getDOP(p, satposs10).vdop,
|
||||
getDOP(p, satposs20).vdop
|
||||
|
||||
));
|
||||
// cmap << longitude <<" " <<latitude <<" " << numsats5 << " " <<numsats10<<" "<<numsats20<<endl;
|
||||
}
|
||||
if(!latvect.empty())
|
||||
ret.push_back(make_pair(latitude, latvect));
|
||||
ret.push_back(make_pair(latitude, latvect));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,10 @@ std::pair<double,double> getLongLat(double x, double y, double z)
|
|||
longitude *= -1;
|
||||
|
||||
Vector toUs{core, pos};
|
||||
double latitude = acos( flat.inner(toUs) / (toUs.length() * flat.length()));
|
||||
double inp = flat.inner(toUs) / (toUs.length() * flat.length());
|
||||
if(inp > 1.0 && inp < 1.0000001) // this happens because of rounding errors
|
||||
inp=1.0;
|
||||
double latitude = acos( inp);
|
||||
if(z < 0)
|
||||
latitude *= -1;
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>galmon.eu geo</title>
|
||||
<link rel="stylesheet" type="text/css" href="geo.css">
|
||||
|
||||
<script src="../d3.v4.min.js"></script>
|
||||
<script src="../ext/topojson.v1.min.js"></script>
|
||||
<script src="../ext/d3-geo-projection.v2.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="galmongeo">
|
||||
<h1>galmon.eu geo</h1>
|
||||
<div id="galmoninfo">
|
||||
This is a live map from the <a href="/">galmon.eu</a> project. Red blocks indicate no Galileo fix is likely possible,
|
||||
because less than four satellites have an elevation of > 5 degrees. Orange means less than four above 10 degrees, yellow less than four above 20 degrees.
|
||||
<b>These calculations are very provisional and might be wrong!</b>
|
||||
<center>
|
||||
<p>
|
||||
<input type="radio" name="kind" id="coverage" onclick="do_timer();" ><label for="coverage">Coverage</label>
|
||||
<input type="radio" name="kind" id="pdop" onclick="do_timer();" checked><label for="pdop">PDOP > 10 or no fix</label>
|
||||
<input type="radio" name="kind" id="hdop" onclick="do_timer();" ><label for="hdop">HDOP > 10 or no fix</label>
|
||||
<input type="radio" name="kind" id="vdop" onclick="do_timer();" ><label for="vdop">VDOP > 10 or no fix</label>
|
||||
|
||||
|
||||
<hr/>
|
||||
<input type="checkbox" id="GalE1" onclick="do_timer();" checked> <label for="GalE1">Galileo E1</label>
|
||||
<input type="checkbox" id="GPSL1CA" onclick="do_timer();"> <label for="GPSL1CA">GPS L1C/A</label>
|
||||
<input type="checkbox" id="Beidou" onclick="do_timer();"> <label for="Beidou">Beidou</label>
|
||||
</p>
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<div id="combined">
|
||||
<svg id="svgworld"></svg>
|
||||
<svg id="svggraticule"></svg>
|
||||
<svg id="svgobservers"></svg>
|
||||
<svg id="svgalmanac"></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="coverage.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,483 @@
|
|||
'use strict';
|
||||
//
|
||||
|
||||
var wantGPS=0, wantGalileo=0, wantBeidou=0;
|
||||
//
|
||||
//
|
||||
|
||||
var fileWorld = "world.geojson";
|
||||
var fileAlmanac = "../almanac.json" // "https://galmon.eu/almanac"
|
||||
var fileObservers = "../observers.json" // "https://galmon.eu/observers"
|
||||
|
||||
var projectionChoice = "Fahey";
|
||||
var projectionChoice = "CylindricalStereographic";
|
||||
var projectionChoice = "Equirectangular";
|
||||
//var projectionChoice = "Aitoff";
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
var svgWorld = d3.select("#svgworld");
|
||||
var idWorld = document.getElementById("svgworld");
|
||||
|
||||
var svgGraticule = d3.select("#svggraticule");
|
||||
var idGraticule = document.getElementById("svggraticule");
|
||||
|
||||
var svgObservers = d3.select("#svgobservers");
|
||||
var idObservers = document.getElementById("svgobservers");
|
||||
|
||||
var svgAlmanac = d3.select("#svgalmanac");
|
||||
var idAlmanac = document.getElementById("svgalmanac");
|
||||
|
||||
var geoPath;
|
||||
var aProjection;
|
||||
|
||||
function draw_world(data_world)
|
||||
{
|
||||
// console.log("draw_world() " + data_world.features.length);
|
||||
|
||||
svgWorld.selectAll("path")
|
||||
.data(data_world.features)
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("class", "countries")
|
||||
.attr("d", geoPath);
|
||||
}
|
||||
|
||||
function draw_graticule()
|
||||
{
|
||||
// Graticule
|
||||
var graticule = d3.geoGraticule();
|
||||
|
||||
// console.log("draw_graticule()");
|
||||
|
||||
svgGraticule.selectAll("path")
|
||||
.data(graticule.lines())
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("class", "graticule line")
|
||||
.attr("id", function(d) {
|
||||
var c = d.coordinates;
|
||||
if (c[0][0] == c[1][0]) {
|
||||
return (c[0][0] < 0) ? -c[0][0] + "W" : +c[0][0] + "E";
|
||||
} else if (c[0][1] == c[1][1]) {
|
||||
return (c[0][1] < 0) ? -c[0][1] + "S" : c[0][1] + "N";
|
||||
}
|
||||
})
|
||||
.attr("d", geoPath);
|
||||
|
||||
svgGraticule.selectAll('text')
|
||||
.data(graticule.lines())
|
||||
.enter()
|
||||
.append("text")
|
||||
.text(function(d) {
|
||||
var c = d.coordinates;
|
||||
if ((c[0][0] == c[1][0]) && (c[0][0] % 30 == 0)) {
|
||||
return (c[0][0]);
|
||||
} else if (c[0][1] == c[1][1]) {
|
||||
return (c[0][1]);
|
||||
}
|
||||
})
|
||||
.attr("class","label")
|
||||
.attr("style", function(d) {
|
||||
var c = d.coordinates;
|
||||
return (c[0][1] == c[1][1]) ? "text-anchor: end" : "text-anchor: middle";
|
||||
})
|
||||
.attr("dx", function(d) {
|
||||
var c = d.coordinates;
|
||||
return (c[0][1] == c[1][1]) ? -10 : 0;
|
||||
})
|
||||
.attr("dy", function(d) {
|
||||
var c = d.coordinates;
|
||||
return (c[0][1] == c[1][1]) ? 4 : 10;
|
||||
})
|
||||
.attr('transform', function(d) {
|
||||
var c = d.coordinates;
|
||||
return ('translate(' + aProjection(c[0]) + ')')
|
||||
});
|
||||
|
||||
svgGraticule.append("path")
|
||||
.datum(graticule.outline)
|
||||
.attr("class", "graticule outline")
|
||||
.attr("d", geoPath);
|
||||
}
|
||||
|
||||
function get_almanac_valid(data_almanac)
|
||||
{
|
||||
var a=[];
|
||||
|
||||
|
||||
Object.keys(data_almanac).forEach(function(e) {
|
||||
var o = data_almanac[e];
|
||||
o.sv = e;
|
||||
if (o["eph-latitude"] != null && ((wantGalileo && o["gnssid"]==2) || (wantGPS && o["gnssid"]==0) || (wantBeidou && o["gnssid"]==3) ) ) {
|
||||
a.push(o);
|
||||
}
|
||||
});
|
||||
return a;
|
||||
}
|
||||
|
||||
function draw_coverage(data_coverage)
|
||||
{
|
||||
console.log(data_coverage.length);
|
||||
let coverage=[];
|
||||
|
||||
let mode = d3.select('input[name="kind"]:checked').node().id;
|
||||
console.log(mode);
|
||||
|
||||
var offset=4;
|
||||
if(mode=="pdop")
|
||||
offset=4;
|
||||
else if(mode=="hdop")
|
||||
offset=7;
|
||||
else if(mode=="vdop")
|
||||
offset=10;
|
||||
// console.log("Offset is "+offset+ " for mode "+mode);
|
||||
|
||||
|
||||
for(var i =0 ; i < data_coverage.length; ++i) {
|
||||
for(var j = 0 ; j < data_coverage[i][1].length; ++j) {
|
||||
if(mode == "coverage") {
|
||||
let numsats = data_coverage[i][1][j][1];
|
||||
if(numsats < 4)
|
||||
coverage.push({latitude: data_coverage[i][0], longitude: data_coverage[i][1][j][0], numsats: numsats, color: "red", opacity: 0.4});
|
||||
else {
|
||||
numsats = data_coverage[i][1][j][2];
|
||||
if(numsats < 4)
|
||||
coverage.push({latitude: data_coverage[i][0], longitude: data_coverage[i][1][j][0], numsats: numsats, color: "orange", opacity: null});
|
||||
else {
|
||||
numsats = data_coverage[i][1][j][3];
|
||||
if(numsats < 4)
|
||||
coverage.push({latitude: data_coverage[i][0], longitude: data_coverage[i][1][j][0], numsats: numsats, color: "yellow", opacity: 0.12});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(mode=="pdop" || mode=="hdop" || mode =="vdop") {
|
||||
let dop = data_coverage[i][1][j][offset];
|
||||
if(dop > 100 || dop < 0)
|
||||
coverage.push({latitude: data_coverage[i][0], longitude: data_coverage[i][1][j][0], dop: dop, color: "red", opacity: 0.4});
|
||||
else {
|
||||
dop = data_coverage[i][1][j][offset+1];
|
||||
if(dop > 100 || dop < 0)
|
||||
coverage.push({latitude: data_coverage[i][0], longitude: data_coverage[i][1][j][0], dop: dop, color: "orange", opacity: null});
|
||||
else {
|
||||
dop = data_coverage[i][1][j][offset+2];
|
||||
if(dop > 100 || dop < 0 )
|
||||
coverage.push({latitude: data_coverage[i][0], longitude: data_coverage[i][1][j][0], dop: dop, color: "yellow", opacity: 0.12});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
svgAlmanac.selectAll("rect")
|
||||
.data(coverage)
|
||||
.enter()
|
||||
.append("rect")
|
||||
.attr("class", "sats")
|
||||
.style("opacity", d => d["opacity"])
|
||||
.attr("width", 20)
|
||||
.attr("height", 20)
|
||||
.attr("x", d => aProjection([d["longitude"],d["latitude"]])[0])
|
||||
.attr("y", d => aProjection([d["longitude"],d["latitude"]])[1])
|
||||
.attr("fill", function(d) {
|
||||
return d["color"];
|
||||
});
|
||||
|
||||
|
||||
// console.log(coverage);
|
||||
}
|
||||
|
||||
function draw_almanac(data_almanac)
|
||||
{
|
||||
var arr = get_almanac_valid(data_almanac);
|
||||
|
||||
console.log("draw_almanac() " + arr.length);
|
||||
|
||||
svgAlmanac.selectAll("circle")
|
||||
.data(arr)
|
||||
.enter()
|
||||
.append("circle")
|
||||
.attr("class", "sats")
|
||||
.attr("r", 3)
|
||||
.attr("cx", d => aProjection([d["eph-longitude"],d["eph-latitude"]])[0])
|
||||
.attr("cy", d => aProjection([d["eph-longitude"],d["eph-latitude"]])[1])
|
||||
.attr("fill", function(d) {
|
||||
switch (d.gnssid) {
|
||||
case 0: return "green"; // GPS
|
||||
case 1: return "gray"; // SBAS - not coded
|
||||
case 2: return "blue"; // Galileo
|
||||
case 3: return "red"; // BeiDou
|
||||
case 4: return "gray"; // IMES - not coded
|
||||
case 5: return "gray"; // QZSS - not coded
|
||||
case 6: return "yellow"; // GLONASS
|
||||
default: return "magenta"; // - should not happen
|
||||
}
|
||||
});
|
||||
|
||||
svgAlmanac.selectAll("text")
|
||||
.data(arr)
|
||||
.enter()
|
||||
.append("text")
|
||||
.attr("class", "labels")
|
||||
.text(d => d.sv)
|
||||
.attr("dx", d => 5+aProjection([d["eph-longitude"],d["eph-latitude"]])[0])
|
||||
.attr("dy", d => 5+aProjection([d["eph-longitude"],d["eph-latitude"]])[1])
|
||||
.attr("fill", function(d) {
|
||||
if (d.sisa != null && d.sisa == 255)
|
||||
return "red";
|
||||
if (d.health != null && d.health != 0) // GPS
|
||||
return "red";
|
||||
|
||||
if (d.observed==true)
|
||||
return "black";
|
||||
|
||||
return "#666666";
|
||||
})
|
||||
.attr("font-weight", function(d) {
|
||||
if (d.observed==true)
|
||||
return "bold";
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
function draw_observers(data_observers)
|
||||
{
|
||||
console.log("draw_observers() " + data_observers.length);
|
||||
|
||||
svgObservers.selectAll("rect")
|
||||
.data(data_observers)
|
||||
.enter()
|
||||
.append("rect")
|
||||
.attr("class", "sats")
|
||||
.attr("width", 8)
|
||||
.attr("height", 8)
|
||||
.attr("x", d => aProjection([d["longitude"],d["latitude"]])[0]-4)
|
||||
.attr("y", d => aProjection([d["longitude"],d["latitude"]])[1]-4)
|
||||
.attr("fill", function(d) { return "black"; });
|
||||
|
||||
}
|
||||
|
||||
function draw_observers_coverage(data_observers)
|
||||
{
|
||||
var radius = 55; // XXX fix
|
||||
var geoCircle = d3.geoCircle();
|
||||
svgObservers.selectAll("path")
|
||||
.data(data_observers)
|
||||
.enter()
|
||||
.append("path")
|
||||
.attr("class", "coverage")
|
||||
.attr("d", function(r) {
|
||||
// console.log([r["longitude"], r["latitude"]] + " = " + aProjection([r["longitude"], r["latitude"]]));
|
||||
return geoPath(geoCircle.center([r["longitude"],r["latitude"]]).radius(radius)());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
var display_observers_count = 0;
|
||||
|
||||
function do_update_almanac(error, results)
|
||||
{
|
||||
var data_almanac = results[0];
|
||||
var data_observers = results[1];
|
||||
var data_coverage = results[2];
|
||||
// console.log("do_update_almanac() " + Object.keys(data_almanac).length + " " + data_observers.length);
|
||||
|
||||
if (display_observers_count == 0) {
|
||||
// does not need that much updating!
|
||||
svgObservers.html("");
|
||||
// draw_observers(data_observers);
|
||||
// draw_observers_coverage(data_observers)
|
||||
display_observers_count = 10;
|
||||
}
|
||||
display_observers_count--;
|
||||
|
||||
// We write into the svgalmanac area - so clean it and rewrite it
|
||||
svgAlmanac.html("");
|
||||
draw_almanac(data_almanac);
|
||||
|
||||
draw_coverage(data_coverage);
|
||||
}
|
||||
|
||||
var repeat;
|
||||
|
||||
function do_timer()
|
||||
{
|
||||
var seconds = 60;
|
||||
clearTimeout(repeat);
|
||||
repeat = setTimeout(do_timer, 1000.0*seconds);
|
||||
|
||||
wantGPS = 0;
|
||||
wantGalileo = 0;
|
||||
wantBeidou = 0;
|
||||
if(d3.select("#GPSL1CA").property("checked"))
|
||||
wantGPS=1;
|
||||
if(d3.select("#GalE1").property("checked"))
|
||||
wantGalileo=1;
|
||||
if(d3.select("#Beidou").property("checked"))
|
||||
wantBeidou=1;
|
||||
|
||||
var galcovurl="../cov.json?gps="+wantGPS+"&galileo="+wantGalileo+"&beidou="+wantBeidou;
|
||||
|
||||
d3.queue(1)
|
||||
.defer(d3.json, fileAlmanac)
|
||||
.defer(d3.json, fileObservers)
|
||||
.defer(d3.json, galcovurl)
|
||||
.awaitAll(do_update_almanac);
|
||||
}
|
||||
|
||||
function set_projection(data_world)
|
||||
{
|
||||
// var aProjection = d3.geoMercator().scale(100).translate([250,250]);
|
||||
// all this complexity is so we can scale to full screen.
|
||||
// see: https://stackoverflow.com/questions/14492284/center-a-map-in-d3-given-a-geojson-object
|
||||
|
||||
var center = [0,0]; // This is very Euro-centric - but that's how these projections works.
|
||||
var scale = 210; // No idea what this does
|
||||
|
||||
var idCombined = document.getElementById("combined");
|
||||
|
||||
svgWorld = d3.select("#svgworld");
|
||||
idWorld = document.getElementById("svgworld");
|
||||
|
||||
svgGraticule = d3.select("#svggraticule");
|
||||
idGraticule = document.getElementById("svggraticule");
|
||||
|
||||
switch(projectionChoice) {
|
||||
default:
|
||||
console.log(projectionChoice + ": not coded");
|
||||
// fall thru to Equirectangular
|
||||
case 'Equirectangular':
|
||||
aProjection = d3.geoEquirectangular()
|
||||
.scale(scale)
|
||||
.translate([idCombined.clientWidth/2,idCombined.clientHeight/2]);
|
||||
break;
|
||||
case 'Aitoff':
|
||||
aProjection = d3.geoAitoff()
|
||||
.scale(scale)
|
||||
.translate([idCombined.clientWidth/2,idCombined.clientHeight/2]);
|
||||
break;
|
||||
case 'CylindricalStereographic':
|
||||
aProjection = d3.geoCylindricalStereographic()
|
||||
.scale(scale)
|
||||
.translate([idCombined.clientWidth/2,idCombined.clientHeight/2]);
|
||||
break;
|
||||
case 'Fahey':
|
||||
aProjection = d3.geoFahey()
|
||||
.scale(scale)
|
||||
.translate([idCombined.clientWidth/2,idCombined.clientHeight/2]);
|
||||
break;
|
||||
case 'Gilbert':
|
||||
aProjection = d3.geoGilbert()
|
||||
.scale(scale)
|
||||
.translate([idCombined.clientWidth/2,idCombined.clientHeight/2]);
|
||||
break;
|
||||
}
|
||||
|
||||
geoPath = d3.geoPath()
|
||||
.projection(aProjection);
|
||||
|
||||
// using the path determine the bounds of the current map and use
|
||||
// these to determine better values for the scale and translation
|
||||
var bounds = geoPath.bounds(data_world);;
|
||||
var hscale = scale*idCombined.clientWidth / (bounds[1][0] - bounds[0][0]);
|
||||
var vscale = scale*idCombined.clientHeight / (bounds[1][1] - bounds[0][1]);
|
||||
scale = (hscale < vscale) ? hscale : vscale;
|
||||
var offset = [
|
||||
idCombined.clientWidth - (bounds[0][0] + bounds[1][0])/2,
|
||||
idCombined.clientHeight - (bounds[0][1] + bounds[1][1])/2
|
||||
];
|
||||
|
||||
if (0) {
|
||||
|
||||
// new projection
|
||||
switch(projectionChoice) {
|
||||
default:
|
||||
console.log(projectionChoice + ": not coded");
|
||||
// fall thru to Equirectangular
|
||||
case 'Equirectangular':
|
||||
aProjection = d3.geoEquirectangular()
|
||||
.center(center)
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'Aitoff':
|
||||
aProjection = d3.geoAitoff()
|
||||
.center(center) .scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'CylindricalStereographic':
|
||||
aProjection = d3.geoCylindricalStereographic()
|
||||
.center(center)
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'Fahey':
|
||||
aProjection = d3.geoFahey()
|
||||
.center(center)
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
case 'Gilbert':
|
||||
aProjection = d3.geoGilbert()
|
||||
.center(center)
|
||||
.scale(scale)
|
||||
.translate(offset);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
console.log("do_draw_world() " + "width=" + idCombined.clientWidth + "," + "height=" + idCombined.clientHeight);
|
||||
|
||||
svgWorld.attr("width", idCombined.clientWidth);
|
||||
svgWorld.attr("height", idCombined.clientHeight);
|
||||
|
||||
svgObservers.attr("width", idCombined.clientWidth);
|
||||
svgObservers.attr("height", idCombined.clientHeight);
|
||||
|
||||
svgGraticule.attr("height", idCombined.clientHeight);
|
||||
svgGraticule.attr("width", idCombined.clientWidth);
|
||||
|
||||
svgAlmanac.attr("height", idCombined.clientHeight);
|
||||
svgAlmanac.attr("width", idCombined.clientWidth);
|
||||
|
||||
}
|
||||
|
||||
function do_draw_world(data_world)
|
||||
{
|
||||
// console.log("do_draw_world()");
|
||||
|
||||
set_projection(data_world);
|
||||
|
||||
svgWorld.html("");
|
||||
draw_world(data_world);
|
||||
|
||||
svgGraticule.html("");
|
||||
draw_graticule();
|
||||
}
|
||||
|
||||
function read_world()
|
||||
{
|
||||
// console.log("read_world()");
|
||||
d3.json(fileWorld, function(result) {
|
||||
var data_world = result;
|
||||
do_draw_world(data_world);
|
||||
// after the world is read in and displayed - then start the timers!
|
||||
// we don't redraw the world!
|
||||
do_timer();
|
||||
});
|
||||
}
|
||||
|
||||
function geo_start()
|
||||
{
|
||||
// console.log("geo_start()");
|
||||
read_world();
|
||||
}
|
||||
|
||||
geo_start();
|
||||
|
||||
// d3.select("body").onresize = do_timer;
|
116
navparse.cc
116
navparse.cc
|
@ -28,6 +28,8 @@
|
|||
#include "navmon.hh"
|
||||
#include <Tle.h>
|
||||
#include "navparse.hh"
|
||||
#include <fenv.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct ObserverPosition
|
||||
|
@ -512,6 +514,9 @@ std::string humanBhs(int bhs)
|
|||
int main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW );
|
||||
|
||||
|
||||
// g_tles.parseFile("active.txt");
|
||||
|
||||
g_tles.parseFile("galileo.txt");
|
||||
|
@ -1011,8 +1016,88 @@ try
|
|||
}
|
||||
);
|
||||
|
||||
h2s.addHandler("/galcov.json", [](auto handler, auto req) {
|
||||
auto cov = emitCoverage();
|
||||
h2s.addHandler("/cov.json", [](auto handler, auto req) {
|
||||
vector<Point> sats;
|
||||
auto galileoalma = g_galileoalmakeeper.get();
|
||||
auto gpsalma = g_gpsalmakeeper.get();
|
||||
auto beidoualma = g_beidoualmakeeper.get();
|
||||
auto svstats = g_statskeeper.get();
|
||||
// cout<<"pseudoTow "<<pseudoTow<<endl;
|
||||
string_view path = convert(req->path);
|
||||
|
||||
bool doGalileo{true}, doGPS{false}, doBeidou{false};
|
||||
auto pos = path.find("gps=");
|
||||
if(pos != string::npos) {
|
||||
doGPS = (path[pos+4]=='1');
|
||||
}
|
||||
pos = path.find("galileo=");
|
||||
if(pos != string::npos) {
|
||||
doGalileo = (path[pos+8]=='1');
|
||||
}
|
||||
pos = path.find("beidou=");
|
||||
if(pos != string::npos) {
|
||||
doBeidou = (path[pos+7]=='1');
|
||||
}
|
||||
|
||||
if(doGalileo)
|
||||
for(const auto &g : galileoalma) {
|
||||
Point sat;
|
||||
getCoordinates(latestTow(2, svstats), g.second, &sat);
|
||||
|
||||
if(g.first < 0)
|
||||
continue;
|
||||
SatID id{2,(uint32_t)g.first,1};
|
||||
if(svstats[id].completeIOD() && svstats[id].liveIOD().sisa == 255) {
|
||||
continue;
|
||||
}
|
||||
if(svstats[id].e1bhs || svstats[id].e1bdvs)
|
||||
continue;
|
||||
sats.push_back(sat);
|
||||
}
|
||||
|
||||
if(doGPS)
|
||||
for(const auto &g : gpsalma) {
|
||||
Point sat;
|
||||
getCoordinates(latestTow(0, svstats), g.second, &sat);
|
||||
|
||||
if(g.first < 0)
|
||||
continue;
|
||||
SatID id{0,(uint32_t)g.first,0};
|
||||
if(svstats[id].completeIOD() && svstats[id].ura == 16) {
|
||||
// cout<<"Skipping G"<<id.sv<<" because of URA"<<endl;
|
||||
continue;
|
||||
}
|
||||
if(svstats[id].gpshealth) {
|
||||
// cout<<"Skipping G"<<id.sv<<" because of health"<<endl;
|
||||
continue;
|
||||
}
|
||||
sats.push_back(sat);
|
||||
}
|
||||
|
||||
if(doBeidou)
|
||||
for(const auto &g : beidoualma) {
|
||||
Point sat;
|
||||
getCoordinates(latestTow(3, svstats), g.second.alma, &sat);
|
||||
|
||||
if(g.first < 0)
|
||||
continue;
|
||||
SatID id{3,(uint32_t)g.first,0};
|
||||
/*
|
||||
if(svstats[id].completeIOD() && svstats[id].ura == 16) {
|
||||
cout<<"Skipping G"<<id.sv<<" because of URA"<<endl;
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
if(svstats[id].gpshealth) {
|
||||
// cout<<"Skipping C"<<id.sv<<" because of health"<<endl;
|
||||
continue;
|
||||
}
|
||||
sats.push_back(sat);
|
||||
}
|
||||
|
||||
|
||||
|
||||
auto cov = emitCoverage(sats);
|
||||
auto ret = nlohmann::json::array();
|
||||
|
||||
// ret =
|
||||
|
@ -1028,6 +1113,17 @@ try
|
|||
jsdatum.push_back(get<1>(longpair));
|
||||
jsdatum.push_back(get<2>(longpair));
|
||||
jsdatum.push_back(get<3>(longpair));
|
||||
jsdatum.push_back((int)(10*get<4>(longpair)));
|
||||
jsdatum.push_back((int)(10*get<5>(longpair)));
|
||||
jsdatum.push_back((int)(10*get<6>(longpair)));
|
||||
|
||||
jsdatum.push_back((int)(10*get<7>(longpair)));
|
||||
jsdatum.push_back((int)(10*get<8>(longpair)));
|
||||
jsdatum.push_back((int)(10*get<9>(longpair)));
|
||||
|
||||
jsdatum.push_back((int)(10*get<10>(longpair)));
|
||||
jsdatum.push_back((int)(10*get<11>(longpair)));
|
||||
jsdatum.push_back((int)(10*get<12>(longpair)));
|
||||
jslongvect.push_back(jsdatum);
|
||||
}
|
||||
jslatvect.push_back(latvect.first);
|
||||
|
@ -1066,12 +1162,14 @@ try
|
|||
item["best-tle-int-desig"] = s.second.tleMatch.internat;
|
||||
}
|
||||
Point p;
|
||||
getCoordinates(s.second.tow, s.second.oldBeidouMessage, &p);
|
||||
auto beidoualma = g_beidoualmakeeper.get();
|
||||
if(auto iter = beidoualma.find(s.first.sv); iter != beidoualma.end()) {
|
||||
Point almapos;
|
||||
getCoordinates(s.second.tow, iter->second.alma, &almapos);
|
||||
item["alma-dist"] = Vector(almapos, p).length()/1000.0;
|
||||
if(s.second.oldBeidouMessage.sqrtA != 0) {
|
||||
getCoordinates(s.second.tow, s.second.oldBeidouMessage, &p);
|
||||
auto beidoualma = g_beidoualmakeeper.get();
|
||||
if(auto iter = beidoualma.find(s.first.sv); iter != beidoualma.end()) {
|
||||
Point almapos;
|
||||
getCoordinates(s.second.tow, iter->second.alma, &almapos);
|
||||
item["alma-dist"] = Vector(almapos, p).length()/1000.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(s.first.gnss == 6) { // glonass
|
||||
|
@ -1341,7 +1439,7 @@ try
|
|||
else if(g_svstats[id].completeIOD()) {
|
||||
getCoordinates(g_svstats[id].tow, g_svstats[id].liveIOD(), &sat);
|
||||
}
|
||||
if(sat.x != 0) {
|
||||
if(sat.x != 0 && g_srcpos[nmm.sourceid()].pos.x != 0) {
|
||||
idb.addValue(id, nmm.localutcseconds()*1000000000, "recdata",
|
||||
{
|
||||
{"db", nmm.rd().db()},
|
||||
|
|
14
navparse.hh
14
navparse.hh
|
@ -143,7 +143,15 @@ struct SVStat
|
|||
typedef std::map<SatID, SVStat> svstats_t;
|
||||
|
||||
// a vector of pairs of latidude,vector<longitude,numsats>
|
||||
typedef vector<pair<double,vector<tuple<double, int, int, int> > > > covmap_t;
|
||||
|
||||
covmap_t emitCoverage();
|
||||
typedef vector<pair<double,vector<tuple<double, int, int, int, double, double, double, double, double, double, double, double, double> > > > covmap_t;
|
||||
covmap_t emitCoverage(const vector<Point>& sats);
|
||||
struct xDOP
|
||||
{
|
||||
double gdop{-1};
|
||||
double pdop{-1};
|
||||
double tdop{-1};
|
||||
double hdop{-1};
|
||||
double vdop{-1};
|
||||
};
|
||||
|
||||
xDOP getDOP(Point& us, vector<Point> sats);
|
||||
|
|
Loading…
Reference in New Issue