Compare commits
90 Commits
eph-store
...
spacecruft
Author | SHA1 | Date |
---|---|---|
bert hubert | c27b3466e6 | |
bert hubert | 21443e89cf | |
bert hubert | 0427adb27b | |
bert hubert | 290721f1c0 | |
bert hubert | eed416e30b | |
bert hubert | 7c9127ab73 | |
bert hubert | c10bce3a2c | |
bert hubert | f4256dac8b | |
bert hubert | 13325316de | |
bert hubert | 1837fd0bea | |
bert hubert | f5d29a3087 | |
bert hubert | 8686561404 | |
bert hubert | 95a41e9612 | |
bert hubert | 155f4c6e72 | |
bert hubert | e1b8225ddb | |
bert hubert | 025427c21d | |
bert hubert | 616e71270d | |
bert hubert | 07c07dd2f1 | |
bert hubert | aa8c3b20e3 | |
bert hubert | 3a28c48ce2 | |
bert hubert | 21df895835 | |
bert hubert | 8ddeff2a6b | |
bert hubert | 4860006fdc | |
bert hubert | 7134316efe | |
bert hubert | 30be749712 | |
bert hubert | bedcc4dc33 | |
bert hubert | 88d6773a29 | |
bert hubert | a88f6034f1 | |
bert hubert | f1746adabb | |
bert hubert | 6a515aaef0 | |
bert hubert | a291a0ff03 | |
bert hubert | 233b201a76 | |
bert hubert | 085a3e93c9 | |
bert hubert | 1feaa97638 | |
bert hubert | 83afbe125d | |
bert hubert | f971a8b3b0 | |
bert hubert | 8179fca981 | |
bert hubert | 27ee8daae2 | |
bert hubert | cfefdc6814 | |
bert hubert | 4b537c593c | |
bert hubert | ffe82326e7 | |
bert hubert | ce9aed389d | |
bert hubert | 7da22078a4 | |
bert hubert | ed8ff0e823 | |
bert hubert | ef62317f8c | |
bert hubert | 04efe69238 | |
bert hubert | 34b7acbcbc | |
berthubert | 4b8fd97dca | |
bert hubert | 37077b8296 | |
bert hubert | 5d1cc3d75f | |
bert hubert | 386ad8dd7d | |
bert hubert | bccc3eb63a | |
bert hubert | 749d8bf687 | |
bert hubert | 71086169f5 | |
bert hubert | 6a963b181c | |
bert hubert | 42c46f532f | |
berthubert | 94009f150f | |
Brad Cowie | c1ba585937 | |
bert hubert | cab1ec9594 | |
berthubert | 8eff5956b2 | |
Brad Cowie | 42810562f8 | |
Brad Cowie | 06abf58801 | |
bert hubert | b1f0ad01d1 | |
bert hubert | 5b03310e62 | |
bert hubert | 9955463c93 | |
bert hubert | aed36e1bf8 | |
bert hubert | b032867261 | |
bert hubert | e5820ec0f7 | |
bert hubert | ab436b0fb3 | |
bert hubert | 58cb1a6335 | |
bert hubert | fbf213da70 | |
bert hubert | 761a896938 | |
bert hubert | ac3494541c | |
bert hubert | 74db87012c | |
bert hubert | 85fc93bb03 | |
bert hubert | d4d97181f9 | |
bert hubert | a555418542 | |
bert hubert | 000e15e761 | |
bert hubert | b1427472cd | |
bert hubert | f96df29038 | |
bert hubert | b500182649 | |
bert hubert | 84cc20f019 | |
bert hubert | 9950905ba8 | |
bert hubert | ee1dd794aa | |
bert hubert | dc531ca4a6 | |
bert hubert | 72f789198a | |
bert hubert | edce2900bb | |
bert hubert | a15ff4da7d | |
Brad Cowie | bf66486f63 | |
Phil Crump | 4095e911d8 |
|
@ -4,15 +4,15 @@ on: [push]
|
|||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: "recursive"
|
||||
- name: deps
|
||||
run: sudo apt-get install protobuf-compiler libh2o-dev libcurl4-openssl-dev libssl-dev libprotobuf-dev libh2o-evloop-dev libwslay-dev libncurses5-dev libeigen3-dev libzstd-dev
|
||||
- name: submodules
|
||||
run: git submodule update --init --recursive
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install protobuf-compiler libh2o-dev libcurl4-openssl-dev libssl-dev libprotobuf-dev libh2o-evloop-dev libwslay-dev libncurses5-dev libeigen3-dev libzstd-dev
|
||||
- name: config
|
||||
run: echo WSLAY=-lwslay > Makefile.local
|
||||
- name: make
|
||||
|
|
|
@ -24,7 +24,7 @@ jobs:
|
|||
- name: Run buildx
|
||||
run: |
|
||||
docker buildx build \
|
||||
--tag galmon/galmon \
|
||||
--tag berthubert/galmon \
|
||||
--platform linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 \
|
||||
--output "type=registry" \
|
||||
--build-arg MAKE_FLAGS=-j1 \
|
||||
|
|
47
Dockerfile
47
Dockerfile
|
@ -1,25 +1,38 @@
|
|||
FROM ubuntu:eoan
|
||||
#
|
||||
# First stage - builder
|
||||
#
|
||||
FROM debian:10-slim AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
# This allows you to use a local Ubuntu mirror
|
||||
ARG APT_URL=
|
||||
ENV APT_URL ${APT_URL:-http://archive.ubuntu.com/ubuntu/}
|
||||
RUN sed -i "s%http://archive.ubuntu.com/ubuntu/%${APT_URL}%" /etc/apt/sources.list
|
||||
# This allows you to use a local Debian mirror
|
||||
ARG APT_URL=http://deb.debian.org/debian/
|
||||
ARG MAKE_FLAGS=-j2
|
||||
|
||||
|
||||
# Update packages and install dependencies
|
||||
RUN apt-get update && apt-get -y upgrade && apt-get -y clean
|
||||
RUN apt-get install -y protobuf-compiler libh2o-dev libcurl4-openssl-dev \
|
||||
libssl-dev libprotobuf-dev libh2o-evloop-dev libwslay-dev libeigen3-dev libzstd-dev \
|
||||
make gcc g++ git build-essential curl autoconf automake libfmt-dev libncurses5-dev \
|
||||
&& apt-get -y clean
|
||||
RUN sed -i "s%http://deb.debian.org/debian/%${APT_URL}%" /etc/apt/sources.list \
|
||||
&& apt-get update && apt-get -y upgrade \
|
||||
&& apt-get install -y protobuf-compiler libh2o-dev libcurl4-openssl-dev \
|
||||
libssl-dev libprotobuf-dev libh2o-evloop-dev libwslay-dev \
|
||||
libeigen3-dev libzstd-dev libfmt-dev libncurses-dev \
|
||||
make gcc g++ git build-essential curl autoconf automake help2man
|
||||
|
||||
# Build
|
||||
ARG MAKE_FLAGS=-j2
|
||||
ADD . /galmon/
|
||||
WORKDIR /galmon/
|
||||
RUN make $MAKE_FLAGS
|
||||
ENV PATH=/galmon:${PATH}
|
||||
ADD . /galmon-src/
|
||||
RUN cd /galmon-src/ \
|
||||
&& make $MAKE_FLAGS \
|
||||
&& prefix=/galmon make install
|
||||
|
||||
#
|
||||
# Second stage - contains just the binaries
|
||||
#
|
||||
FROM debian:10-slim
|
||||
RUN apt-get update && apt-get -y upgrade \
|
||||
&& apt-get install -y libcurl4 libssl1.1 libprotobuf17 libh2o-evloop0.13 \
|
||||
libncurses6 \
|
||||
&& apt-get -y clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=builder /galmon/ /galmon/
|
||||
ENV PATH=/galmon/bin:${PATH}
|
||||
ENV LC_ALL C.UTF-8
|
||||
WORKDIR /galmon/bin
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
#FROM ubuntu:disco
|
||||
FROM arm32v7/debian:unstable
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
# Update packages and install dependencies
|
||||
RUN apt-get update && apt-get -y upgrade && apt-get -y clean
|
||||
RUN apt-get install -y protobuf-compiler libh2o-dev libcurl4-openssl-dev \
|
||||
libssl-dev libprotobuf-dev libh2o-evloop-dev libwslay-dev libeigen3-dev libzstd-dev \
|
||||
make clang-9 git build-essential curl autoconf automake libfmt-dev libncurses5-dev \
|
||||
&& apt-get -y clean
|
||||
|
||||
# Build
|
||||
ADD . /galmon/
|
||||
WORKDIR /galmon/
|
||||
RUN make
|
||||
ENV PATH=/galmon:${PATH}
|
18
Makefile
18
Makefile
|
@ -25,7 +25,7 @@ endif
|
|||
CHEAT_ARG := $(shell ./update-git-hash-if-necessary)
|
||||
|
||||
PROGRAMS = navparse ubxtool navnexus navcat navrecv navdump testrunner navdisplay tlecatch reporter sp3feed \
|
||||
galmonmon rinreport rinjoin rtcmtool gndate
|
||||
galmonmon rinreport rinjoin rtcmtool gndate septool navmerge
|
||||
|
||||
all: navmon.pb.cc $(PROGRAMS)
|
||||
|
||||
|
@ -76,7 +76,7 @@ download-raspbian-package:
|
|||
decrypt: decrypt.o bits.o ext/fmt-6.1.2/src/format.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@
|
||||
|
||||
navparse: navparse.o ext/fmt-6.1.2/src/format.o $(H2OPP) $(SIMPLESOCKETS) minicurl.o ubx.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) tle.o navmon.o coverage.o osen.o trkmeas.o influxpush.o ${EXTRADEP} githash.o sbas.o rtcm.o
|
||||
navparse: navparse.o ext/fmt-6.1.2/src/format.o $(H2OPP) $(SIMPLESOCKETS) minicurl.o ubx.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) tle.o navmon.o coverage.o osen.o trkmeas.o influxpush.o ${EXTRADEP} githash.o sbas.o rtcm.o galileo.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -L/usr/local/opt/openssl/lib/ -lh2o-evloop -lssl -lcrypto -lz -lcurl -lprotobuf $(WSLAY)
|
||||
|
||||
reporter: reporter.o ext/fmt-6.1.2/src/format.o $(SIMPLESOCKETS) minicurl.o ubx.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) tle.o navmon.o coverage.o osen.o githash.o influxpush.o
|
||||
|
@ -94,14 +94,14 @@ galmonmon: galmonmon.o ext/fmt-6.1.2/src/format.o $(SIMPLESOCKETS) minicurl.o ub
|
|||
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl
|
||||
|
||||
|
||||
navdump: navdump.o ext/fmt-6.1.2/src/format.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o navmon.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) tle.o sp3.o osen.o trkmeas.o githash.o rinex.o sbas.o rtcm.o ${EXTRADEP}
|
||||
navdump: navdump.o ext/fmt-6.1.2/src/format.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o navmon.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) tle.o sp3.o osen.o trkmeas.o githash.o rinex.o sbas.o rtcm.o galileo.o ${EXTRADEP}
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf -lz
|
||||
|
||||
navdisplay: navdisplay.o ext/fmt-6.1.2/src/format.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o ephemeris.o navmon.o osen.o githash.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf -lncurses
|
||||
|
||||
|
||||
navnexus: navnexus.o ext/fmt-6.1.2/src/format.o $(SIMPLESOCKETS) ubx.o bits.o navmon.pb.o storage.o githash.o
|
||||
navnexus: navnexus.o ext/fmt-6.1.2/src/format.o $(SIMPLESOCKETS) bits.o navmon.pb.o storage.o githash.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf
|
||||
|
||||
navcat: navcat.o ext/fmt-6.1.2/src/format.o $(SIMPLESOCKETS) ubx.o bits.o navmon.pb.o storage.o navmon.o githash.o
|
||||
|
@ -111,6 +111,10 @@ navcat: navcat.o ext/fmt-6.1.2/src/format.o $(SIMPLESOCKETS) ubx.o bits.o navmo
|
|||
navrecv: navrecv.o ext/fmt-6.1.2/src/format.o $(SIMPLESOCKETS) navmon.pb.o storage.o githash.o zstdwrap.o navmon.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf -lzstd
|
||||
|
||||
navmerge: navmerge.o ext/fmt-6.1.2/src/format.o $(SIMPLESOCKETS) navmon.pb.o storage.o githash.o zstdwrap.o navmon.o nmmsender.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf -lzstd
|
||||
|
||||
|
||||
tlecatch: tlecatch.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) githash.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf
|
||||
|
||||
|
@ -128,8 +132,12 @@ rtcmtool: rtcmtool.o navmon.pb.o githash.o ext/fmt-6.1.2/src/format.o bits.o nm
|
|||
ubxtool: navmon.pb.o ubxtool.o ubx.o bits.o ext/fmt-6.1.2/src/format.o galileo.o gps.o beidou.o navmon.o ephemeris.o $(SIMPLESOCKETS) osen.o githash.o nmmsender.o zstdwrap.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lprotobuf -pthread -lzstd
|
||||
|
||||
septool: navmon.pb.o septool.o bits.o ext/fmt-6.1.2/src/format.o galileo.o gps.o beidou.o navmon.o ephemeris.o $(SIMPLESOCKETS) osen.o githash.o nmmsender.o zstdwrap.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lprotobuf -pthread -lzstd
|
||||
|
||||
|
||||
testrunner: navmon.pb.o testrunner.o ubx.o bits.o ext/fmt-6.1.2/src/format.o galileo.o gps.o beidou.o ephemeris.o sp3.o osen.o navmon.o rinex.o githash.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lprotobuf -lz
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lprotobuf -lz -pthread
|
||||
|
||||
gndate: gndate.o githash.o ext/fmt-6.1.2/src/format.o navmon.o
|
||||
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib
|
||||
|
|
120
README.md
120
README.md
|
@ -4,14 +4,17 @@ galileo/GPS/GLONASS/BeiDou open source monitoring. GPL3 licensed.
|
|||
|
||||
Live website: https://galmon.eu/
|
||||
|
||||
Theoretically multi-vendor, although currently only the U-blox 8 and 9
|
||||
chipsets are supported. Navilock NL-8012U receiver works really well, as
|
||||
does the U-blox evaluation kit for the 8MT. In addition, many stations have
|
||||
reported success with this very cheap [AliExpress sourced
|
||||
device](https://www.aliexpress.com/item/32816656706.html). The best and
|
||||
most high-end receiver, which does all bands, all the time, is the Ublox
|
||||
F9P, several of us use the
|
||||
[ArdusimpleRTK2B](https://www.ardusimple.com/simplertk2b/) board.
|
||||
Multi-vendor, with support for U-blox 8 and 9 chipsets and many Septentrio
|
||||
devices. Navilock NL-8012U receiver works really well, as does the U-blox
|
||||
evaluation kit for the 8MT. In addition, many stations have reported
|
||||
success with this very cheap [AliExpress sourced
|
||||
device](https://www.aliexpress.com/item/32816656706.html).
|
||||
|
||||
For ublox, there is good support for the F9P, several of us use the
|
||||
[ArdusimpleRTK2B](https://www.ardusimple.com/simplertk2b/) board. It adds
|
||||
the Galileo E5b band.
|
||||
|
||||
Septentrio devices support even more bands.
|
||||
|
||||
An annotated presentation about our project aimed at GNSS professionals can
|
||||
be found [here](https://berthub.eu/galileo/The%20galmon.eu%20project.pdf).
|
||||
|
@ -29,9 +32,10 @@ guidelines](https://github.com/ahupowerdns/galmon/blob/master/Operator.md).
|
|||
Highlights
|
||||
----------
|
||||
|
||||
* Support for Septentrio and U-blox.
|
||||
* Processes raw frames/strings/words from GPS, GLONASS, BeiDou and Galileo
|
||||
* All-band support (E1, E5b, B1I, B2I, Glonass L1, Glonass L2, GPS L1C/A)
|
||||
so far, GPS L2C and Galileo E5a pending).
|
||||
* All-band support (E1, E5a, E5b, B1I, B2I, Glonass L1, Glonass L2, GPS L1C/A)
|
||||
so far.
|
||||
* Calculate ephemeris positions
|
||||
* Comparison of ephemerides to independent SP3 data to determine SISE
|
||||
* Globally, locally, worst user location
|
||||
|
@ -68,8 +72,8 @@ Goals:
|
|||
|
||||
Works on Linux (including Raspbian Buster on Pi Zero W), OSX and OpenBSD.
|
||||
|
||||
Build locally
|
||||
-------------
|
||||
Build locally (Linux, Debian, Ubuntu)
|
||||
-------------------------------------
|
||||
|
||||
To get started, make sure you have a C++17 compiler (like g++ 8 or higher),
|
||||
git, protobuf-compiler. Then run 'make ubxtool navdump' to build the
|
||||
|
@ -79,7 +83,7 @@ To build everything, including the webserver, try:
|
|||
|
||||
```
|
||||
apt-get install protobuf-compiler libh2o-dev libcurl4-openssl-dev libssl-dev libprotobuf-dev \
|
||||
libh2o-evloop-dev libwslay-dev libncurses5-dev libeigen3-dev libzstd-dev
|
||||
libh2o-evloop-dev libwslay-dev libncurses5-dev libeigen3-dev libzstd-dev g++
|
||||
git clone https://github.com/ahupowerdns/galmon.git --recursive
|
||||
cd galmon
|
||||
make
|
||||
|
@ -92,25 +96,41 @@ library installed. If you get an error about 'wslay', do the following, and run
|
|||
echo WSLAY=-lwslay > Makefile.local
|
||||
```
|
||||
|
||||
Building on OSX
|
||||
---------------
|
||||
With thanks to a contributor from Prague. First make sure you've installed
|
||||
brew, which you can get [here](https://brew.sh/). Then do:
|
||||
|
||||
```
|
||||
brew install protobuf lzlib zstd h2o eigen
|
||||
```
|
||||
|
||||
And then:
|
||||
```
|
||||
git clone https://github.com/ahupowerdns/galmon.git --recursive
|
||||
cd galmon
|
||||
make
|
||||
```
|
||||
|
||||
Running in Docker
|
||||
-----------------
|
||||
|
||||
We publish official Docker images for galmon on
|
||||
[docker hub](https://hub.docker.com/r/faucet/faucet) for multiple architectures.
|
||||
[docker hub](https://hub.docker.com/r/berthubert/galmon) for multiple architectures.
|
||||
|
||||
To run a container with a shell in there (this will also expose a port so you
|
||||
can view the UI too and assumes a ublox GPS device too -
|
||||
you may need to tweak as necessary):
|
||||
|
||||
```
|
||||
docker run -it --rm --device=/dev/ttyACM0 -p 10000:10000 galmon/galmon
|
||||
docker run -it --rm --device=/dev/ttyACM0 -p 10000:10000 berthubert/galmon
|
||||
```
|
||||
|
||||
Running a daemonized docker container reporting data to a remote server
|
||||
might look like:
|
||||
|
||||
```
|
||||
docker run -d --restart=always --device=/dev/ttyACM0 --name=galmon galmon/galmon /galmon/ubxtool --wait --port /dev/ttyACM0 --gps --galileo --glonass --destination [server] --station [station-id] --owner [owner]
|
||||
docker run -d --restart=always --device=/dev/ttyACM0 --name=galmon berthubert/galmon ubxtool --wait --port /dev/ttyACM0 --gps --galileo --glonass --destination [server] --station [station-id] --owner [owner]
|
||||
```
|
||||
|
||||
To make your docker container update automatically you could use a tool such as
|
||||
|
@ -118,10 +138,15 @@ To make your docker container update automatically you could use a tool such as
|
|||
|
||||
Running
|
||||
-------
|
||||
|
||||
On u-blox:
|
||||
Once compiled, run for example `./ubxtool --wait --port /dev/ttyACM0
|
||||
--station 1 --stdout --galileo | ./navparse --bind [::1]:10000`
|
||||
|
||||
For Septentrio, try: `nc 192.168.1.1 29000 | ./septool --station x --stdout |
|
||||
./navparse --bind [::1]:10000`, assuming your Septentrio can be reached on
|
||||
192.168.1.1.1 and you have defined an SBF stream on port 29000. For more
|
||||
details, please see below.
|
||||
|
||||
Next up, browse to http://[::1]:10000 (or try http://localhost:10000/ and
|
||||
you should be in business. ubxtool changes (non-permanently) the
|
||||
configuration of your u-blox receiver so it emits the required frames for
|
||||
|
@ -159,6 +184,10 @@ Tooling:
|
|||
* ubxtool: can configure a u-blox 8 chipset, parses its output & will
|
||||
convert it into a protbuf stream of GNSS NAV frames + metadata
|
||||
Adds 64-bit timestamps plus origin information to each message
|
||||
* septool: ingests the Septentrio binary format (SBF) and converts it to our
|
||||
protobuf format. Supports same protocol as ubxtool.
|
||||
* rtcmtool: ingest ntripclient output, decodes RTCM messages and converts
|
||||
them to our protobuf format
|
||||
* xtool: if you have another chipset, build something that extracts NAV
|
||||
frames & metadata. Not done yet.
|
||||
* navrecv: receives GNSS NAV frames and stores them on disk, split out per
|
||||
|
@ -229,6 +258,47 @@ This also works for `navparse` for the pretty website and influx storage, `nc 12
|
|||
if you have an influxdb running on localhost with a galileo database in there.
|
||||
The default URL is http://127.0.0.1:29599/
|
||||
|
||||
Septentrio specifics
|
||||
--------------------
|
||||
Unlike `ubxtool`, our `septool` does not (re)configure your Septentrio
|
||||
device. Instead, the tool expects Septentrio Binary Format (SBF) on input,
|
||||
and that this stream includes at least the following messages:
|
||||
|
||||
* MeasEpoch
|
||||
* PVTCartesian
|
||||
|
||||
We currently parse and understand:
|
||||
* GALRawFNAV
|
||||
* GALRawINAV
|
||||
|
||||
Support will be added soon for:
|
||||
* GPSRawCA
|
||||
* GPSRawL2C
|
||||
* GPSRawL5
|
||||
* GLORawCA
|
||||
* BDSRaw
|
||||
* BDSRawB1C
|
||||
* BDSRawB2a
|
||||
|
||||
A typical invocation of `septool` looks like this:
|
||||
|
||||
```
|
||||
nc 192.168.1.1 29000 | ./septool --station x --destination galmon-eu-server.example.com
|
||||
```
|
||||
|
||||
Or to test, try:
|
||||
|
||||
```
|
||||
nc 192.168.1.1 29000 | ./septool --station x --stdout | ./navdump
|
||||
```
|
||||
|
||||
This is assuming that you can reach your Septentrio on 192.168.1.1 and that
|
||||
you have defined a TCP server stream on port 29000.
|
||||
|
||||
Septool will also accept input from a serial port or basically anything that
|
||||
can provide SBF. Please let us know if our tooling can make your life
|
||||
easier.
|
||||
|
||||
Internals
|
||||
---------
|
||||
The transport format consists of repeats of:
|
||||
|
@ -275,16 +345,26 @@ The software can interpret SP3 files, good sources:
|
|||
to have less of a delay than the ESA ESM series.
|
||||
* GBU = ultra rapid, still a few days delay, but much more recent.
|
||||
|
||||
Uncompress and concatenate all downloaded files into 'all.sp3' and run
|
||||
'navdump ' on collected protobuf, and it will output 'sp3.csv' with fit data.
|
||||
|
||||
To get SP3 GBM from GFZ Potsdam for GPS week number 2111:
|
||||
|
||||
```
|
||||
WN=2111
|
||||
lftp -c "mget ftp://ftp.gfz-potsdam.de/GNSS/products/mgnss/${WN}/gbm*sp3.Z"
|
||||
gunzip gbm*sp3.Z
|
||||
```
|
||||
|
||||
To feed data, use:
|
||||
|
||||
```
|
||||
./sp3feed --sp3src=gbm --influxdb=galileo gbm*sp3
|
||||
```
|
||||
|
||||
This will populate the sp3 tables in the database. A subsequent run of
|
||||
`reporter`, while setting the `--sp3src` parameter, will provide SP3
|
||||
deviation statistics, and fill out the `sp3delta` table in there, which
|
||||
stores deviations from the SP3 provided position, per sp3src.
|
||||
|
||||
Further interesting (ephemeris) data is on http://mgex.igs.org/IGS_MGEX_Products.php
|
||||
|
||||
RTCM
|
||||
----
|
||||
|
|
|
@ -228,7 +228,8 @@ struct BeidouMessage : GPSLikeEphemeris
|
|||
|
||||
// 2^-30 2^-50
|
||||
int a0gps, a1gps, a0gal, a1gal, a0glo, a1glo, a0utc, a1utc;
|
||||
int8_t deltaTLS;
|
||||
int8_t deltaTLS, deltaTLSF;
|
||||
uint8_t wnLSF, dn;
|
||||
|
||||
// in Beidou the offset is a0utc + SOW * a1utc
|
||||
std::pair<double, double> getUTCOffset(int tow) const
|
||||
|
@ -268,6 +269,9 @@ struct BeidouMessage : GPSLikeEphemeris
|
|||
a0utc = bbits(91, 32);
|
||||
a1utc = bbits(131, 24);
|
||||
deltaTLS = bbits(31+12+1+7, 8);
|
||||
deltaTLSF = bbits(61+6, 8);
|
||||
wnLSF = bbits(61+6+8, 8);
|
||||
dn = bbits(151+12, 8);
|
||||
}
|
||||
else {
|
||||
alma.sqrtA = bbitu(51, 24);
|
||||
|
|
2947
ext/doctest.h
2947
ext/doctest.h
File diff suppressed because it is too large
Load Diff
88
galileo.cc
88
galileo.cc
|
@ -24,3 +24,91 @@ bool getTOWFromInav(std::basic_string_view<uint8_t> inav, uint32_t *satTOW, uint
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
int GalileoMessage::parseFnav(std::basic_string_view<uint8_t> page)
|
||||
{
|
||||
const uint8_t* ptr = &page[0];
|
||||
int offset=0;
|
||||
auto gbum=[&ptr, &offset](int bits) {
|
||||
unsigned int ret = getbitu(ptr, offset, bits);
|
||||
offset += bits;
|
||||
return ret;
|
||||
};
|
||||
|
||||
auto gbsm=[&ptr, &offset](int bits) {
|
||||
int ret = getbits(ptr, offset, bits);
|
||||
offset += bits;
|
||||
return ret;
|
||||
};
|
||||
|
||||
wtype = gbum(6);
|
||||
if(wtype == 1) {
|
||||
/*int sv = */ (void)gbum(6);
|
||||
iodnav = gbum(10);
|
||||
t0c = gbum(14);
|
||||
af0 = gbsm(31);
|
||||
af1 = gbsm(21);
|
||||
af2 = gbsm(6);
|
||||
sisa = gbum(8);
|
||||
ai0 = gbum(11);
|
||||
ai1 = gbsm(11);
|
||||
ai2 = gbsm(14);
|
||||
sf1 = gbum(1);
|
||||
sf2 = gbum(1);
|
||||
sf3 = gbum(1);
|
||||
sf4 = gbum(1);
|
||||
sf5 = gbum(1);
|
||||
BGDE1E5a = gbsm(10);
|
||||
e5ahs = gbum(2);
|
||||
wn = gbum(12);
|
||||
tow = gbum(20);
|
||||
e5advs=gbum(1);
|
||||
}
|
||||
else if(wtype==2) {
|
||||
iodnav = gbum(10);
|
||||
m0 = gbsm(32);
|
||||
omegadot = gbsm(24);
|
||||
e = gbum(32);
|
||||
sqrtA = gbum(32);
|
||||
omega0 = gbsm(32);
|
||||
idot = gbsm(14);
|
||||
wn = gbum(12);
|
||||
tow = gbum(20);
|
||||
}
|
||||
else if(wtype == 3) {
|
||||
iodnav = gbum(10);
|
||||
i0 = gbsm(32);
|
||||
omega = gbsm(32);
|
||||
deltan = gbsm(16);
|
||||
cuc = gbsm(16);
|
||||
cus = gbsm(16);
|
||||
crc =gbsm(16);
|
||||
crs = gbsm(16);
|
||||
t0e = gbum(14);
|
||||
wn = gbum(12);
|
||||
tow = gbum(20);
|
||||
}
|
||||
else if(wtype == 4) {
|
||||
iodnav = gbum(10);
|
||||
cic = gbsm(16);
|
||||
cis = gbsm(16);
|
||||
|
||||
a0 = gbsm(32);
|
||||
a1 = gbsm(24);
|
||||
|
||||
dtLS= gbsm(8);
|
||||
t0t = gbum(8);
|
||||
wn0t = gbum(8);
|
||||
wnLSF = gbum(8);
|
||||
|
||||
dn = gbum(3);
|
||||
dtLSF = gbsm(8);
|
||||
|
||||
t0g = gbum(8);
|
||||
a0g = gbsm(16);
|
||||
a1g = gbsm(12);
|
||||
wn0g = gbum(6);
|
||||
tow = gbum(20);
|
||||
}
|
||||
return wtype;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ struct GalileoMessage : GPSLikeEphemeris
|
|||
return wtype;
|
||||
}
|
||||
|
||||
int parseFnav(std::basic_string_view<uint8_t> page);
|
||||
|
||||
uint8_t sparetime{0};
|
||||
uint16_t wn{0};
|
||||
uint32_t tow{0};
|
||||
|
@ -63,13 +65,13 @@ struct GalileoMessage : GPSLikeEphemeris
|
|||
{
|
||||
}
|
||||
|
||||
uint8_t e5bhs{0}, e1bhs{0};
|
||||
uint8_t e5ahs{0}, e5bhs{0}, e1bhs{0};
|
||||
uint8_t gpshealth{0};
|
||||
uint16_t ai0{0};
|
||||
int16_t ai1{0}, ai2{0};
|
||||
bool sf1{0}, sf2{0}, sf3{0}, sf4{0}, sf5{0};
|
||||
int BGDE1E5a{0}, BGDE1E5b{0};
|
||||
bool e5bdvs{false}, e1bdvs{false};
|
||||
bool e5advs{false}, e5bdvs{false}, e1bdvs{false};
|
||||
bool disturb1{false}, disturb2{false}, disturb3{false}, disturb4{false}, disturb5{false};
|
||||
|
||||
//
|
||||
|
@ -224,7 +226,7 @@ struct GalileoMessage : GPSLikeEphemeris
|
|||
{
|
||||
int dw = (int)(wn%64) - (int)(wn0g%64);
|
||||
if(dw > 31)
|
||||
dw = 31- dw;
|
||||
dw = dw - 64;
|
||||
int delta = dw*7*86400 + tow - getT0g(); // NOT ephemeris age tricks
|
||||
|
||||
// 2^-35 2^-51 3600
|
||||
|
|
47
galmonmon.cc
47
galmonmon.cc
|
@ -1,4 +1,4 @@
|
|||
|
||||
#include <optional>
|
||||
#include "minicurl.hh"
|
||||
#include <iostream>
|
||||
#include "navmon.hh"
|
||||
|
@ -45,7 +45,6 @@ public:
|
|||
std::optional<string> reportState(string_view thing, string_view name, var_t state, const std::string& state_text="");
|
||||
std::optional<string> getState(string_view thing, string_view name);
|
||||
|
||||
|
||||
std::optional<string> getPrevState(string_view thing, string_view name);
|
||||
|
||||
struct State
|
||||
|
@ -166,23 +165,7 @@ std::optional<string> StateKeeper::reportState(string_view thing, string_view na
|
|||
|
||||
|
||||
StateKeeper g_sk;
|
||||
#if 0
|
||||
static std::string string_replace(const std::string& str, const std::string& match,
|
||||
const std::string& replacement, unsigned int max_replacements = UINT_MAX)
|
||||
{
|
||||
size_t pos = 0;
|
||||
std::string newstr = str;
|
||||
unsigned int replacements = 0;
|
||||
while ((pos = newstr.find(match, pos)) != std::string::npos
|
||||
&& replacements < max_replacements)
|
||||
{
|
||||
newstr.replace(pos, match.length(), replacement);
|
||||
pos += replacement.length();
|
||||
replacements++;
|
||||
}
|
||||
return newstr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void sendTweet(const string& tweet)
|
||||
{
|
||||
string etweet = tweet;
|
||||
|
@ -197,8 +180,8 @@ int main(int argc, char **argv)
|
|||
{
|
||||
MiniCurl mc;
|
||||
MiniCurl::MiniCurlHeaders mch;
|
||||
// string url="https://galmon.eu/svs.json";
|
||||
string url="http://[::1]:29599/";
|
||||
string url="https://galmon.eu/";
|
||||
// string url="http://[::1]:29599/";
|
||||
bool doVERSION{false};
|
||||
|
||||
CLI::App app(program);
|
||||
|
@ -222,6 +205,7 @@ int main(int argc, char **argv)
|
|||
g_sk.setBoolNames("health", "healthy", "unhealthy");
|
||||
g_sk.setBoolNames("eph-too-old", "ephemeris fresh", "ephemeris aged");
|
||||
g_sk.setBoolNames("silent", "observed", "not observed");
|
||||
g_sk.setBoolNames("osnma", "OFF", "ON");
|
||||
|
||||
std::variant<bool, string> tst;
|
||||
|
||||
|
@ -273,7 +257,8 @@ int main(int argc, char **argv)
|
|||
|
||||
res = mc.getURL(url+"svs.json");
|
||||
j = nlohmann::json::parse(res);
|
||||
bool first=true;
|
||||
bool first=true;
|
||||
bool globalOsnma=false;
|
||||
for(const auto& sv : j) {
|
||||
if(!sv.count("gnssid") || !sv.count("fullName") || !sv.count("sigid")) {
|
||||
cout<<"Skipping "<< sv.count("gnssid") <<", "<< sv.count("fullName") <<", " <<sv.count("sigid") <<endl;
|
||||
|
@ -295,7 +280,8 @@ int main(int argc, char **argv)
|
|||
// cout<<"Skipping "<<fullName<<" in loop: "<<sv.count("healthissue")<<", "<<sv.count("eph-age-m") << ", "<<sv.count("perrecv")<<endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
for(const auto& recv : sv["perrecv"]) {
|
||||
if(!recv.count("last-seen-s")) {
|
||||
cout<<"Missing last-seen-s"<<endl;
|
||||
|
@ -305,9 +291,11 @@ int main(int argc, char **argv)
|
|||
numfresh++;
|
||||
if((int)recv["last-seen-s"] < 3600)
|
||||
notseen=false;
|
||||
|
||||
}
|
||||
if(sv.count("osnma") && sv["osnma"]==true)
|
||||
globalOsnma |= 1;
|
||||
|
||||
|
||||
auto healthchange = g_sk.reportState(fullName, "health", sv["healthissue"]!=0);
|
||||
std::optional<string> tooOldChange;
|
||||
if(gnssid == 2)
|
||||
|
@ -410,6 +398,17 @@ int main(int argc, char **argv)
|
|||
cout<<humanTimeNow() <<" " << tweet << endl;
|
||||
}
|
||||
}
|
||||
|
||||
auto osnmachange = g_sk.reportState("global", "osnma", globalOsnma);
|
||||
if(osnmachange) {
|
||||
string tweet= "Galileo OSNMA new state: "+*osnmachange;
|
||||
cout<<humanTimeNow()<< " " <<tweet<<endl;
|
||||
if(doTweet) {
|
||||
sendTweet(tweet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cout<<".";
|
||||
cout.flush();
|
||||
}
|
||||
|
|
|
@ -110,16 +110,16 @@ static double passedMsec(const Clock::time_point& then, const Clock::time_point&
|
|||
return std::chrono::duration_cast<std::chrono::microseconds>(now - then).count()/1000.0;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static double passedMsec(const Clock::time_point& then)
|
||||
{
|
||||
return passedMsec(then, Clock::now());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
double getCoordinates(double tow, const GlonassMessage& eph, Point* p)
|
||||
{
|
||||
auto start = Clock::now();
|
||||
// auto start = Clock::now();
|
||||
|
||||
double y0[6] = {ldexp(eph.x, -11), ldexp(eph.y, -11), ldexp(eph.z, -11),
|
||||
ldexp(eph.dx, -20), ldexp(eph.dy, -20), ldexp(eph.dz, -20)};
|
||||
|
@ -137,7 +137,7 @@ double getCoordinates(double tow, const GlonassMessage& eph, Point* p)
|
|||
rk4step (A, y0, h);
|
||||
|
||||
*p = Point (1E3*y0[0], 1E3*y0[1], 1E3*y0 [2]);
|
||||
static double total=0;
|
||||
// static double total=0;
|
||||
// cout<<"Took: "<<(total+=passedMsec(start))<<" ms" <<endl;
|
||||
return 0;
|
||||
}
|
||||
|
|
28
gndate.cc
28
gndate.cc
|
@ -7,16 +7,21 @@ extern const char* g_gitHash;
|
|||
using namespace std;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
string program("gndate");
|
||||
CLI::App app(program);
|
||||
string date;
|
||||
bool doGPSWN{false}, doGALWN{false}, doVERSION{false}, doUTC{false};
|
||||
int galwn{-1};
|
||||
bool doProgOutput{false};
|
||||
bool doGPSWN{false}, doGALWN{false}, doBEIDOUWN{false}, doVERSION{false}, doUTC{false};
|
||||
app.add_flag("--version", doVERSION, "show program version and copyright");
|
||||
app.add_option("--date,-d", date, "yyyy-mm-dd hh:mm");
|
||||
app.add_option("--date,-d", date, "yyyy-mm-dd hh:mm[:ss] hh:mm yyyymmdd hhmm");
|
||||
app.add_option("--date-gal-wn", galwn, "Give data for this Galileo week number");
|
||||
app.add_flag("--utc,-u", doUTC, "Interpret --date,-d as UTC");
|
||||
app.add_flag("--gps-wn", doGPSWN, "Print GPS week number");
|
||||
app.add_flag("--gal-wn", doGALWN, "Print GPS week number");
|
||||
app.add_flag("--prog-output", doProgOutput, "Modulate some date formats for use as parameters to programs");
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch(const CLI::Error &e) {
|
||||
|
@ -40,6 +45,15 @@ int main(int argc, char** argv)
|
|||
|
||||
int wn, tow;
|
||||
|
||||
if(galwn >= 0) {
|
||||
time_t week=utcFromGST(galwn, 0);
|
||||
if(doProgOutput)
|
||||
cout<<influxTime(week) << endl;
|
||||
else
|
||||
cout<<humanTime(week)<< " - " << humanTime(week+7*86400) << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(doGPSWN) {
|
||||
getGPSDateFromUTC(now, wn, tow);
|
||||
cout<<wn<<endl;
|
||||
|
@ -48,10 +62,20 @@ int main(int argc, char** argv)
|
|||
getGalDateFromUTC(now, wn, tow);
|
||||
cout<<wn<<endl;
|
||||
}
|
||||
else if(doBEIDOUWN) {
|
||||
getBeiDouDateFromUTC(now, wn, tow);
|
||||
cout<<wn<<endl;
|
||||
}
|
||||
else {
|
||||
getGPSDateFromUTC(now, wn, tow);
|
||||
cout<<"GPS Week Number (non-wrapped): "<< wn << ", tow " << tow << endl;
|
||||
getGalDateFromUTC(now, wn, tow);
|
||||
cout<<"Galileo Week Number: "<< wn << ", tow " << tow << endl;
|
||||
getBeiDouDateFromUTC(now, wn, tow);
|
||||
cout<<"BeiDou Week Number: "<< wn << ", sow " << tow << endl;
|
||||
|
||||
}
|
||||
}
|
||||
catch(exception& e) {
|
||||
cerr<<"Error: "<<e.what()<<endl;
|
||||
}
|
||||
|
|
4
gps.cc
4
gps.cc
|
@ -96,8 +96,12 @@ int GPSState::parseGPSMessage(std::basic_string_view<uint8_t> cond, uint8_t* pag
|
|||
|
||||
t0t = getbitu(&cond[0], 7*24 + 8, 8) * 4096; // WE SCALE THIS FOR THE USER!
|
||||
wn0t = getbitu(&cond[0], 7*24 + 16, 8);
|
||||
|
||||
dtLS = getbits(&cond[0], 8*24, 8);
|
||||
wnLSF= getbitu(&cond[0], 8*24 + 8, 8);
|
||||
dn = getbitu(&cond[0], 8*24 + 16, 8);
|
||||
dtLSF = getbits(&cond[0], 9*24, 8);
|
||||
|
||||
|
||||
// cerr<<": a0: "<<a0<<", a1: "<<a1<<", t0t: "<< t0t * (1<<12) <<", wn0t: "<< wn0t<<", rough offset: "<<ldexp(a0, -30)<<endl;
|
||||
// cerr<<"deltaTLS: "<< (int)dtLS<<", post "<< (int)dtLSF<<endl;
|
||||
|
|
|
@ -14,7 +14,7 @@ function maketable(str, arr)
|
|||
enter().
|
||||
append("tr");
|
||||
|
||||
var columns = ["sv", "best-tle", "iod", "eph-age-m", "latest-disco", "time-disco", "sisa", "health", "alma-dist", "delta-utc", "sources", "db", "rtcm-eph-delta-cm","rtcm-clock-dclock0", "prres", "elev", "last-seen-s"];
|
||||
var columns = ["sv", "best-tle", "iod", "eph-age-m", "latest-disco", "time-disco", "sisa", "health", "alma-dist", "osnma", "sources", "db", "rtcm-eph-delta-cm","rtcm-clock-dclock0", "prres", "elev", "last-seen-s"];
|
||||
|
||||
// append the header row
|
||||
thead.append("tr")
|
||||
|
@ -61,7 +61,7 @@ function maketable(str, arr)
|
|||
else if(row["gnssid"] == 6)
|
||||
img='ext/glo.png';
|
||||
|
||||
ret.value = '<img width="16" height="16" src="https://ds9a.nl/tmp/'+ img +'"/>';
|
||||
ret.value = '<img width="16" height="16" src="https://berthub.eu/tmp/'+ img +'"/>';
|
||||
// ret.value="";
|
||||
ret.value += " <a href='sv.html?gnssid="+row.gnssid+"&sv="+row.svid+"&sigid="+row.sigid+"'>"+row.sv+"</a>";
|
||||
}
|
||||
|
@ -130,6 +130,13 @@ function maketable(str, arr)
|
|||
else if(column == "norad") {
|
||||
ret.value = row["best-tle-norad"];
|
||||
}
|
||||
else if(column == "osnma" && row["osnma"] != null) {
|
||||
if(row["osnma"]==true)
|
||||
ret.value="ON!";
|
||||
else
|
||||
ret.value="off";
|
||||
}
|
||||
|
||||
else if(column == "delta-utc" && row["delta-utc"] != null) {
|
||||
ret.value = row["delta-utc"]+'<span class="CellComment">a0: '+row["a0"]+'<br/>a1: '+row["a1"]+'<br/>wn0t: ' + row["wn0t"]+'<br/>t0t: '+row["t0t"]+'</span>';
|
||||
ret.Class = 'CellWithComment';
|
||||
|
@ -288,6 +295,8 @@ function updateSats()
|
|||
let wantIt = false;
|
||||
if(d3.select("#GalE1").property("checked") && arr[n].gnssid==2 && arr[n].sigid == 1)
|
||||
wantIt = true;
|
||||
if(d3.select("#GalE5a").property("checked") && arr[n].gnssid==2 && arr[n].sigid == 6)
|
||||
wantIt = true;
|
||||
if(d3.select("#GalE5b").property("checked") && arr[n].gnssid==2 && arr[n].sigid == 5)
|
||||
wantIt = true;
|
||||
if(d3.select("#GPSL1CA").property("checked") && arr[n].gnssid==0 && arr[n].sigid == 0)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<div class="centered">
|
||||
<hr/>
|
||||
<input type="checkbox" id="GalE1" onclick="updateSats();"> <label for="GalE1">Galileo E1</label>
|
||||
<input type="checkbox" id="GalE5a" onclick="updateSats();"> <label for="GalE5a">Galileo E5a</label>
|
||||
<input type="checkbox" id="GalE5b" onclick="updateSats();"> <label for="GalE5b">Galileo E5b</label>
|
||||
<input type="checkbox" id="BeiDouB1I" onclick="updateSats();"> <label for="BeiDouB1I">BeiDou B1I</label>
|
||||
<input type="checkbox" id="BeiDouB2I" onclick="updateSats();"> <label for="BeiDouB2I">BeiDou B2I</label>
|
||||
|
@ -60,10 +61,11 @@
|
|||
The official Galileo constellation status can be found on the <a href="https://www.gsc-europa.eu/system-status/Constellation-Information">European GNSS Service Centre page</a>, which also lists "NAGUs", <a href="https://www.gsc-europa.eu/system-status/user-notifications">notifications about outages or changes</a>.
|
||||
</p>
|
||||
<p>
|
||||
Official GLONASS status can be found on <a href="https://www.glonass-iac.ru/en/GLONASS/">this page</a> from the Russian Information and Analysis Center for Positioning, Navigation and Timing.
|
||||
Official GLONASS status can be found on <a href="https://www.glonass-iac.ru/en/sostavOG/">this page</a> from the Russian Information and Analysis Center for Positioning, Navigation and Timing.
|
||||
</p>
|
||||
<p>
|
||||
Sadly reduced status updates on GPS can be found on <a href="https://www.navcen.uscg.gov/?Do=constellationStatus">this page</a> from the US Department of Homeland Security.
|
||||
Status updates on GPS can be found on <a
|
||||
href="https://www.navcen.uscg.gov/gps-constellation">this page</a>.
|
||||
</p>
|
||||
<p>
|
||||
Information on the status of BeiDou can be found on <a href="http://www.csno-tarc.cn/system/constellation&ce=english">this page</a> from the Chinese Test and Assessment Research Center of China Satellite Navigation Office.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
Last update: <span id="freshness"></span>. More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found <a href="https://github.com/ahupowerdns/galmon/blob/master/README.md#galmon">here</a>. Live map <a href="geo">here!</a>. Contact <a href="https://ds9a.nl/">me</a> if you want access to the Grafana dashboard.<br/>
|
||||
Last update: <span id="freshness"></span>. More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found <a href="https://github.com/ahupowerdns/galmon/blob/master/README.md#galmon">here</a>. Live map <a href="geo">here!</a>. Contact <a href="https://berthub.eu/">me</a> if you want access to the Grafana dashboard.<br/>
|
||||
<table>
|
||||
<tr style="background: #FFF">
|
||||
<td style="vertical-align:top">
|
||||
|
@ -27,7 +27,7 @@
|
|||
It is very much a work in progress, and will not be available at all times. Extremely rough code is on
|
||||
<a href="https://github.com/ahuPowerDNS/galmon">GitHub</a>.
|
||||
|
||||
Some technical detail behind this setup can be found in <a href="https://ds9a.nl/articles/posts/galileo-notes/">this post</a>.
|
||||
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>.
|
||||
|
||||
For updates, follow <a href="https://twitter.com/GalileoSats">@GalileoSats</a> on Twitter, or join us on our IRC channel (chat) via the
|
||||
<a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.
|
||||
|
|
|
@ -85,7 +85,6 @@ function componentDidMount() {
|
|||
.text(function(d) { return d + "°"; });
|
||||
|
||||
sats.select('g.satellites').remove();
|
||||
console.log(gnss_position);
|
||||
|
||||
let points = sats
|
||||
.insert("g")
|
||||
|
@ -259,7 +258,6 @@ function update()
|
|||
|
||||
}
|
||||
|
||||
console.log(window.location.href);
|
||||
var url = new URL(window.location.href);
|
||||
observer = url.searchParams.get("observer");
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
Last update: <span id="freshness"></span>. More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found <a href="https://github.com/ahupowerdns/galmon/blob/master/README.md#galmon">here</a>. Live map <a href="geo">here!</a>. Contact <a href="https://ds9a.nl/">me</a> if you want access to the Grafana dashboard.<br/>
|
||||
Last update: <span id="freshness"></span>. More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found <a href="https://github.com/ahupowerdns/galmon/blob/master/README.md#galmon">here</a>. Live map <a href="geo">here!</a>. Contact <a href="https://berthub.eu/">me</a> if you want access to the Grafana dashboard.<br/>
|
||||
<table id="galileo"></table>
|
||||
<p>
|
||||
This table shows live output from four Galileo/GPS/BeiDou/GLONASS receivers hosted in Nootdorp, The Netherlands and California, United States.
|
||||
It is very much a work in progress, and will not be available at all times. Extremely rough code is on
|
||||
<a href="https://github.com/ahuPowerDNS/galmon">GitHub</a>.
|
||||
|
||||
Some technical detail behind this setup can be found in <a href="https://ds9a.nl/articles/posts/galileo-notes/">this post</a>.
|
||||
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>.
|
||||
|
||||
For updates, follow <a href="https://twitter.com/GalileoSats">@GalileoSats</a> on Twitter, or join us on our IRC channel (chat) via the
|
||||
<a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
Last update: <span id="freshness"></span>. More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found <a href="https://github.com/ahupowerdns/galmon/blob/master/README.md#galmon">here</a>. Live map <a href="geo">here!</a>. Contact <a href="https://ds9a.nl/">me</a> if you want access to the Grafana dashboard.<br/>
|
||||
Last update: <span id="freshness"></span>. More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found <a href="https://github.com/ahupowerdns/galmon/blob/master/README.md#galmon">here</a>. Live map <a href="geo">here!</a>. Contact <a href="https://berthub.eu/">me</a> if you want access to the Grafana dashboard.<br/>
|
||||
<table id="galileo"></table>
|
||||
<p>
|
||||
This table shows live output from four Galileo/GPS/BeiDou/GLONASS receivers hosted in Nootdorp, The Netherlands and California, United States.
|
||||
It is very much a work in progress, and will not be available at all times. Extremely rough code is on
|
||||
<a href="https://github.com/ahuPowerDNS/galmon">GitHub</a>.
|
||||
|
||||
Some technical detail behind this setup can be found in <a href="https://ds9a.nl/articles/posts/galileo-notes/">this post</a>.
|
||||
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>.
|
||||
|
||||
For updates, follow <a href="https://twitter.com/GalileoSats">@GalileoSats</a> on Twitter, or join us on our IRC channel (chat) via the
|
||||
<a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<table id="sbasstale"></table>
|
||||
<p>
|
||||
|
||||
Some technical detail behind this setup can be found in <a href="https://ds9a.nl/articles/posts/galileo-notes/">this post</a>.
|
||||
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>.
|
||||
|
||||
For updates, follow <a href="https://twitter.com/GalileoSats">@GalileoSats</a> on Twitter, or join us on our IRC channel (chat) via the
|
||||
<a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.
|
||||
|
|
|
@ -75,7 +75,7 @@ function maketable(str, arr)
|
|||
|
||||
ret.value = sbas + " ";
|
||||
if(img != "")
|
||||
ret.value += '<img width="16" height="16" src="https://ds9a.nl/tmp/'+ img +'"/>';
|
||||
ret.value += '<img width="16" height="16" src="https://berthub.eu/tmp/'+ img +'"/>';
|
||||
else
|
||||
ret.value += "";
|
||||
// ret.value="";
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<table id="sbasstale"></table>
|
||||
<p>
|
||||
|
||||
Some technical detail behind this setup can be found in <a href="https://ds9a.nl/articles/posts/galileo-notes/">this post</a>.
|
||||
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>.
|
||||
|
||||
For updates, follow <a href="https://twitter.com/GalileoSats">@GalileoSats</a> on Twitter, or join us on our IRC channel (chat) via the
|
||||
<a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
Last update: <span id="freshness"></span>. More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found <a href="https://github.com/ahupowerdns/galmon/blob/master/README.md#galmon">here</a>. Live map <a href="geo">here!</a>. Contact <a href="https://ds9a.nl/">me</a> if you want access to the Grafana dashboard.<br/>
|
||||
Last update: <span id="freshness"></span>. More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found <a href="https://github.com/ahupowerdns/galmon/blob/master/README.md#galmon">here</a>. Live map <a href="geo">here!</a>. Contact <a href="https://berthub.eu/">me</a> if you want access to the Grafana dashboard.<br/>
|
||||
<table id="galileo"></table>
|
||||
<p>
|
||||
This table shows live output from four Galileo/GPS/BeiDou/GLONASS receivers hosted in Nootdorp, The Netherlands and California, United States.
|
||||
It is very much a work in progress, and will not be available at all times. Extremely rough code is on
|
||||
<a href="https://github.com/ahuPowerDNS/galmon">GitHub</a>.
|
||||
|
||||
Some technical detail behind this setup can be found in <a href="https://ds9a.nl/articles/posts/galileo-notes/">this post</a>.
|
||||
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>.
|
||||
|
||||
For updates, follow <a href="https://twitter.com/GalileoSats">@GalileoSats</a> on Twitter, or join us on our IRC channel (chat) via the
|
||||
<a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.
|
||||
|
|
28
influxdb.md
28
influxdb.md
|
@ -131,9 +131,37 @@ Observer and SV measurements:
|
|||
* ele: calculated elevation for SV from this receiver
|
||||
* prres: pseudorange residual according to receiver
|
||||
* qi: 0-7, quality indicator according to receiver
|
||||
* used: did the receiver use this SV?
|
||||
* ubx\_jamming
|
||||
* noise\_per\_ms: the Ublox noisePerMS field
|
||||
* agccnt: the Ublox automatic gain correction "count"
|
||||
* jamind: The Ublox jamming indicator
|
||||
* flag: The Ublox jamming flag field
|
||||
|
||||
Fed by separate tool:
|
||||
|
||||
SP3 design, tagged by GNSSID, SV:
|
||||
* sp3\_data:
|
||||
* x
|
||||
* y
|
||||
* z
|
||||
* clk
|
||||
* provider
|
||||
|
||||
ephemeris, tagged by GNSSID, SV, SIGID:
|
||||
* active-ephemeris
|
||||
* all the raw parameters
|
||||
|
||||
GDOP/PDOP stats?
|
||||
* covdop
|
||||
* lat
|
||||
* lon
|
||||
* cov5
|
||||
* cov10
|
||||
* cov20
|
||||
* hdop5
|
||||
* hdop10
|
||||
* hdop20
|
||||
|
||||
etc
|
||||
|
||||
|
|
113
navcat.cc
113
navcat.cc
|
@ -26,7 +26,7 @@ using namespace std;
|
|||
|
||||
extern const char* g_gitHash;
|
||||
|
||||
|
||||
// get all stations (numerical) from a directory
|
||||
vector<uint64_t> getSources(string_view dirname)
|
||||
{
|
||||
DIR *dir = opendir(&dirname[0]);
|
||||
|
@ -54,8 +54,13 @@ vector<uint64_t> getSources(string_view dirname)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool operator==(const timespec& a, const timespec& b)
|
||||
{
|
||||
return a.tv_sec == b.tv_sec && a.tv_nsec && b.tv_nsec;
|
||||
}
|
||||
|
||||
void sendProtobuf(string_view dir, vector<uint64_t> stations, time_t startTime, time_t stopTime=0)
|
||||
// send protobuf data from the named directories and stations, between start and stoptime
|
||||
void sendProtobuf(const vector<string>& dirs, vector<uint64_t> stations, time_t startTime, time_t stopTime=0)
|
||||
{
|
||||
timespec start;
|
||||
start.tv_sec = startTime;
|
||||
|
@ -66,44 +71,54 @@ void sendProtobuf(string_view dir, vector<uint64_t> stations, time_t startTime,
|
|||
vector<pair<timespec,string> > rnmms;
|
||||
|
||||
for(;;) {
|
||||
cerr<<"Gathering data"<<endl;
|
||||
vector<uint64_t> srcs = stations.empty() ? getSources(dir) : stations;
|
||||
rnmms.clear();
|
||||
for(const auto& src: srcs) {
|
||||
string fname = getPath(dir, start.tv_sec, src);
|
||||
FILE* fp = fopen(fname.c_str(), "r");
|
||||
if(!fp)
|
||||
continue;
|
||||
uint32_t offset= fpos[fname];
|
||||
if(fseek(fp, offset, SEEK_SET) < 0) {
|
||||
cerr<<"Error seeking: "<<strerror(errno) <<endl;
|
||||
fclose(fp);
|
||||
continue;
|
||||
}
|
||||
// cerr <<"Seeked to position "<<fpos[fname]<<" of "<<fname<<endl;
|
||||
|
||||
uint32_t looked=0;
|
||||
string msg;
|
||||
struct timespec ts;
|
||||
while(getRawNMM(fp, ts, msg, offset)) {
|
||||
// don't drop data that is only 5 seconds too old
|
||||
if(make_pair(ts.tv_sec + 5, ts.tv_nsec) >= make_pair(start.tv_sec, start.tv_nsec)) {
|
||||
rnmms.push_back({ts, msg});
|
||||
for(const auto& dir : dirs) {
|
||||
cerr<<"Gathering data from "<<humanTime(start.tv_sec)<<" from "<<dir<<".. ";
|
||||
|
||||
vector<uint64_t> srcs = stations.empty() ? getSources(dir) : stations;
|
||||
int count=0;
|
||||
for(const auto& src: srcs) {
|
||||
string fname = getPath(dir, start.tv_sec, src);
|
||||
FILE* fp = fopen(fname.c_str(), "r");
|
||||
if(!fp)
|
||||
continue;
|
||||
uint32_t offset= fpos[fname];
|
||||
if(fseek(fp, offset, SEEK_SET) < 0) {
|
||||
cerr<<"Error seeking: "<<strerror(errno) <<endl;
|
||||
fclose(fp);
|
||||
continue;
|
||||
}
|
||||
++looked;
|
||||
}
|
||||
// cerr<<"Harvested "<<rnmms.size()<<" events out of "<<looked<<endl;
|
||||
fpos[fname]=offset;
|
||||
fclose(fp);
|
||||
}
|
||||
// cerr <<"Seeked to position "<<fpos[fname]<<" of "<<fname<<endl;
|
||||
|
||||
string msg;
|
||||
struct timespec ts;
|
||||
while(getRawNMM(fp, ts, msg, offset)) {
|
||||
// don't drop data that is only 5 seconds too old
|
||||
if(make_pair(ts.tv_sec + 5, ts.tv_nsec) >= make_pair(start.tv_sec, start.tv_nsec)) {
|
||||
rnmms.push_back({ts, msg});
|
||||
++count;
|
||||
}
|
||||
}
|
||||
// cerr<<"Harvested "<<rnmms.size()<<" events out of "<<looked<<endl;
|
||||
|
||||
cerr<<"Sorting data"<<endl;
|
||||
fpos[fname]=offset;
|
||||
fclose(fp);
|
||||
}
|
||||
cerr<<" added "<<count<<endl;
|
||||
}
|
||||
// cerr<<"Sorting data"<<endl;
|
||||
sort(rnmms.begin(), rnmms.end(), [](const auto& a, const auto& b)
|
||||
{
|
||||
return std::tie(a.first.tv_sec, a.first.tv_nsec)
|
||||
< std::tie(b.first.tv_sec, b.first.tv_nsec);
|
||||
});
|
||||
cerr<<"Sending data"<<endl;
|
||||
|
||||
auto newend=unique(rnmms.begin(), rnmms.end());
|
||||
cerr<<"Removed "<<rnmms.end() - newend <<" duplicates, ";
|
||||
|
||||
rnmms.erase(newend, rnmms.end());
|
||||
cerr<<"sending data"<<endl;
|
||||
unsigned int count=0;
|
||||
for(const auto& nmm: rnmms) {
|
||||
if(nmm.first.tv_sec > stopTime)
|
||||
break;
|
||||
|
@ -113,8 +128,9 @@ void sendProtobuf(string_view dir, vector<uint64_t> stations, time_t startTime,
|
|||
buf += nmm.second;
|
||||
//fwrite(buf.c_str(), 1, buf.size(), stdout);
|
||||
writen2(1, buf.c_str(), buf.size());
|
||||
++count;
|
||||
}
|
||||
cerr<<"Done sending"<<endl;
|
||||
cerr<<"Done sending " << count<<" messages"<<endl;
|
||||
if(3600 + start.tv_sec - (start.tv_sec%3600) < stopTime)
|
||||
start.tv_sec = 3600 + start.tv_sec - (start.tv_sec%3600);
|
||||
else {
|
||||
|
@ -129,14 +145,16 @@ int main(int argc, char** argv)
|
|||
bool doVERSION{false};
|
||||
|
||||
CLI::App app(program);
|
||||
string beginarg, endarg, storage;
|
||||
app.add_option("--storage,-s", storage, "Location of storage files");
|
||||
string beginarg, endarg;
|
||||
vector<string> storages;
|
||||
int galwn{-1};
|
||||
app.add_option("--storage,-s", storages, "Locations of storage files");
|
||||
vector<uint64_t> stations;
|
||||
app.add_flag("--version", doVERSION, "show program version and copyright");
|
||||
app.add_option("--begin,-b", beginarg, "Begin time (2020-01-01 00:00, or 12:30)")->required();
|
||||
app.add_option("--begin,-b", beginarg, "Begin time (2020-01-01 00:00, or 12:30)");
|
||||
app.add_option("--end,-e", endarg, "End time. Now if omitted");
|
||||
app.add_option("--stations", stations, "only send data from listed stations");
|
||||
|
||||
app.add_option("--gal-wn", galwn, "Galileo week number to report on");
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
|
||||
|
@ -145,9 +163,19 @@ int main(int argc, char** argv)
|
|||
exit(0);
|
||||
}
|
||||
|
||||
time_t startTime = parseTime(beginarg);
|
||||
|
||||
time_t stopTime = endarg.empty() ? time(0) : parseTime(endarg);
|
||||
time_t startTime, stopTime;
|
||||
if(galwn >=0) {
|
||||
startTime=utcFromGST(galwn, 0);
|
||||
stopTime=startTime + 7*86400;
|
||||
}
|
||||
else if(!beginarg.empty()) {
|
||||
startTime = parseTime(beginarg);
|
||||
stopTime = endarg.empty() ? time(0) : parseTime(endarg);
|
||||
}
|
||||
else {
|
||||
cerr<<"No time range specified, use -b or --gal-wn"<<endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
cerr<<"Emitting from "<<humanTime(startTime) << " to " << humanTime(stopTime) << endl;
|
||||
if(!stations.empty()) {
|
||||
|
@ -155,7 +183,6 @@ int main(int argc, char** argv)
|
|||
for(const auto& s : stations)
|
||||
cerr<<" "<<s;
|
||||
cerr<<endl;
|
||||
}
|
||||
sendProtobuf(storage, stations, startTime, stopTime);
|
||||
|
||||
}
|
||||
sendProtobuf(storages, stations, startTime, stopTime);
|
||||
}
|
||||
|
|
171
navdump.cc
171
navdump.cc
|
@ -25,6 +25,7 @@
|
|||
#include "tle.hh"
|
||||
#include "sp3.hh"
|
||||
#include "ubx.hh"
|
||||
#include <optional>
|
||||
#include <unistd.h>
|
||||
#include "sbas.hh"
|
||||
#include "version.hh"
|
||||
|
@ -257,12 +258,20 @@ try
|
|||
bool doReceptionData{false};
|
||||
bool doRFData{true};
|
||||
bool doObserverPosition{false};
|
||||
bool doObserverDetails{false};
|
||||
bool doTimeOffsets{false};
|
||||
bool doVERSION{false};
|
||||
string rinexfname;
|
||||
string osnmafname;
|
||||
app.add_option("--svs", svpairs, "Listen to specified svs. '0' = gps, '2' = Galileo, '2,1' is E01");
|
||||
app.add_option("--stations", stations, "Listen to specified stations.");
|
||||
app.add_option("--positions,-p", doObserverPosition, "Print out observer positions (or not)");
|
||||
app.add_option("--rfdata,-r", doRFData, "Print out RF data (or not)");
|
||||
app.add_option("--observerdetails,-o", doObserverDetails, "Print out observer detail data (or not)");
|
||||
app.add_option("--timeoffsets,-t", doTimeOffsets, "Print out timeoffset data (or not)");
|
||||
app.add_option("--recdata", doReceptionData, "Print out reception data (or not)");
|
||||
app.add_option("--rinex", rinexfname, "If set, emit ephemerides to this filename");
|
||||
app.add_option("--osnma", osnmafname, "If set, emit OSNMA CSV to this filename");
|
||||
app.add_flag("--version", doVERSION, "show program version and copyright");
|
||||
|
||||
try {
|
||||
|
@ -299,7 +308,18 @@ try
|
|||
|
||||
sp3csv<<"timestamp gnssid sv ephAge sp3X sp3Y sp3Z ephX ephY ephZ sp3Clock ephClock distance along clockDelta E speed"<<endl;
|
||||
|
||||
// RINEXNavWriter rnw("test.rnx");
|
||||
std::optional<RINEXNavWriter> rnw;
|
||||
|
||||
if(!rinexfname.empty())
|
||||
rnw = RINEXNavWriter(rinexfname);
|
||||
|
||||
std::optional<ofstream> osnmacsv;
|
||||
if(!osnmafname.empty()) {
|
||||
osnmacsv = ofstream(osnmafname);
|
||||
(*osnmacsv)<<"wn,tow,wtype,sv,osnma\n";
|
||||
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
char bert[4];
|
||||
int res = readn2(0, bert, 4);
|
||||
|
@ -388,13 +408,26 @@ try
|
|||
int wtype = gm.parse(inav);
|
||||
|
||||
gm.tow = nmm.gi().gnsstow();
|
||||
bool isnew = gmwtypes[{nmm.gi().gnsssv(), wtype}].tow != gm.tow;
|
||||
gmwtypes[{nmm.gi().gnsssv(), wtype}] = gm;
|
||||
static map<int,GalileoMessage> oldEph;
|
||||
cout << "gal inav wtype "<<wtype<<" for "<<nmm.gi().gnssid()<<","<<nmm.gi().gnsssv()<<","<<nmm.gi().sigid()<<" pbwn "<<nmm.gi().gnsswn()<<" pbtow "<< nmm.gi().gnsstow();
|
||||
static uint32_t tow;
|
||||
|
||||
if(osnmacsv && isnew)
|
||||
(*osnmacsv)<<nmm.gi().gnsswn()<<","<<gm.tow<<","<<wtype<<","<<nmm.gi().gnsssv()<<","<<makeHexDump(nmm.gi().reserved1())<<endl;
|
||||
|
||||
|
||||
if(wtype >=1 && wtype <= 5) {
|
||||
if(nmm.gi().has_reserved1()) {
|
||||
cout<<" res1 "<<makeHexDump(nmm.gi().reserved1());
|
||||
}
|
||||
}
|
||||
if(wtype == 4) {
|
||||
// 2^-34 2^-46
|
||||
cout <<" iodnav "<<gm.iodnav <<" af0 "<<gm.af0 <<" af1 "<<gm.af1 <<", scaled: "<<ldexp(1.0*gm.af0, 19-34)<<", "<<ldexp(1.0*gm.af1, 38-46);
|
||||
cout << " t0g " << gm.t0g <<" a0g " << gm.a0g <<" a1g " << gm.a1g <<" WN0g " << gm.wn0g;
|
||||
|
||||
if(tow && oldgm4s.count(nmm.gi().gnsssv()) && oldgm4s[nmm.gi().gnsssv()].iodnav != gm.iodnav) {
|
||||
|
||||
auto& oldgm4 = oldgm4s[nmm.gi().gnsssv()];
|
||||
|
@ -453,7 +486,9 @@ try
|
|||
if(!oldEph[sv].sqrtA)
|
||||
oldEph[sv] = gm;
|
||||
else if(oldEph[sv].iodnav != gm.iodnav) {
|
||||
// rnw.emitEphemeris(sid, gm);
|
||||
if(rnw)
|
||||
rnw->emitEphemeris(sid, gm);
|
||||
// gm.toJSON();
|
||||
|
||||
cout<<" disco! "<< oldEph[sv].iodnav << " - > "<<gm.iodnav <<", "<< (gm.getT0e() - oldEph[sv].getT0e())/3600.0 <<" hours-jump insta-age "<<ephAge(gm.tow, gm.getT0e())/3600.0<<" hours";
|
||||
Point oldPoint, newPoint;
|
||||
|
@ -498,7 +533,9 @@ try
|
|||
}
|
||||
if(wtype == 6) {
|
||||
cout<<" a0 " << gm.a0 <<" a1 " << gm.a1 <<" t0t "<<gm.t0t << " dtLS "<<(int)gm.dtLS;
|
||||
cout <<" wnLSF "<< (unsigned int)gm.wnLSF<<" dn " << (unsigned int)gm.dn<< " dtLSF "<<(int)gm.dtLSF<<endl;
|
||||
}
|
||||
|
||||
|
||||
// if(wtype < 7)
|
||||
// gm = GalileoMessage{};
|
||||
|
@ -535,7 +572,7 @@ try
|
|||
if(gm.alma3.e1bhs != 0) {
|
||||
cout <<" gm.tow "<<gm.tow<<" gmwtypes.tow "<< gmwtypes[{sv,9}].tow <<" ";
|
||||
}
|
||||
cout<<" "<<gmwtypes[{sv,9}].alma3.svid <<" af0 "<<gm.alma3.af0<<" af1 "<< gm.alma3.af1 <<" e5bhs "<< gm.alma3.e5bhs<<" e1bhs "<< gm.alma3.e1bhs <<" a0g " << gm.a0g <<" a1g "<< gm.a1g <<" t0g "<<gm.t0g <<" wn0g "<<gm.wn0g <<" delta-gps "<< gm.getGPSOffset(gm.tow, gm.wn).first<<"ns";
|
||||
cout<<" alma3.sv "<<gmwtypes[{sv,9}].alma3.svid <<" af0 "<<gm.alma3.af0<<" af1 "<< gm.alma3.af1 <<" e5bhs "<< gm.alma3.e5bhs<<" e1bhs "<< gm.alma3.e1bhs <<" a0g " << gm.a0g <<" a1g "<< gm.a1g <<" t0g "<<gm.t0g <<" wn0g "<<gm.wn0g <<" delta-gps "<< gm.getGPSOffset(gm.tow, gm.wn).first<<"ns";
|
||||
|
||||
int dw = (int)(gm.wn%64) - (int)(gm.wn0g);
|
||||
if(dw > 31)
|
||||
|
@ -546,6 +583,55 @@ try
|
|||
|
||||
cout<<endl;
|
||||
}
|
||||
else if(nmm.type() == NavMonMessage::GalileoCnavType) {
|
||||
basic_string<uint8_t> cnav((uint8_t*)nmm.gc().contents().c_str(), nmm.gc().contents().size());
|
||||
int sv = nmm.gc().gnsssv();
|
||||
if(!svfilter.check(2, sv, nmm.gc().sigid()))
|
||||
continue;
|
||||
etstamp();
|
||||
cout << "C/NAV for " << nmm.gc().gnssid()<<","<<nmm.gc().gnsssv()<<","<<nmm.gc().sigid() <<": header ";
|
||||
cout<<fmt::sprintf("%02x%02x%02x (status %d, MT %d, MID %d, MS %d, PID %d) rest ",
|
||||
getbitu(cnav.c_str(), 14, 8),
|
||||
getbitu(cnav.c_str(), 22, 8),
|
||||
getbitu(cnav.c_str(), 30, 8),
|
||||
getbitu(cnav.c_str(), 14+0, 2), // status
|
||||
getbitu(cnav.c_str(), 14+4, 2), // MT
|
||||
getbitu(cnav.c_str(), 14+6, 5), // MID
|
||||
getbitu(cnav.c_str(), 14+11, 5), // MIS
|
||||
getbitu(cnav.c_str(), 14+16, 8) // PID
|
||||
|
||||
);
|
||||
for(int n=0; n < 51; ++n)
|
||||
cout << fmt::sprintf("%02x ", getbitu(cnav.c_str(), 38 +n *8, 8));
|
||||
cout<<endl;
|
||||
|
||||
}
|
||||
else if(nmm.type() == NavMonMessage::GalileoFnavType) {
|
||||
basic_string<uint8_t> fnav((uint8_t*)nmm.gf().contents().c_str(), nmm.gf().contents().size());
|
||||
int sv = nmm.gf().gnsssv();
|
||||
if(!svfilter.check(2, sv, nmm.gf().sigid()))
|
||||
continue;
|
||||
etstamp();
|
||||
GalileoMessage gm;
|
||||
gm.parseFnav(fnav);
|
||||
cout<<"gal F/NAV wtype "<< (int)gm.wtype << " for "<<nmm.gf().gnssid()<<","<<nmm.gf().gnsssv()<<","<<nmm.gf().sigid();
|
||||
if(gm.wtype ==1 || gm.wtype == 2 || gm.wtype == 3 || gm.wtype == 4)
|
||||
cout<<" iodnav " <<gm.iodnav <<" tow " << gm.tow;
|
||||
if(gm.wtype == 1) {
|
||||
cout <<" af0 "<<gm.af0 << " af1 "<<gm.af1 <<" af2 "<< (int)gm.af2;
|
||||
}
|
||||
if(gm.wtype == 2) {
|
||||
cout <<" sqrtA "<<gm.sqrtA;
|
||||
}
|
||||
if(gm.wtype == 3) {
|
||||
cout <<" t0e "<<gm.t0e;
|
||||
}
|
||||
if(gm.wtype == 4) {
|
||||
cout <<" dtLS "<<(int)gm.dtLS <<" wnLSF "<< (unsigned int)gm.wnLSF<<" dn " << (unsigned int)gm.dn<< " dtLSF "<<(int)gm.dtLSF<<endl;
|
||||
}
|
||||
|
||||
cout<<endl;
|
||||
}
|
||||
else if(nmm.type() == NavMonMessage::GPSInavType) {
|
||||
int sv = nmm.gpsi().gnsssv();
|
||||
|
||||
|
@ -652,7 +738,7 @@ try
|
|||
cout<<" 2nd-match "<<second.name << " dist "<<second.distance/1000<<" km t0e "<<gs.gpsalma.getT0e() << " t " <<nmm.localutcseconds();
|
||||
}
|
||||
if(page == 18)
|
||||
cout << " dtLS " << (int)gs.dtLS <<" dtLSF "<< (int)gs.dtLSF;
|
||||
cout << " wnLSF "<< (int)gs.wnLSF <<" dn " << (int)gs.dn << " t0t " << (int)gs.t0t <<" wn0t "<<(int)gs.wn0t<<" dtLS " << (int)gs.dtLS <<" dtLSF "<< (int)gs.dtLSF;
|
||||
}
|
||||
else if(frame == 5) {
|
||||
if(gs.gpsalma.sv <= 24) {
|
||||
|
@ -700,19 +786,42 @@ try
|
|||
}
|
||||
}
|
||||
else if(rm.type == 1045 || rm.type == 1046) {
|
||||
|
||||
static ofstream af0inavstr("af0inav.csv"), af0fnavstr("af0fnav.csv"), bgdstr("bgdstr.csv");
|
||||
static bool first{true};
|
||||
|
||||
if(first) {
|
||||
af0inavstr<<"timestamp sv wn t0c af0 af1\n";
|
||||
af0fnavstr<<"timestamp sv wn t0c af0 af1\n";
|
||||
first=false;
|
||||
}
|
||||
SatID sid;
|
||||
sid.gnss = 2;
|
||||
sid.sv = rm.d_sv;
|
||||
sid.sigid = (rm.type == 1045) ? 5 : 1;
|
||||
|
||||
cout<< makeSatIDName(sid)<<" ";
|
||||
if(rm.type == 1045)
|
||||
cout<<"F/NAV ";
|
||||
else
|
||||
cout <<"I/NAV ";
|
||||
|
||||
cout <<" iode " << rm.d_gm.iodnav << " sisa " << (unsigned int) rm.d_gm.sisa << " af0 "<<rm.d_gm.af0 <<" af1 " << rm.d_gm.af1 <<" af2 " << (int) rm.d_gm.af2 << endl;
|
||||
if(rm.type == 1045) {
|
||||
af0fnavstr << nmm.localutcseconds()<<" " << rm.d_sv <<" " << rm.d_gm.wn<<" "<< rm.d_gm.t0c << " " << rm.d_gm.af0 << " " << rm.d_gm.af1<<"\n";
|
||||
cout<<"F/NAV";
|
||||
}
|
||||
else {
|
||||
af0inavstr << nmm.localutcseconds() <<" " << rm.d_sv <<" " << rm.d_gm.wn<<" "<<rm.d_gm.t0c<<" "<< rm.d_gm.af0 << " " << rm.d_gm.af1<<"\n";
|
||||
bgdstr << nmm.localutcseconds() <<" " << rm.d_sv<<" " <<rm.d_gm.BGDE1E5a <<" " << rm.d_gm.BGDE1E5b << "\n";
|
||||
cout <<"I/NAV";
|
||||
}
|
||||
|
||||
cout <<" iode " << rm.d_gm.iodnav << " sisa " << (unsigned int) rm.d_gm.sisa << " t0c " << rm.d_gm.t0c << " af0 "<<rm.d_gm.af0 <<" af1 " << rm.d_gm.af1 <<" af2 " << (int) rm.d_gm.af2 << " BGDE1E5a " << rm.d_gm.BGDE1E5a;
|
||||
if(rm.type == 1046) // I/NAV
|
||||
cout <<" BGDE1E5b "<< rm.d_gm.BGDE1E5b;
|
||||
cout<<endl;
|
||||
}
|
||||
else if(rm.type == 1059 || rm.type==1242) {
|
||||
cout<<"\n";
|
||||
for(const auto& dcb : rm.d_dcbs) {
|
||||
cout<<" "<<makeSatIDName(dcb.first)<<": "<<dcb.second<<" meters\n";
|
||||
}
|
||||
|
||||
cout<<endl;
|
||||
}
|
||||
else {
|
||||
cout<<" len " << nmm.rm().contents().size() << endl;
|
||||
|
@ -781,8 +890,6 @@ try
|
|||
cout<<" best-tle-match "<<match.name <<" dist "<<match.distance /1000<<" km";
|
||||
cout<<" norad " <<match.norad <<" int-desig " << match.internat;
|
||||
cout<<" 2nd-match "<<second.name << " dist "<<second.distance/1000<<" km";
|
||||
|
||||
|
||||
}
|
||||
else if((fraid == 4 && 1<= pageno && pageno <= 24) ||
|
||||
(fraid == 5 && 1<= pageno && pageno <= 6) ||
|
||||
|
@ -813,7 +920,7 @@ try
|
|||
cout<<" WNa "<<getbitu(&cond[0], beidouBitconv(190), 8)<<" t0a "<<getbitu(&cond[0], beidouBitconv(198), 8);
|
||||
}
|
||||
else if(bm.fraid == 5 && pageno==10) {
|
||||
cout <<" dTLS "<< (int)bm.deltaTLS;
|
||||
cout <<" dTLS "<< (int)bm.deltaTLS << " dTLSF " << (int) bm.deltaTLSF <<" wnLSF " << (unsigned int)bm.wnLSF <<" dn "<<(unsigned int) bm.dn<<endl;
|
||||
}
|
||||
else if(bm.fraid == 5 && pageno==24) {
|
||||
int AmID= getbitu(&cond[0], beidouBitconv(216), 2);
|
||||
|
@ -1014,17 +1121,19 @@ try
|
|||
|
||||
}
|
||||
else if(nmm.type() == NavMonMessage::ObserverDetailsType) {
|
||||
etstamp();
|
||||
cout<<"vendor "<<nmm.od().vendor()<<" hwversion " <<nmm.od().hwversion()<<" modules "<<nmm.od().modules()<<" swversion "<<nmm.od().swversion();
|
||||
cout<<" serial "<<nmm.od().serialno();
|
||||
if(nmm.od().has_owner())
|
||||
cout<<" owner "<<nmm.od().owner();
|
||||
if(nmm.od().has_clockoffsetdriftns())
|
||||
cout<<" drift "<<nmm.od().clockoffsetdriftns();
|
||||
if(nmm.od().has_clockaccuracyns())
|
||||
cout<<" clock-accuracy "<<nmm.od().clockaccuracyns();
|
||||
|
||||
cout<<endl;
|
||||
if(doObserverDetails) {
|
||||
etstamp();
|
||||
cout<<"vendor "<<nmm.od().vendor()<<" hwversion " <<nmm.od().hwversion()<<" modules "<<nmm.od().modules()<<" swversion "<<nmm.od().swversion();
|
||||
cout<<" serial "<<nmm.od().serialno();
|
||||
if(nmm.od().has_owner())
|
||||
cout<<" owner "<<nmm.od().owner();
|
||||
if(nmm.od().has_clockoffsetdriftns())
|
||||
cout<<" drift "<<nmm.od().clockoffsetdriftns();
|
||||
if(nmm.od().has_clockaccuracyns())
|
||||
cout<<" clock-accuracy "<<nmm.od().clockaccuracyns();
|
||||
|
||||
cout<<endl;
|
||||
}
|
||||
}
|
||||
else if(nmm.type() == NavMonMessage::UbloxJammingStatsType) {
|
||||
etstamp();
|
||||
|
@ -1195,12 +1304,14 @@ try
|
|||
cout<< nmm.sr().gnsssv() << " beacon "<<hexstring <<" code "<<(int)nmm.sr().code()<<" params "<< makeHexDump(nmm.sr().params()) <<endl;
|
||||
}
|
||||
else if(nmm.type() == NavMonMessage::TimeOffsetType) {
|
||||
etstamp();
|
||||
cout<<" got a time-offset message with "<< nmm.to().offsets().size()<<" offsets: ";
|
||||
for(const auto& o : nmm.to().offsets()) {
|
||||
cout << "gnssid "<<o.gnssid()<<" offset " << o.offsetns() << " +- "<<o.tacc()<<" ("<<o.valid()<<") , ";
|
||||
if(doTimeOffsets) {
|
||||
etstamp();
|
||||
cout<<" got a time-offset message with "<< nmm.to().offsets().size()<<" offsets: ";
|
||||
for(const auto& o : nmm.to().offsets()) {
|
||||
cout << "gnssid "<<o.gnssid()<<" offset " << o.offsetns() << " +- "<<o.tacc()<<" ("<<o.valid()<<") , ";
|
||||
}
|
||||
cout<<endl;
|
||||
}
|
||||
cout<<endl;
|
||||
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
#include "sclasses.hh"
|
||||
#include <map>
|
||||
#include "navmon.hh"
|
||||
#include "navmon.pb.h"
|
||||
#include <thread>
|
||||
#include "nmmsender.hh"
|
||||
#include "CLI/CLI.hpp"
|
||||
#include "version.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static char program[]="navmerge";
|
||||
|
||||
/* ubxtool/rtcmtool/septool deliver data to one of several `navrecv` instances
|
||||
This means we need a 'merge' tool.
|
||||
|
||||
The merge tool should be able to stream data from multiple `navnexus` instances
|
||||
(that correspond to the `navrecv` instances)
|
||||
|
||||
Currently, `navnexus` is really simple - it will send you a feed from x hours back, where
|
||||
you don't get to pick x.
|
||||
|
||||
The simplest navmerge implementation does nothing but connect to a few navnexus instances
|
||||
and it mixes them together.
|
||||
|
||||
Every message "should" only appear on one of the upstreams, but you never know.
|
||||
|
||||
On initial connection, the different navnexuses may start up from a different time, currently.
|
||||
Let us state that This Should Not Happen.
|
||||
|
||||
On initial connect, a navnexus might take dozens of seconds before it starts coughing up data.
|
||||
|
||||
Initial goal for navmerge is: only make sure realtime works.
|
||||
|
||||
Every upstream has a thread that loops trying to connect
|
||||
If a new message comes in, it is stored in a shared data structure
|
||||
If a new connect is made, set a "don't send" marker for a whole minute
|
||||
|
||||
There is a sender thread that periodically polls this data structure
|
||||
Any data that is older than the previous high-water mark gets sent out & removed from structure
|
||||
However, transmission stops 10 seconds before realtime
|
||||
If a "don't send" marker is set, we don't do a thing
|
||||
|
||||
*/
|
||||
|
||||
multimap<pair<uint64_t, uint64_t>, string> g_buffer;
|
||||
std::mutex g_mut;
|
||||
|
||||
void recvSession(ComboAddress upstream)
|
||||
{
|
||||
for(;;) {
|
||||
try {
|
||||
Socket sock(upstream.sin4.sin_family, SOCK_STREAM);
|
||||
cerr<<"Connecting to "<< upstream.toStringWithPort()<<" to source data..";
|
||||
SConnectWithTimeout(sock, upstream, 5);
|
||||
cerr<<" done"<<endl;
|
||||
|
||||
for(int count=0;;++count) {
|
||||
string part=SRead(sock, 4);
|
||||
if(part.empty()) {
|
||||
cerr<<"EOF from "<<upstream.toStringWithPort()<<endl;
|
||||
break;
|
||||
}
|
||||
if(part != "bert") {
|
||||
cerr << "Message "<<count<<", wrong magic from "<<upstream.toStringWithPort()<<": "<<makeHexDump(part)<<endl;
|
||||
break;
|
||||
}
|
||||
if(!count)
|
||||
cerr<<"Receiving messages from "<<upstream.toStringWithPort()<<endl;
|
||||
string out=part;
|
||||
|
||||
part = SRead(sock, 2);
|
||||
out += part;
|
||||
|
||||
uint16_t len;
|
||||
memcpy(&len, part.c_str(), 2);
|
||||
len = htons(len);
|
||||
|
||||
part = SRead(sock, len); // XXXXX ???
|
||||
if(part.size() != len) {
|
||||
cerr<<"Mismatch, "<<part.size()<<", len "<<len<<endl;
|
||||
// XX AND THEN WHAT??
|
||||
}
|
||||
out += part;
|
||||
|
||||
NavMonMessage nmm;
|
||||
nmm.ParseFromString(part);
|
||||
// writeToDisk(nmm.localutcseconds(), nmm.sourceid(), out);
|
||||
// do something with the message
|
||||
|
||||
std::lock_guard<std::mutex> mut(g_mut);
|
||||
g_buffer.insert({{nmm.localutcseconds(), nmm.localutcnanoseconds()}, part});
|
||||
}
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
cerr<<"Error in receiving thread: "<<e.what()<<endl;
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
cerr<<"Thread for "<<upstream.toStringWithPort()<< " exiting"<<endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
vector<string> destinations;
|
||||
vector<string> sources;
|
||||
|
||||
bool doVERSION{false}, doSTDOUT{false};
|
||||
CLI::App app(program);
|
||||
app.add_option("--source", sources, "Connect to these IP address:port to source protobuf");
|
||||
app.add_option("--destination,-d", destinations, "Send output to this IPv4/v6 address");
|
||||
app.add_flag("--version", doVERSION, "show program version and copyright");
|
||||
app.add_flag("--stdout", doSTDOUT, "Emit output to stdout");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
if(doVERSION) {
|
||||
showVersion(program, g_gitHash);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
NMMSender ns;
|
||||
|
||||
ns.d_debug = true;
|
||||
for(const auto& s : destinations) {
|
||||
auto res=resolveName(s, true, true);
|
||||
if(res.empty()) {
|
||||
cerr<<"Unable to resolve '"<<s<<"' as destination for data, exiting"<<endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
ns.addDestination(s); // ComboAddress(s, 29603));
|
||||
}
|
||||
if(doSTDOUT)
|
||||
ns.addDestination(1);
|
||||
|
||||
|
||||
for(const auto& s : sources) {
|
||||
ComboAddress oneaddr(s, 29601);
|
||||
std::thread one(recvSession, oneaddr);
|
||||
one.detach();
|
||||
}
|
||||
|
||||
time_t start=time(0);
|
||||
for(;;) {
|
||||
sleep(1);
|
||||
vector<string> tosend;
|
||||
{
|
||||
std::lock_guard<std::mutex> mut(g_mut);
|
||||
|
||||
time_t now = time(0);
|
||||
if(now - start < 30) {
|
||||
cerr<<"Have "<<g_buffer.size()<<" messages"<<endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
for(auto iter = g_buffer.begin(); iter != g_buffer.end(); ) {
|
||||
if(iter->first.first > (uint64_t)now - 5)
|
||||
break;
|
||||
tosend.push_back(iter->second);
|
||||
iter = g_buffer.erase(iter);
|
||||
}
|
||||
}
|
||||
// cerr<<"Have "<<tosend.size()<<" messages to send, "<<g_buffer.size()<<" left in queue"<<endl;
|
||||
std::string buf;
|
||||
for(const auto& m : tosend) {
|
||||
ns.emitNMM(m);
|
||||
}
|
||||
}
|
||||
}
|
63
navmon.cc
63
navmon.cc
|
@ -159,6 +159,18 @@ std::string humanTimeShort(time_t t)
|
|||
return buffer;
|
||||
}
|
||||
|
||||
// influx ascii time format, in UTC
|
||||
std::string influxTime(time_t t)
|
||||
{
|
||||
struct tm tm={0};
|
||||
gmtime_r(&t, &tm);
|
||||
|
||||
char buffer[80];
|
||||
std::string fmt = "%Y-%m-%d %H:%M:%S";
|
||||
|
||||
strftime(buffer, sizeof(buffer), fmt.c_str(), &tm);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string humanTime(time_t t, uint32_t nanoseconds)
|
||||
{
|
||||
|
@ -280,22 +292,33 @@ std::string makeSatPartialName(const SatID& satid)
|
|||
return fmt::sprintf("%c%02d", getGNSSChar(satid.gnss), satid.sv);
|
||||
}
|
||||
|
||||
int g_dtLS{18}, g_dtLSBeidou{4};
|
||||
|
||||
void getGPSDateFromUTC(time_t t, int& wn, int& tow)
|
||||
{
|
||||
t -= 315964800;
|
||||
t += 18;
|
||||
t += 18; // XXXXXX LEAP SECOND
|
||||
wn = t/(7*86400);
|
||||
tow = t%(7*86400);
|
||||
}
|
||||
void getGalDateFromUTC(time_t t, int& wn, int& tow)
|
||||
{
|
||||
t -= 935280000;
|
||||
t += 18;
|
||||
t += 18; // XXXXXXX LEAP SECOND
|
||||
wn = t/(7*86400);
|
||||
tow = t%(7*86400);
|
||||
}
|
||||
|
||||
int g_dtLS{18}, g_dtLSBeidou{4};
|
||||
void getBeiDouDateFromUTC(time_t t, int&wn, int& sow)
|
||||
{
|
||||
// Week number count started from zero at 00:00:00 on Jan. 1, 2006 of BDT
|
||||
t -= 1136070000;
|
||||
t+= g_dtLSBeidou;
|
||||
wn = t/(7*86400);
|
||||
sow = t%(7*86400);
|
||||
}
|
||||
|
||||
|
||||
uint64_t utcFromGST(int wn, int tow)
|
||||
{
|
||||
return (935280000 + wn * 7*86400 + tow - g_dtLS);
|
||||
|
@ -324,6 +347,19 @@ string makeHexDump(const string& str)
|
|||
return ret;
|
||||
}
|
||||
|
||||
string makeHexDump(const basic_string<uint8_t>& str)
|
||||
{
|
||||
char tmp[5];
|
||||
string ret;
|
||||
ret.reserve((int)(str.size()*2.2));
|
||||
|
||||
for(string::size_type n=0;n<str.size();++n) {
|
||||
snprintf(tmp, sizeof(tmp), "%02x ", (unsigned char)str[n]);
|
||||
ret+=tmp;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string sbasName(int prn)
|
||||
{
|
||||
string sbas;
|
||||
|
@ -375,7 +411,7 @@ time_t parseTime(std::string_view in)
|
|||
{
|
||||
time_t now=time(0);
|
||||
|
||||
vector<string> formats({"%Y-%m-%d %H:%M", "%Y%m%d %H%M", "%H:%M", "%H%M"});
|
||||
vector<string> formats({"%Y-%m-%d %H:%M", "%Y-%m-%d %H:%M:%S", "%Y%m%d %H%M", "%H:%M", "%H%M"});
|
||||
for(const auto& f : formats) {
|
||||
struct tm tm;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
@ -392,7 +428,24 @@ time_t parseTime(std::string_view in)
|
|||
|
||||
string err="Can only parse on";
|
||||
for(const auto& f : formats)
|
||||
err += " "+ f;
|
||||
err += " '"+ f+"'";
|
||||
throw runtime_error(err);
|
||||
}
|
||||
|
||||
|
||||
std::string string_replace(const std::string& str, const std::string& match,
|
||||
const std::string& replacement, unsigned int max_replacements)
|
||||
{
|
||||
size_t pos = 0;
|
||||
std::string newstr = str;
|
||||
unsigned int replacements = 0;
|
||||
while ((pos = newstr.find(match, pos)) != std::string::npos
|
||||
&& replacements < max_replacements)
|
||||
{
|
||||
newstr.replace(pos, match.length(), replacement);
|
||||
pos += replacement.length();
|
||||
replacements++;
|
||||
}
|
||||
return newstr;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <string>
|
||||
#include <tuple>
|
||||
#include <mutex>
|
||||
|
||||
#include <limits.h>
|
||||
extern const char* g_gitHash;
|
||||
|
||||
|
||||
|
@ -18,6 +18,8 @@ std::string humanTimeNow();
|
|||
std::string humanTime(time_t t);
|
||||
std::string humanTimeShort(time_t t);
|
||||
std::string humanTime(time_t t, uint32_t nanoseconds);
|
||||
// influx ascii time format, in UTC
|
||||
std::string influxTime(time_t t);
|
||||
struct SatID
|
||||
{
|
||||
uint32_t gnss{255}; // these could all be 'int16_t' but leads to howling numbers of warnings with protobuf
|
||||
|
@ -78,9 +80,12 @@ double utcFromGPS(int wn, double tow);
|
|||
|
||||
void getGPSDateFromUTC(time_t t, int& wn, int& tow);
|
||||
void getGalDateFromUTC(time_t t, int& wn, int& tow);
|
||||
|
||||
void getBeiDouDateFromUTC(time_t t, int&wn, int& sow);
|
||||
|
||||
std::string makeHexDump(const std::string& str);
|
||||
std::string makeHexDump(const std::basic_string<uint8_t>& str);
|
||||
size_t writen2(int fd, const void *buf, size_t count);
|
||||
void unixDie(const std::string& reason);
|
||||
time_t parseTime(std::string_view in);
|
||||
std::string string_replace(const std::string& str, const std::string& match,
|
||||
const std::string& replacement, unsigned int max_replacements = UINT_MAX);
|
||||
|
|
30
navmon.proto
30
navmon.proto
|
@ -18,6 +18,8 @@ message NavMonMessage {
|
|||
GPSCnavType = 14;
|
||||
RTCMMessageType = 15;
|
||||
TimeOffsetType = 16;
|
||||
GalileoFnavType = 17;
|
||||
GalileoCnavType = 18;
|
||||
}
|
||||
|
||||
required uint64 sourceID = 1;
|
||||
|
@ -33,7 +35,31 @@ message NavMonMessage {
|
|||
required uint32 gnssID =3;
|
||||
required uint32 gnssSV =4;
|
||||
required bytes contents =5;
|
||||
optional uint32 sigid = 6;
|
||||
optional uint32 sigid = 6;
|
||||
optional bytes reserved1 = 7;
|
||||
optional bytes reserved2 = 8;
|
||||
optional bytes sar = 9;
|
||||
optional bytes spare = 10;
|
||||
optional bytes crc = 11;
|
||||
}
|
||||
|
||||
message GalileoFnav {
|
||||
required uint32 gnssWN =1;
|
||||
required uint32 gnssTOW =2; // INTEGERS!
|
||||
|
||||
required uint32 gnssID =3;
|
||||
required uint32 gnssSV =4;
|
||||
required bytes contents =5;
|
||||
required uint32 sigid = 6;
|
||||
}
|
||||
message GalileoCnav {
|
||||
required uint32 gnssWN =1;
|
||||
required uint32 gnssTOW =2; // INTEGERS!
|
||||
|
||||
required uint32 gnssID =3;
|
||||
required uint32 gnssSV =4;
|
||||
required bytes contents =5;
|
||||
required uint32 sigid = 6;
|
||||
}
|
||||
|
||||
message GPSInav {
|
||||
|
@ -217,4 +243,6 @@ message NavMonMessage {
|
|||
optional GPSCnav gpsc = 18;
|
||||
optional RTCMMessage rm = 19;
|
||||
optional TimeOffsetMessage to = 20;
|
||||
optional GalileoFnav gf=21;
|
||||
optional GalileoCnav gc=22;
|
||||
}
|
||||
|
|
12
navnexus.cc
12
navnexus.cc
|
@ -88,7 +88,7 @@ try
|
|||
close(fd);
|
||||
continue;
|
||||
}
|
||||
cout <<"Seeked to position "<<fpos[fname]<<" of "<<fname<<endl;
|
||||
// cout <<"Seeked to position "<<fpos[fname]<<" of "<<fname<<endl;
|
||||
NavMonMessage nmm;
|
||||
|
||||
uint32_t looked=0;
|
||||
|
@ -106,15 +106,15 @@ try
|
|||
fpos[fname]=offset;
|
||||
close(fd);
|
||||
}
|
||||
cout<<"Sorting.. ";
|
||||
cout.flush();
|
||||
// cout<<"Sorting.. ";
|
||||
// cout.flush();
|
||||
sort(rnmms.begin(), rnmms.end(), [](const auto& a, const auto& b)
|
||||
{
|
||||
return std::tie(a.first.tv_sec, a.first.tv_nsec)
|
||||
< std::tie(b.first.tv_sec, b.first.tv_nsec);
|
||||
});
|
||||
cout<<"Sending.. ";
|
||||
cout.flush();
|
||||
// cout<<"Sending.. ";
|
||||
// cout.flush();
|
||||
for(const auto& nmm: rnmms) {
|
||||
std::string buf="bert";
|
||||
uint16_t len = htons(nmm.second.size());
|
||||
|
@ -122,7 +122,7 @@ try
|
|||
buf += nmm.second;
|
||||
SWriten(clientfd, buf);
|
||||
}
|
||||
cout<<"Done"<<endl;
|
||||
// cout<<"Done"<<endl;
|
||||
if(3600 + start.tv_sec - (start.tv_sec % 3600) < time(0))
|
||||
start.tv_sec = 3600 + start.tv_sec - (start.tv_sec % 3600);
|
||||
else {
|
||||
|
|
433
navparse.cc
433
navparse.cc
|
@ -37,7 +37,7 @@
|
|||
#include "gpscnav.hh"
|
||||
#include "rtcm.hh"
|
||||
#include "version.hh"
|
||||
|
||||
//#include "nequick.hh"
|
||||
|
||||
static char program[]="navparse";
|
||||
|
||||
|
@ -45,7 +45,7 @@ using namespace std;
|
|||
|
||||
extern const char* g_gitHash;
|
||||
|
||||
struct ObserverPosition
|
||||
struct ObserverFacts
|
||||
{
|
||||
Point pos;
|
||||
double groundSpeed{-1};
|
||||
|
@ -65,7 +65,7 @@ struct ObserverPosition
|
|||
string remark;
|
||||
time_t lastSeen{0};
|
||||
};
|
||||
std::map<int, ObserverPosition> g_srcpos;
|
||||
std::map<int, ObserverFacts> g_srcpos;
|
||||
|
||||
struct SBASAndReceiverStatus
|
||||
{
|
||||
|
@ -263,9 +263,6 @@ void SVStat::reportNewEphemeris(const SatID& id, InfluxPusher& idb)
|
|||
if(gnss==0) { // GPS
|
||||
const auto& eg = ephgpsmsg;
|
||||
|
||||
idb.addValue(id, "ephemeris-actual", {
|
||||
{"iod", eg.getIOD()}}, satUTCTime(id));
|
||||
|
||||
|
||||
idb.addValue(id, "ephemeris-actual", {
|
||||
{"iod", eg.getIOD()},
|
||||
|
@ -295,10 +292,6 @@ void SVStat::reportNewEphemeris(const SatID& id, InfluxPusher& idb)
|
|||
|
||||
if(gnss==2) {
|
||||
const auto& eg = ephgalmsg;
|
||||
|
||||
idb.addValue(id, "ephemeris-actual", {
|
||||
{"iod", eg.getIOD()}}, satUTCTime(id));
|
||||
|
||||
|
||||
idb.addValue(id, "ephemeris-actual", {
|
||||
{"iod", eg.getIOD()},
|
||||
|
@ -568,6 +561,25 @@ try {
|
|||
}
|
||||
sats.push_back(sat);
|
||||
}
|
||||
|
||||
vector<SatID> aux{{2, 14, 1}, {2,18,1}};
|
||||
for(const auto& id : aux) {
|
||||
if(!g_svstats.count(id))
|
||||
continue;
|
||||
|
||||
const auto& svstat = g_svstats[id];
|
||||
|
||||
if(svstat.completeIOD() && svstat.galmsg.sisa == 255) {
|
||||
continue;
|
||||
}
|
||||
if(svstat.galmsg.e1bhs || svstat.galmsg.e1bdvs) {
|
||||
continue;
|
||||
}
|
||||
Point sat;
|
||||
getCoordinates(tow, svstat.galmsg, &sat);
|
||||
sats.push_back(sat);
|
||||
}
|
||||
|
||||
// cout<<endl;
|
||||
auto cov = emitCoverage(sats);
|
||||
int cells=0;
|
||||
|
@ -629,10 +641,11 @@ try
|
|||
string localAddress("127.0.0.1:29599");
|
||||
string htmlDir("./html");
|
||||
string influxDBName("null");
|
||||
|
||||
bool doGalileoReportSpeedup{false};
|
||||
bool doLogRFData{false};
|
||||
app.add_flag("--version", doVERSION, "show program version and copyright");
|
||||
app.add_flag("--log-rf-data", doLogRFData, "store per station RF/correlator data");
|
||||
app.add_flag("--gal-report-speedup", doGalileoReportSpeedup, "skip debugging data, glonass, beidou, SBAS");
|
||||
app.add_option("--bind,-b", localAddress, "Address to bind to");
|
||||
app.add_option("--html", htmlDir, "Where to source the HTML & JavaScript");
|
||||
app.add_option("--influxdb", influxDBName, "Name of influxdb database");
|
||||
|
@ -651,7 +664,7 @@ try
|
|||
// feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW );
|
||||
|
||||
|
||||
// g_tles.parseFile("active.txt");
|
||||
// g_tles.parseFile("active.txt");
|
||||
|
||||
g_tles.parseFile("galileo.txt");
|
||||
g_tles.parseFile("glo-ops.txt");
|
||||
|
@ -1524,6 +1537,9 @@ try
|
|||
}
|
||||
}
|
||||
if(s.first.gnss == 2) {
|
||||
if(s.second.osnmaTime >= 0 && ephAge(s.second.galmsg.tow, s.second.osnmaTime) < 60)
|
||||
item["osnma"] = s.second.osnma;
|
||||
|
||||
auto galileoalma = g_galileoalmakeeper.get();
|
||||
if(auto iter = galileoalma.find(s.first.sv); iter != galileoalma.end()) {
|
||||
Point almapos;
|
||||
|
@ -1836,7 +1852,7 @@ try
|
|||
if(!lastCovSyncPoint)
|
||||
holdOffTime = nmm.localutcseconds() + 600;
|
||||
|
||||
if(nmm.localutcseconds() > holdOffTime)
|
||||
if((time_t)nmm.localutcseconds() > holdOffTime)
|
||||
storeCoverageStats(idb, nmm.localutcseconds());
|
||||
lastCovSyncPoint = nmm.localutcseconds() / lastCovInterval;
|
||||
}
|
||||
|
@ -1847,6 +1863,63 @@ try
|
|||
storeSelfStats(idb, nmm.localutcseconds());
|
||||
lastSelfstatSyncPoint = nmm.localutcseconds() / lastSelfstatInterval;
|
||||
}
|
||||
|
||||
#if 0
|
||||
constexpr auto lastIonoInterval = 3600;
|
||||
static time_t lastIonoSyncPoint;
|
||||
if(nmm.localutcseconds() / lastIonoInterval > (unsigned int)lastIonoSyncPoint) {
|
||||
// go over all satellites
|
||||
NeQuickInst nqi;
|
||||
// cerr<<"Looking at all sats"<<endl;
|
||||
for(const auto& s : g_svstats) {
|
||||
if(s.first.gnss!=2 || !s.second.completeIOD())
|
||||
continue;
|
||||
Point sat;
|
||||
s.second.getCoordinates(s.second.tow(), &sat);
|
||||
for(const auto& pr : s.second.perrecv) {
|
||||
// cerr<<"Looking at "<<s.first.sv<<", "<<pr.second.db<<" "<<nmm.localutcseconds()<<", "<<pr.second.t<<" perrecv "<<pr.first<<" count " << g_srcpos.count(pr.first)<<endl;
|
||||
if(g_srcpos.count(pr.first) && (pr.second.db > 0 || (nmm.localutcseconds() - pr.second.t < 120))) {
|
||||
// cerr<<"Doing it -> "<< s.second.galmsg.ai0 <<" " <<s.second.galmsg.ai1<<" " << s.second.galmsg.ai2<< endl;
|
||||
const auto& sp = g_srcpos[pr.first];
|
||||
try {
|
||||
// cerr<<"Obs "<<pr.first<<" pos: "<<sp.pos.x<<", "<<sp.pos.y<<", "<<sp.pos.z<<endl;
|
||||
// cerr<<"Sat " <<s.first.sv<<" pos: "<<sat.x<<", "<<sat.y<<", "<<sat.z<<endl;
|
||||
// auto obs = ecefToWGS84Deg(sp.pos.x, sp.pos.y, sp.pos.z);
|
||||
// cerr<<"Observer height: "<<get<2>(obs)<<" meters, long "<<get<0>(obs)<<", lat "<<get<1>(obs)<<endl;
|
||||
|
||||
// auto satdegs = ecefToWGS84Deg(sat.x, sat.y, sat.z);
|
||||
// cerr<<"Satellite height: "<<get<2>(satdegs)<<" meters, long "<<get<0>(satdegs)<<", lat "<<get<1>(satdegs)<< " -> elevation "<<getElevationDeg(sat, sp.pos)<<" deg"<<endl;
|
||||
|
||||
if(getElevationDeg(sat, sp.pos) < 0) // below the horizon
|
||||
continue;
|
||||
|
||||
if(sp.pos.x==0 || isnan(sat.x)) {
|
||||
// cerr<<"Fake position, skipping"<<endl;
|
||||
continue;
|
||||
}
|
||||
double meters = (40.3e16/(1575420000.0*1575420000.0)) *
|
||||
nqi.getTecu(nmm.localutcseconds(),
|
||||
s.second.galmsg.ai0,
|
||||
s.second.galmsg.ai1,
|
||||
s.second.galmsg.ai2, sp.pos, sat);
|
||||
|
||||
idb.addValue(s.first, "nequick",
|
||||
{
|
||||
{"meters", meters}
|
||||
}, nmm.localutcseconds() + nmm.localutcnanoseconds()/1000000000.0, pr.first);
|
||||
// cerr<<"Meters: "<<meters<<endl;
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
cerr<<"Exception during NeQuick: "<<e.what()<<endl;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// cerr<<"Done"<<endl;
|
||||
lastIonoSyncPoint = nmm.localutcseconds() / lastIonoInterval;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if(nmm.type() == NavMonMessage::ReceptionDataType) {
|
||||
|
@ -1877,12 +1950,12 @@ try
|
|||
|
||||
Point sat{0,0,0};
|
||||
//cout<<"Got recdata for "<<id.gnss<<","<<id.sv<<","<<id.sigid<<": count="<<g_svstats.count(id)<<endl;
|
||||
if(g_svstats[id].completeIOD() && (id.gnss != 6 || !(random() % 16))) { // glonass is too slow
|
||||
if(g_svstats[id].completeIOD() && !(random() % 16)) {
|
||||
g_svstats[id].getCoordinates(g_svstats[id].tow(), &sat);
|
||||
}
|
||||
|
||||
if(sat.x != 0 && g_srcpos[nmm.sourceid()].pos.x != 0) {
|
||||
if(doLogRFData && !(random() % 4))
|
||||
if(doLogRFData && !(random() % 16))
|
||||
idb.addValue(id, "recdata",
|
||||
{
|
||||
{"db", nmm.rd().db()},
|
||||
|
@ -1904,31 +1977,6 @@ try
|
|||
else
|
||||
sigid = 1; // default to E1B
|
||||
SatID id={2,(uint32_t)sv,(uint32_t)sigid};
|
||||
/*
|
||||
struct DedupKey
|
||||
{
|
||||
SatID id;
|
||||
int wn;
|
||||
int tow;
|
||||
basic_string<uint8_t> contents;
|
||||
bool operator<(const DedupKey& rhs) const
|
||||
{
|
||||
return tie(id, wn, tow, contents) <
|
||||
tie(rhs.id, rhs.wn, rhs.tow, rhs.contents);
|
||||
}
|
||||
};
|
||||
|
||||
static set<DedupKey> s_dedup;
|
||||
DedupKey dk{id, (int)nmm.gi().gnsswn(), (int)nmm.gi().gnsstow(), inav};
|
||||
if(s_dedup.insert(dk).second == false) {
|
||||
// cout<<"Dedup"<<endl;
|
||||
continue;
|
||||
}
|
||||
if(s_dedup.size() > 10000)
|
||||
s_dedup.clear();
|
||||
*/
|
||||
// XXX conversion, may be vital
|
||||
// g_svstats[id].wn = nmm.gi().gnsswn();
|
||||
|
||||
auto& svstat = g_svstats[id];
|
||||
svstat.gnss = id.gnss;
|
||||
|
@ -1937,7 +1985,6 @@ try
|
|||
auto& gm = svstat.galmsg;
|
||||
unsigned int wtype = gm.parse(inav);
|
||||
|
||||
|
||||
if(wtype == 5 && svstat.galmsgTyped.count(5)) {
|
||||
const auto& old5gm = svstat.galmsgTyped[5];
|
||||
if(make_tuple(old5gm.e5bhs, old5gm.e1bhs, old5gm.e5bdvs, old5gm.e1bdvs) !=
|
||||
|
@ -1949,6 +1996,15 @@ try
|
|||
svstat.galmsgTyped[wtype] = gm;
|
||||
|
||||
if(wtype == 1 || wtype == 2 || wtype == 3 || wtype == 4) {
|
||||
if(nmm.gi().has_reserved1()) {
|
||||
static string off;
|
||||
if(off.empty())
|
||||
off.append(5, (char)0);
|
||||
svstat.osnma = nmm.gi().reserved1() != off;
|
||||
if(svstat.osnma) // eventually this will become too much but ok for now
|
||||
idb.addValue(id, "osnma", {{"wtype", wtype}, {"field", makeHexDump(nmm.gi().reserved1())}}, satUTCTime(id));
|
||||
svstat.osnmaTime = gm.tow;
|
||||
}
|
||||
idb.addValue(id, "ephemeris", {{"iod-live", svstat.galmsg.iodnav},
|
||||
{"eph-age", ephAge(gm.tow, gm.getT0e())}}, satUTCTime(id));
|
||||
|
||||
|
@ -1959,34 +2015,27 @@ try
|
|||
if(w > 1 && svstat.galmsgTyped[w-1].iodnav != svstat.galmsgTyped[w].iodnav)
|
||||
break;
|
||||
}
|
||||
if(w==5) {
|
||||
if(w==5) { // have complete new ephemeris
|
||||
if(svstat.ephgalmsg.iodnav != svstat.galmsgTyped[1].iodnav) {
|
||||
svstat.oldephgalmsg = svstat.ephgalmsg;
|
||||
svstat.ephgalmsg = svstat.galmsgTyped[wtype];
|
||||
svstat.reportNewEphemeris(id, idb);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// XXX conversion possibly vital
|
||||
// g_svstats[id].tow = nmm.gi().gnsstow();
|
||||
|
||||
// g_svstats[id].perrecv[nmm.sourceid()].wn = nmm.gi().gnsswn();
|
||||
// g_svstats[id].perrecv[nmm.sourceid()].tow = nmm.gi().gnsstow();
|
||||
g_svstats[id].perrecv[nmm.sourceid()].t = nmm.localutcseconds();
|
||||
svstat.perrecv[nmm.sourceid()].t = nmm.localutcseconds();
|
||||
|
||||
if(wtype >=1 && wtype <= 4) { // ephemeris
|
||||
if(wtype == 3) {
|
||||
idb.addValue(id, "sisa", {{"value", g_svstats[id].galmsg.sisa}}, satUTCTime(id));
|
||||
idb.addValue(id, "sisa", {{"value", svstat.galmsg.sisa}}, satUTCTime(id));
|
||||
}
|
||||
else if(wtype == 4) {
|
||||
idb.addValue(id, "clock", {{"offset_ns", svstat.galmsg.getAtomicOffset(svstat.tow()).first},
|
||||
{"t0c", g_svstats[id].galmsg.t0c*60}, // getT0c()??
|
||||
{"af0", g_svstats[id].galmsg.af0},
|
||||
{"af1", g_svstats[id].galmsg.af1},
|
||||
{"af2", g_svstats[id].galmsg.af2}}, satUTCTime(id));
|
||||
{"t0c", svstat.galmsg.t0c*60}, // getT0c()??
|
||||
{"af0", svstat.galmsg.af0},
|
||||
{"af1", svstat.galmsg.af1},
|
||||
{"af2", svstat.galmsg.af2}}, satUTCTime(id));
|
||||
|
||||
|
||||
if(oldgm.af0 && oldgm.t0c != svstat.galmsg.t0c) {
|
||||
|
@ -1994,35 +2043,47 @@ try
|
|||
auto newOffset = svstat.galmsg.getAtomicOffset(svstat.tow());
|
||||
svstat.timeDisco = oldOffset.first - newOffset.first;
|
||||
if(fabs(svstat.timeDisco) < 10000)
|
||||
idb.addValue(id, "clock_jump_ns", {{"value", svstat.timeDisco}}, satUTCTime(id));
|
||||
idb.addValue(id, "clock_jump_ns", {
|
||||
{"jump", svstat.timeDisco},
|
||||
{"duration", ephAge(svstat.galmsg.t0c * 60, oldgm.t0c * 60)},
|
||||
{"old-af0", oldgm.af0},
|
||||
{"old-af1", oldgm.af1},
|
||||
{"old-af2", oldgm.af2},
|
||||
{"old-t0c", oldgm.t0c * 60},
|
||||
{"new-af0", svstat.galmsg.af0},
|
||||
{"new-af1", svstat.galmsg.af1},
|
||||
{"new-af2", svstat.galmsg.af2},
|
||||
{"new-t0c", svstat.galmsg.t0c * 60}
|
||||
|
||||
}, satUTCTime(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(wtype == 5) {
|
||||
idb.addValue(id, "iono", {
|
||||
{"ai0", g_svstats[id].galmsg.ai0},
|
||||
{"ai1", g_svstats[id].galmsg.ai1},
|
||||
{"ai2", g_svstats[id].galmsg.ai2},
|
||||
{"sf1", g_svstats[id].galmsg.sf1},
|
||||
{"sf2", g_svstats[id].galmsg.sf2},
|
||||
{"sf3", g_svstats[id].galmsg.sf3},
|
||||
{"sf4", g_svstats[id].galmsg.sf4},
|
||||
{"sf5", g_svstats[id].galmsg.sf5}}, satUTCTime(id));
|
||||
{"ai0", svstat.galmsg.ai0},
|
||||
{"ai1", svstat.galmsg.ai1},
|
||||
{"ai2", svstat.galmsg.ai2},
|
||||
{"sf1", svstat.galmsg.sf1},
|
||||
{"sf2", svstat.galmsg.sf2},
|
||||
{"sf3", svstat.galmsg.sf3},
|
||||
{"sf4", svstat.galmsg.sf4},
|
||||
{"sf5", svstat.galmsg.sf5}}, satUTCTime(id));
|
||||
|
||||
|
||||
idb.addValue(id, "galbgd", {
|
||||
{"BGDE1E5a", g_svstats[id].galmsg.BGDE1E5a},
|
||||
{"BGDE1E5b", g_svstats[id].galmsg.BGDE1E5b}}, satUTCTime(id));
|
||||
{"BGDE1E5a", svstat.galmsg.BGDE1E5a},
|
||||
{"BGDE1E5b", svstat.galmsg.BGDE1E5b}}, satUTCTime(id));
|
||||
|
||||
|
||||
idb.addValue(id, "galhealth", {
|
||||
{"e1bhs", g_svstats[id].galmsg.e1bhs},
|
||||
{"e5bhs", g_svstats[id].galmsg.e5bhs},
|
||||
{"e5bdvs", g_svstats[id].galmsg.e5bdvs},
|
||||
{"e1bdvs", g_svstats[id].galmsg.e1bdvs}}, satUTCTime(id));
|
||||
{"e1bhs", svstat.galmsg.e1bhs},
|
||||
{"e5bhs", svstat.galmsg.e5bhs},
|
||||
{"e5bdvs", svstat.galmsg.e5bdvs},
|
||||
{"e1bdvs", svstat.galmsg.e1bdvs}}, satUTCTime(id));
|
||||
}
|
||||
else if(wtype == 6) { // GST-UTC
|
||||
const auto& sv = g_svstats[id];
|
||||
const auto& sv = svstat;
|
||||
g_GSTUTCOffset = sv.galmsg.getUTCOffset(sv.tow(), sv.wn()).first;
|
||||
idb.addValue(id, "utcoffset", {
|
||||
{"a0", sv.galmsg.a0},
|
||||
|
@ -2061,26 +2122,146 @@ try
|
|||
g_galileoalma[gm.alma3.svid] = gm.alma3;
|
||||
}
|
||||
g_GSTGPSOffset = gm.getGPSOffset(gm.tow, gm.wn).first;
|
||||
idb.addValue(id, "gpsoffset", {{"a0g", g_svstats[id].galmsg.a0g},
|
||||
{"a1g", g_svstats[id].galmsg.a1g},
|
||||
{"t0g", g_svstats[id].galmsg.t0g},
|
||||
{"wn0g", g_svstats[id].galmsg.wn0g},
|
||||
idb.addValue(id, "gpsoffset", {{"a0g", svstat.galmsg.a0g},
|
||||
{"a1g", svstat.galmsg.a1g},
|
||||
{"t0g", svstat.galmsg.t0g},
|
||||
{"wn0g", svstat.galmsg.wn0g},
|
||||
{"delta", g_GSTGPSOffset}
|
||||
}, satUTCTime(id));
|
||||
|
||||
}
|
||||
}
|
||||
else if(nmm.type() == NavMonMessage::GalileoCnavType) {
|
||||
SatID id={2,(uint32_t) nmm.gc().gnsssv(), 8}; // E6
|
||||
idb.addValue(id, "galcnav", {{"msg", makeHexDump(nmm.gc().contents())}},
|
||||
nmm.localutcseconds());
|
||||
// ... no idea what this contains
|
||||
}
|
||||
else if(nmm.type() == NavMonMessage::GalileoFnavType) {
|
||||
basic_string<uint8_t> fnav((uint8_t*)nmm.gf().contents().c_str(), nmm.gf().contents().size());
|
||||
int sv = nmm.gf().gnsssv();
|
||||
SatID id={2,(uint32_t)sv,6}; // E5a
|
||||
|
||||
auto& svstat = g_svstats[id];
|
||||
svstat.gnss = id.gnss;
|
||||
auto oldgm = svstat.galmsg;
|
||||
|
||||
// CONVERSION XXX
|
||||
#if 0
|
||||
for(auto& ent : g_svstats) {
|
||||
// fmt::printf("%2d\t", ent.first);
|
||||
id=ent.first;
|
||||
if(ent.second.completeIOD() && ent.second.prevIOD.first >= 0) {
|
||||
auto& gm = svstat.galmsg;
|
||||
unsigned int wtype = gm.parseFnav(fnav);
|
||||
|
||||
ent.second.clearPrev();
|
||||
|
||||
if(wtype == 1 && svstat.galmsgTyped.count(1)) {
|
||||
const auto& old5gm = svstat.galmsgTyped[1];
|
||||
if(make_tuple(old5gm.e5ahs, old5gm.e1bhs, old5gm.e5advs, old5gm.e1bdvs) !=
|
||||
make_tuple(gm.e5ahs, gm.e1bhs, gm.e5advs, gm.e1bdvs)) {
|
||||
cout<<humanTime(id.gnss, svstat.wn(), svstat.tow())<<" src "<<nmm.sourceid()<<" Galileo "<<id.sv <<" sigid "<<id.sigid<<" change in health: ["<<humanBhs(old5gm.e5ahs)<<", "<<humanBhs(old5gm.e1bhs)<<", "<<(int)old5gm.e5advs <<", " << (int)old5gm.e1bdvs<<"] -> ["<< humanBhs(gm.e5ahs)<<", "<< humanBhs(gm.e1bhs)<<", "<< (int)gm.e5advs <<", " << (int)gm.e1bdvs<<"], lastseen "<<ephAge(old5gm.tow, gm.tow)/3600.0 <<" hours"<<endl;
|
||||
}
|
||||
}
|
||||
|
||||
svstat.galmsgTyped[wtype] = gm;
|
||||
|
||||
if(wtype == 1 || wtype == 2 || wtype == 3 || wtype == 4) {
|
||||
idb.addValue(id, "ephemeris", {{"iod-live", svstat.galmsg.iodnav},
|
||||
{"eph-age", ephAge(gm.tow, gm.getT0e())}}, satUTCTime(id));
|
||||
|
||||
int w = 1;
|
||||
for(; w < 5; ++w) {
|
||||
if(!svstat.galmsgTyped.count(w))
|
||||
break;
|
||||
if(w > 1 && svstat.galmsgTyped[w-1].iodnav != svstat.galmsgTyped[w].iodnav)
|
||||
break;
|
||||
}
|
||||
if(w==5) { // have complete new ephemeris
|
||||
|
||||
if(svstat.ephgalmsg.iodnav != svstat.galmsgTyped[2].iodnav) {
|
||||
// cout<<"New F/NAV ephemeris for "<<makeSatIDName(id)<<" iod " << svstat.galmsgTyped[1].iodnav << " t0e " << svstat.galmsgTyped[3].t0e << " af0 "<< gm.af0 <<" iod1 "<<svstat.galmsgTyped[1].iodnav;
|
||||
/*
|
||||
cout<<" iod2 "<<svstat.galmsgTyped[2].iodnav;
|
||||
cout<<" iod3 "<<svstat.galmsgTyped[3].iodnav;
|
||||
cout<<" iod4 "<<svstat.galmsgTyped[4].iodnav << endl;
|
||||
*/
|
||||
svstat.oldephgalmsg = svstat.ephgalmsg;
|
||||
svstat.ephgalmsg = svstat.galmsg;
|
||||
svstat.reportNewEphemeris(id, idb);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
svstat.perrecv[nmm.sourceid()].t = nmm.localutcseconds();
|
||||
|
||||
if(wtype >=1 && wtype <= 4) { // ephemeris
|
||||
if(wtype == 1) {
|
||||
idb.addValue(id, "sisa", {{"value", svstat.galmsg.sisa}}, satUTCTime(id));
|
||||
|
||||
idb.addValue(id, "galbgd", {
|
||||
{"BGDE1E5a", svstat.galmsg.BGDE1E5a},
|
||||
}, satUTCTime(id));
|
||||
|
||||
|
||||
idb.addValue(id, "galhealth", {
|
||||
{"e5ahs", svstat.galmsg.e5bhs},
|
||||
{"e5advs", svstat.galmsg.e5bdvs}
|
||||
}, satUTCTime(id));
|
||||
|
||||
|
||||
idb.addValue(id, "clock", {{"offset_ns", svstat.galmsg.getAtomicOffset(svstat.tow()).first},
|
||||
{"t0c", svstat.galmsg.t0c*60}, // getT0c()??
|
||||
{"af0", svstat.galmsg.af0},
|
||||
{"af1", svstat.galmsg.af1},
|
||||
{"af2", svstat.galmsg.af2}}, satUTCTime(id));
|
||||
|
||||
if(oldgm.af0 && oldgm.t0c != svstat.galmsg.t0c) {
|
||||
auto oldOffset = oldgm.getAtomicOffset(svstat.tow());
|
||||
auto newOffset = svstat.galmsg.getAtomicOffset(svstat.tow());
|
||||
svstat.timeDisco = oldOffset.first - newOffset.first;
|
||||
if(fabs(svstat.timeDisco) < 10000)
|
||||
idb.addValue(id, "clock_jump_ns", {
|
||||
{"jump", svstat.timeDisco},
|
||||
{"duration", ephAge(svstat.galmsg.t0c * 60, oldgm.t0c * 60)},
|
||||
{"old-af0", oldgm.af0},
|
||||
{"old-af1", oldgm.af1},
|
||||
{"old-af2", oldgm.af2},
|
||||
{"old-t0c", oldgm.t0c * 60},
|
||||
{"new-af0", svstat.galmsg.af0},
|
||||
{"new-af1", svstat.galmsg.af1},
|
||||
{"new-af2", svstat.galmsg.af2},
|
||||
{"new-t0c", svstat.galmsg.t0c * 60}
|
||||
|
||||
}, satUTCTime(id));
|
||||
}
|
||||
|
||||
idb.addValue(id, "iono", {
|
||||
{"ai0", svstat.galmsg.ai0},
|
||||
{"ai1", svstat.galmsg.ai1},
|
||||
{"ai2", svstat.galmsg.ai2},
|
||||
{"sf1", svstat.galmsg.sf1},
|
||||
{"sf2", svstat.galmsg.sf2},
|
||||
{"sf3", svstat.galmsg.sf3},
|
||||
{"sf4", svstat.galmsg.sf4},
|
||||
{"sf5", svstat.galmsg.sf5}}, satUTCTime(id));
|
||||
|
||||
}
|
||||
}
|
||||
else if(wtype == 4) {
|
||||
const auto& sv = g_svstats[id];
|
||||
g_GSTUTCOffset = sv.galmsg.getUTCOffset(sv.tow(), sv.wn()).first;
|
||||
idb.addValue(id, "utcoffset", {
|
||||
{"a0", sv.galmsg.a0},
|
||||
{"a1", sv.galmsg.a1},
|
||||
{"t0t", sv.galmsg.t0t},
|
||||
{"delta", g_GSTUTCOffset}
|
||||
},
|
||||
satUTCTime(id));
|
||||
|
||||
g_dtLS = sv.galmsg.dtLS;
|
||||
g_GSTGPSOffset = gm.getGPSOffset(gm.tow, gm.wn).first;
|
||||
idb.addValue(id, "gpsoffset", {{"a0g", svstat.galmsg.a0g},
|
||||
{"a1g", svstat.galmsg.a1g},
|
||||
{"t0g", svstat.galmsg.t0g},
|
||||
{"wn0g", svstat.galmsg.wn0g},
|
||||
{"delta", g_GSTGPSOffset}
|
||||
}, satUTCTime(id));
|
||||
}
|
||||
}
|
||||
else if(nmm.type() == NavMonMessage::ObserverPositionType) {
|
||||
g_srcpos[nmm.sourceid()].lastSeen = nmm.localutcseconds();
|
||||
|
@ -2288,7 +2469,8 @@ try
|
|||
}
|
||||
|
||||
else if(nmm.type()== NavMonMessage::DebuggingType) {
|
||||
// continue; // speedup
|
||||
if(doGalileoReportSpeedup)
|
||||
continue; // speedup
|
||||
|
||||
auto ret = parseTrkMeas(basic_string<uint8_t>((const uint8_t*)nmm.dm().payload().c_str(), nmm.dm().payload().size()));
|
||||
for(const auto& tss : ret) {
|
||||
|
@ -2382,8 +2564,22 @@ try
|
|||
auto oldOffset = getGPSAtomicOffset(gm.tow, oldgm);
|
||||
auto newOffset = getGPSAtomicOffset(gm.tow, gm);
|
||||
svstat.timeDisco = oldOffset.first - newOffset.first;
|
||||
if(fabs(svstat.timeDisco) < 10000)
|
||||
idb.addValue(id, "clock_jump_ns", {{"value", svstat.timeDisco}}, satUTCTime(id));
|
||||
if(fabs(svstat.timeDisco) < 10000) {
|
||||
idb.addValue(id, "clock_jump_ns", {
|
||||
{"jump", svstat.timeDisco},
|
||||
{"duration", ephAge(gm.t0c * 16, oldgm.t0c * 16)},
|
||||
{"old-af0", oldgm.af0},
|
||||
{"old-af1", oldgm.af1},
|
||||
{"old-af2", oldgm.af2},
|
||||
{"old-t0c", oldgm.t0c * 16},
|
||||
{"new-af0", gm.af0},
|
||||
{"new-af1", gm.af1},
|
||||
{"new-af2", gm.af2},
|
||||
{"new-t0c", gm.t0c * 16}
|
||||
|
||||
}, satUTCTime(id));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(frame==2) {
|
||||
|
@ -2435,7 +2631,8 @@ try
|
|||
if(rm.type == 1057 || rm.type == 1240) {
|
||||
for(const auto& ed : rm.d_ephs) {
|
||||
auto iter = g_svstats.find(ed.id);
|
||||
if(iter != g_svstats.end() && iter->second.completeIOD() && iter->second.liveIOD().getIOD() == ed.iod)
|
||||
// XXX NAVCAST ONLY
|
||||
if(iter != g_svstats.end() && iter->second.completeIOD() && iter->second.liveIOD().getIOD() == ed.iod && nmm.sourceid()==302)
|
||||
iter->second.rtcmEphDelta = ed;
|
||||
|
||||
idb.addValue(ed.id, "rtcm-eph-correction", {
|
||||
|
@ -2461,7 +2658,7 @@ try
|
|||
|
||||
for(const auto& cd : rm.d_clocks) {
|
||||
auto iter = g_svstats.find(cd.id);
|
||||
if(iter != g_svstats.end())
|
||||
if(iter != g_svstats.end() && nmm.sourceid()==302) /// XXX wrong
|
||||
iter->second.rtcmClockDelta = cd;
|
||||
|
||||
idb.addValue(cd.id, "rtcm-clock-correction", {
|
||||
|
@ -2478,10 +2675,18 @@ try
|
|||
|
||||
}
|
||||
}
|
||||
else if(rm.type == 1059 || rm.type == 1242) {
|
||||
for(const auto& dcb : rm.d_dcbs) {
|
||||
idb.addValue(dcb.first, "rtcm-dcb", {
|
||||
{"value", dcb.second}},
|
||||
nmm.localutcseconds(),
|
||||
nmm.sourceid());
|
||||
}
|
||||
}
|
||||
else if(rm.type == 1060 || rm.type == 1243) {
|
||||
for(const auto& ed : rm.d_ephs) {
|
||||
auto iter = g_svstats.find(ed.id);
|
||||
if(iter != g_svstats.end() && iter->second.completeIOD() && iter->second.liveIOD().getIOD() == ed.iod)
|
||||
if(iter != g_svstats.end() && iter->second.completeIOD() && iter->second.liveIOD().getIOD() == ed.iod && nmm.sourceid()==302)
|
||||
iter->second.rtcmEphDelta = ed;
|
||||
|
||||
idb.addValue(ed.id, "rtcm-eph-correction", {
|
||||
|
@ -2515,7 +2720,7 @@ try
|
|||
id.sv = rm.d_sv;
|
||||
id.sigid = 6; // seems reasonable for E5a
|
||||
|
||||
static map<pair<int, int>, int> lastT0e;
|
||||
static map<pair<int, int>, unsigned int> lastT0e;
|
||||
|
||||
pair<int, int> key(nmm.sourceid(), rm.d_sv);
|
||||
if(!lastT0e.count(key) || lastT0e[key] != eg.t0e) {
|
||||
|
@ -2549,7 +2754,7 @@ try
|
|||
|
||||
for(const auto& cd : rm.d_clocks) {
|
||||
auto iter = g_svstats.find(cd.id);
|
||||
if(iter != g_svstats.end())
|
||||
if(iter != g_svstats.end() && nmm.sourceid()==302)
|
||||
iter->second.rtcmClockDelta = cd;
|
||||
|
||||
idb.addValue(cd.id, "rtcm-clock-correction", {
|
||||
|
@ -2588,7 +2793,9 @@ try
|
|||
|
||||
}
|
||||
else if(nmm.type()== NavMonMessage::BeidouInavTypeD1) {
|
||||
// continue; // XXX speedup
|
||||
if(doGalileoReportSpeedup)
|
||||
continue; // speedup
|
||||
|
||||
try {
|
||||
SatID id{nmm.bid1().gnssid(), nmm.bid1().gnsssv(), nmm.bid1().sigid()};
|
||||
|
||||
|
@ -2623,7 +2830,7 @@ try
|
|||
auto newOffset = bm.getAtomicOffset(bm.sow);
|
||||
svstat.timeDisco = oldOffset.first - newOffset.first;
|
||||
if(fabs(svstat.timeDisco) < 10000)
|
||||
idb.addValue(id, "clock_jump_ns", {{"value", svstat.timeDisco}}, satUTCTime(id));
|
||||
idb.addValue(id, "clock_jump_ns", {{"jump", svstat.timeDisco}}, satUTCTime(id));
|
||||
}
|
||||
svstat.lastBeidouMessage1 = bm;
|
||||
}
|
||||
|
@ -2687,7 +2894,9 @@ try
|
|||
*/
|
||||
}
|
||||
else if(nmm.type()== NavMonMessage::GlonassInavType) {
|
||||
// continue; // XXX speedup
|
||||
if(doGalileoReportSpeedup)
|
||||
continue; // speedup
|
||||
|
||||
SatID id{nmm.gloi().gnssid(), nmm.gloi().gnsssv(), nmm.gloi().sigid()};
|
||||
auto& svstat = g_svstats[id];
|
||||
svstat.gnss = id.gnss;
|
||||
|
@ -2719,7 +2928,7 @@ try
|
|||
if(oldgm.taun && oldgm.taun != gm.taun) {
|
||||
if(gm.getGloTime() - oldgm.getGloTime() < 300) {
|
||||
svstat.timeDisco = gm.getTaunNS() - oldgm.getTaunNS();
|
||||
idb.addValue(id, "clock_jump_ns", {{"value", svstat.timeDisco}}, satUTCTime(id));
|
||||
idb.addValue(id, "clock_jump_ns", {{"jump", svstat.timeDisco}}, satUTCTime(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2754,7 +2963,9 @@ try
|
|||
// cout<<"GLONASS R"<<id.second<<" str "<<strno<<endl;
|
||||
}
|
||||
else if(nmm.type() == NavMonMessage::SBASMessageType) {
|
||||
// continue; // XXX speedup
|
||||
if(doGalileoReportSpeedup)
|
||||
continue; // speedup
|
||||
|
||||
auto& sb = g_sbas[nmm.sbm().gnsssv()];
|
||||
|
||||
sb.perrecv[nmm.sourceid()].last_seen = nmm.localutcseconds();
|
||||
|
@ -2901,3 +3112,33 @@ catch(std::exception& e)
|
|||
{
|
||||
cerr<<"Exiting because of fatal error "<<e.what()<<endl;
|
||||
}
|
||||
|
||||
|
||||
// dedup techniques
|
||||
#if 0
|
||||
/*
|
||||
struct DedupKey
|
||||
{
|
||||
SatID id;
|
||||
int wn;
|
||||
int tow;
|
||||
basic_string<uint8_t> contents;
|
||||
bool operator<(const DedupKey& rhs) const
|
||||
{
|
||||
return tie(id, wn, tow, contents) <
|
||||
tie(rhs.id, rhs.wn, rhs.tow, rhs.contents);
|
||||
}
|
||||
};
|
||||
|
||||
static set<DedupKey> s_dedup;
|
||||
DedupKey dk{id, (int)nmm.gf().gnsswn(), (int)nmm.gf().gnsstow(), inav};
|
||||
if(s_dedup.insert(dk).second == false) {
|
||||
// cout<<"Dedup"<<endl;
|
||||
continue;
|
||||
}
|
||||
if(s_dedup.size() > 10000)
|
||||
s_dedup.clear();
|
||||
*/
|
||||
// XXX conversion, may be vital
|
||||
// g_svstats[id].wn = nmm.gf().gnsswn();
|
||||
#endif
|
||||
|
|
|
@ -49,6 +49,8 @@ struct SVStat
|
|||
GalileoMessage ephgalmsg, galmsg, oldephgalmsg;
|
||||
// internal
|
||||
map<int, GalileoMessage> galmsgTyped;
|
||||
bool osnma{false};
|
||||
time_t osnmaTime{0};
|
||||
|
||||
// Glonass
|
||||
GlonassMessage ephglomsg, glonassMessage, oldephglomsg;
|
||||
|
|
103
navrecv.cc
103
navrecv.cc
|
@ -16,6 +16,7 @@
|
|||
#include "version.hh"
|
||||
#include <netinet/tcp.h>
|
||||
#include "navmon.hh"
|
||||
#include <mutex>
|
||||
|
||||
static char program[]="navrecv";
|
||||
|
||||
|
@ -140,8 +141,94 @@ void writeToDisk(time_t s, uint64_t sourceid, std::string_view message)
|
|||
}
|
||||
}
|
||||
|
||||
// This is used to report clients, so we can log them
|
||||
// The idea is that cleanup runs from the Sentinel which, when destroyed, will remove the entry
|
||||
struct ClientKeeper
|
||||
{
|
||||
struct ClientStatus
|
||||
{
|
||||
bool oldProtocol;
|
||||
time_t lastMessage;
|
||||
int station;
|
||||
uint64_t messages{0};
|
||||
};
|
||||
|
||||
struct Sentinel
|
||||
{
|
||||
Sentinel(ClientKeeper* parent, const ComboAddress& us) : d_parent(parent), d_us(us)
|
||||
{
|
||||
}
|
||||
|
||||
Sentinel(Sentinel&& s)
|
||||
{
|
||||
// cerr<<"Moved!"<<endl;
|
||||
d_parent = s.d_parent;
|
||||
d_us = s.d_us;
|
||||
s.d_parent=0;
|
||||
}
|
||||
|
||||
~Sentinel()
|
||||
{
|
||||
// cerr<<"Destructor"<<endl;
|
||||
if(d_parent) {
|
||||
d_parent->remove(d_us);
|
||||
}
|
||||
else
|
||||
; //cerr<<" but we were moved already!\n";
|
||||
|
||||
}
|
||||
void update(int station, bool oldProtocol)
|
||||
{
|
||||
time_t now = time(0);
|
||||
std::lock_guard<std::mutex> l(d_parent->d_mut);
|
||||
ClientStatus& cs = d_parent->d_clients[d_us];
|
||||
cs.station = station;
|
||||
cs.lastMessage = now;
|
||||
cs.messages++;
|
||||
cs.oldProtocol = oldProtocol;
|
||||
}
|
||||
ClientKeeper* d_parent;
|
||||
ComboAddress d_us;
|
||||
};
|
||||
|
||||
Sentinel reportClient(const ComboAddress& client)
|
||||
{
|
||||
Sentinel s2(this, client);
|
||||
|
||||
std::lock_guard<std::mutex> l(d_mut);
|
||||
d_clients[client];
|
||||
return s2;
|
||||
}
|
||||
|
||||
void remove(const ComboAddress& client)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(d_mut);
|
||||
d_clients.erase(client);
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
std::lock_guard<std::mutex> l(d_mut);
|
||||
string format("{:<50}{:<5}{:<10}{:<10}{:<10}\n");
|
||||
ofstream out("clients.bak");
|
||||
time_t now=time(0);
|
||||
out<< fmt::format(format, "IP Address", "ID", "Protocol", "Messages", "Age");
|
||||
for(const auto& c : d_clients) {
|
||||
out << fmt::format(format, c.first.toStringWithPort(), c.second.station, c.second.oldProtocol ? "Old" : "New", c.second.messages, now-c.second.lastMessage);
|
||||
}
|
||||
out.close();
|
||||
unlink("clients.txt");
|
||||
rename("clients.bak", "clients.txt");
|
||||
}
|
||||
|
||||
map<ComboAddress, ClientStatus> d_clients;
|
||||
std::mutex d_mut;
|
||||
};
|
||||
|
||||
ClientKeeper g_ckeeper;
|
||||
|
||||
// note that this moves the socket
|
||||
void recvSession2(Socket&& uns, ComboAddress client)
|
||||
void recvSession2(Socket&& uns, ComboAddress client, ClientKeeper::Sentinel& sentinel)
|
||||
{
|
||||
string secret = SRead(uns, 8); // ignored for now
|
||||
cerr << "Entering compressed session for "<<client.toStringWithPort()<<endl;
|
||||
|
@ -177,6 +264,7 @@ void recvSession2(Socket&& uns, ComboAddress client)
|
|||
memcpy(&denum, num.c_str(), 4);
|
||||
denum = htonl(denum);
|
||||
// cerr<<"Received message "<<denum<< " "<<nmm.localutcseconds()<<" " << nmm.localutcnanoseconds()/1000000000.0<<endl;
|
||||
sentinel.update(nmm.sourceid(), false);
|
||||
writeToDisk(nmm.localutcseconds(), nmm.sourceid(), out);
|
||||
|
||||
if(first) {
|
||||
|
@ -201,6 +289,9 @@ void recvSession(int s, ComboAddress client)
|
|||
SSetsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 1); // saves file descriptors
|
||||
cerr<<"Receiving messages from "<<client.toStringWithPort()<<endl;
|
||||
bool first=true;
|
||||
|
||||
ClientKeeper::Sentinel sentinel=g_ckeeper.reportClient(client);
|
||||
|
||||
for(int count=0;;++count) {
|
||||
string part=SRead(sock, 4);
|
||||
if(part.empty()) {
|
||||
|
@ -209,7 +300,7 @@ void recvSession(int s, ComboAddress client)
|
|||
}
|
||||
if(part != "bert") {
|
||||
if(part == "RNIE")
|
||||
return recvSession2(std::move(sock), client); // protocol v2, socket is moved cuz cleanup is special
|
||||
return recvSession2(std::move(sock), client, sentinel); // protocol v2, socket is moved cuz cleanup is special
|
||||
cerr << "Message "<<count<<", wrong magic from "<<client.toStringWithPort()<<": "<<makeHexDump(part)<<endl;
|
||||
break;
|
||||
}
|
||||
|
@ -225,6 +316,7 @@ void recvSession(int s, ComboAddress client)
|
|||
part = SRead(s, len);
|
||||
if(part.size() != len) {
|
||||
cerr<<"Mismatch, "<<part.size()<<", len "<<len<<endl;
|
||||
// XX AND THEN WHAT??
|
||||
}
|
||||
out += part;
|
||||
|
||||
|
@ -234,6 +326,7 @@ void recvSession(int s, ComboAddress client)
|
|||
cerr<<"\tstation: "<<nmm.sourceid() << endl;
|
||||
first=false;
|
||||
}
|
||||
sentinel.update(nmm.sourceid(), true);
|
||||
writeToDisk(nmm.localutcseconds(), nmm.sourceid(), out);
|
||||
}
|
||||
}
|
||||
|
@ -285,8 +378,10 @@ int main(int argc, char** argv)
|
|||
|
||||
thread recvThread(recvListener, std::move(receiver), recvaddr);
|
||||
recvThread.detach();
|
||||
|
||||
|
||||
sleep(5);
|
||||
for(;;) {
|
||||
sleep(1);
|
||||
g_ckeeper.dump();
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
|
|
14
nmmsender.cc
14
nmmsender.cc
|
@ -175,16 +175,22 @@ void NMMSender::sendTCPThread(Destination* d)
|
|||
|
||||
|
||||
void NMMSender::emitNMM(const NavMonMessage& nmm)
|
||||
{
|
||||
string out;
|
||||
nmm.SerializeToString(& out);
|
||||
emitNMM(out);
|
||||
}
|
||||
|
||||
void NMMSender::emitNMM(const std::string& out)
|
||||
{
|
||||
for(auto& d : d_dests) {
|
||||
d->emitNMM(nmm, d_compress);
|
||||
d->emitNMM(out, d_compress);
|
||||
}
|
||||
}
|
||||
|
||||
void NMMSender::Destination::emitNMM(const NavMonMessage& nmm, bool compressed)
|
||||
|
||||
void NMMSender::Destination::emitNMM(const std::string& out, bool compressed)
|
||||
{
|
||||
string out;
|
||||
nmm.SerializeToString(& out);
|
||||
string msg;
|
||||
if(dst.empty() || !compressed)
|
||||
msg="bert";
|
||||
|
|
|
@ -16,7 +16,7 @@ class NMMSender
|
|||
|
||||
std::deque<std::string> queue;
|
||||
std::mutex mut;
|
||||
void emitNMM(const NavMonMessage& nmm, bool compress);
|
||||
void emitNMM(const std::string& out, bool compress);
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -45,6 +45,7 @@ public:
|
|||
void sendTCPThread(Destination* d);
|
||||
|
||||
void emitNMM(const NavMonMessage& nmm);
|
||||
void emitNMM(const std::string& out);
|
||||
bool d_debug{false};
|
||||
bool d_compress{false}; // set BEFORE launch
|
||||
bool d_pleaseQuit{false};
|
||||
|
|
117
reporter.cc
117
reporter.cc
|
@ -84,6 +84,8 @@ private:
|
|||
struct IntervalStat
|
||||
{
|
||||
std::optional<int> unhealthy;
|
||||
std::optional<int> dataunhealthy;
|
||||
std::optional<int> osnma;
|
||||
std::optional<int> sisa;
|
||||
bool ripe{false};
|
||||
bool expired{false};
|
||||
|
@ -95,6 +97,7 @@ struct IntervalStat
|
|||
map<SatID, map<time_t,IntervalStat>> g_stats;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
try
|
||||
{
|
||||
MiniCurl mc;
|
||||
MiniCurl::MiniCurlHeaders mch;
|
||||
|
@ -111,15 +114,19 @@ int main(int argc, char **argv)
|
|||
string sp3src("default");
|
||||
int gnssid=2;
|
||||
int rtcmsrc=300;
|
||||
int galwn=-1;
|
||||
string influxserver="http://127.0.0.1:8086";
|
||||
app.add_flag("--version", doVERSION, "show program version and copyright");
|
||||
app.add_option("--period,-p", periodarg, "period over which to report (1h, 1w)");
|
||||
app.add_option("--begin,-b", beginarg, "Beginning");
|
||||
app.add_option("--end,-e", endarg, "End");
|
||||
app.add_option("--gal-wn", galwn, "Galileo week number to report on");
|
||||
app.add_option("--sp3src", sp3src, "Identifier of SP3 source");
|
||||
app.add_option("--rtcmsrc", rtcmsrc, "Identifier of RTCM source");
|
||||
app.add_option("--sigid,-s", sigid, "Signal identifier. 1 or 5 for Galileo.");
|
||||
app.add_option("--gnssid,-g", gnssid, "gnssid, 0 GPS, 2 Galileo");
|
||||
app.add_option("--influxdb", influxDBName, "Name of influxdb database");
|
||||
app.add_option("--influxserver", influxserver, "Address of influx server");
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch(const CLI::Error &e) {
|
||||
|
@ -131,7 +138,11 @@ int main(int argc, char **argv)
|
|||
exit(0);
|
||||
}
|
||||
|
||||
if(beginarg.empty() && endarg.empty())
|
||||
if(galwn>= 0) {
|
||||
time_t w = utcFromGST(galwn, 0);
|
||||
period = "time >= '"+influxTime(w)+"' and time < '"+influxTime(w+7*86400) +"'";
|
||||
}
|
||||
else if(beginarg.empty() && endarg.empty())
|
||||
period = "time > now() - "+periodarg;
|
||||
else {
|
||||
period = "time > '"+ beginarg +"' and time <= '" + endarg +"'";
|
||||
|
@ -142,13 +153,16 @@ int main(int argc, char **argv)
|
|||
|
||||
// auto res = mc.getURL(url + mc.urlEncode("select distinct(value) from sisa where "+period+" and sigid='"+to_string(sigid)+"' group by gnssid,sv,sigid,time(10m)"));
|
||||
|
||||
|
||||
|
||||
string url="http://127.0.0.1:8086/query?db="+influxDBName+"&epoch=s&q=";
|
||||
if(influxserver.find("http"))
|
||||
influxserver="http://"+influxserver;
|
||||
if(influxserver.empty() || influxserver[influxserver.size()-1]!='/')
|
||||
influxserver+="/";
|
||||
string url=influxserver+"query?db="+influxDBName+"&epoch=s&q=";
|
||||
string sisaname = (gnssid==2) ? "sisa" : "gpsura";
|
||||
string query="select distinct(value) from "+sisaname+" where "+period+" and sigid='"+to_string(sigid)+"' group by gnssid,sv,sigid,time(10m)";
|
||||
|
||||
cout<<"query: "<<query<<endl;
|
||||
cout<<"url: "<<(url + mc.urlEncode(query))<<endl;
|
||||
auto res = mc.getURL(url + mc.urlEncode(query));
|
||||
|
||||
auto j = nlohmann::json::parse(res);
|
||||
|
@ -174,13 +188,44 @@ int main(int argc, char **argv)
|
|||
const auto& tags=sv["tags"];
|
||||
SatID id{(unsigned int)std::stoi((string)tags["gnssid"]), (unsigned int)std::stoi((string)tags["sv"]), (unsigned int)std::stoi((string)tags["sigid"])};
|
||||
|
||||
|
||||
for(const auto& v : sv["values"]) {
|
||||
auto healthy = (int)v[1];
|
||||
g_stats[id][(int)v[0]].unhealthy = healthy; // hngg
|
||||
}
|
||||
}
|
||||
|
||||
if(gnssid == 2) {
|
||||
res = mc.getURL(url + mc.urlEncode("select distinct(e1bdvs) from galhealth where "+period+" and sigid='"+to_string(sigid)+"' group by gnssid,sv,sigid,time(10m)"));
|
||||
j = nlohmann::json::parse(res);
|
||||
|
||||
for(const auto& sv : j["results"][0]["series"]) {
|
||||
const auto& tags=sv["tags"];
|
||||
SatID id{(unsigned int)std::stoi((string)tags["gnssid"]), (unsigned int)std::stoi((string)tags["sv"]), (unsigned int)std::stoi((string)tags["sigid"])};
|
||||
|
||||
for(const auto& v : sv["values"]) {
|
||||
auto dhealthy = (int)v[1]; // if true, "working without guarantee"
|
||||
g_stats[id][(int)v[0]].dataunhealthy = dhealthy;
|
||||
}
|
||||
}
|
||||
}
|
||||
res = mc.getURL(url + mc.urlEncode("select count(\"field\") from osnma where "+period+" and sigid='"+to_string(sigid)+"' group by gnssid,sv,sigid,time(10m)"));
|
||||
j = nlohmann::json::parse(res);
|
||||
|
||||
for(const auto& sv : j["results"][0]["series"]) {
|
||||
const auto& tags=sv["tags"];
|
||||
SatID id{(unsigned int)std::stoi((string)tags["gnssid"]), (unsigned int)std::stoi((string)tags["sv"]), (unsigned int)std::stoi((string)tags["sigid"])};
|
||||
|
||||
|
||||
for(const auto& v : sv["values"]) {
|
||||
auto osnma = (int)v[1];
|
||||
if(!g_stats[id][(int)v[0]].osnma)
|
||||
g_stats[id][(int)v[0]].osnma = osnma;
|
||||
else
|
||||
(*g_stats[id][(int)v[0]].osnma) += osnma;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
res = mc.getURL(url + mc.urlEncode("select max(\"eph-age\") from ephemeris where "+period+" and sigid='"+to_string(sigid)+"' group by gnssid,sv,sigid,time(10m)"));
|
||||
j = nlohmann::json::parse(res);
|
||||
for(const auto& sv : j["results"][0]["series"]) {
|
||||
|
@ -503,13 +548,51 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
/////
|
||||
string dishesQuery = "select iod,sv from \"ephemeris-actual\" where "+period+" and sigid='"+to_string(sigid)+"' and gnssid='"+to_string(gnssid)+"' and iod < 128";
|
||||
cout<<"dishesquery: "<<dishesQuery<<endl;
|
||||
res = mc.getURL(url + mc.urlEncode(dishesQuery));
|
||||
cout<<res<<endl;
|
||||
j = nlohmann::json::parse(res);
|
||||
map<time_t, set<int>> dishcount;
|
||||
set<int> totsvs;
|
||||
for(const auto& sv : j["results"][0]["series"]) {
|
||||
for(const auto& v : sv["values"]) {
|
||||
try {
|
||||
int sv = (unsigned int)std::stoi((string)v[2]);
|
||||
int t = (int)v[0];
|
||||
// t &= (~31);
|
||||
dishcount[t].insert(sv);
|
||||
totsvs.insert(sv);
|
||||
}
|
||||
catch(exception& e) {
|
||||
cerr<<"error: "<<e.what()<<endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
map<time_t, unsigned int> maxcounts;
|
||||
for(const auto& dc : dishcount) {
|
||||
auto& bin = maxcounts[dc.first - (dc.first % 3600)];
|
||||
if(bin < dc.second.size())
|
||||
bin = dc.second.size();
|
||||
cout << dc.first<<" "<<humanTimeShort(dc.first) <<", " << fmt::sprintf("%2d", dc.second.size())<<": ";
|
||||
for(const auto& n : totsvs) {
|
||||
if(dc.second.count(n))
|
||||
cout<<fmt::sprintf("%2d ", n);
|
||||
else
|
||||
cout<<" ";
|
||||
}
|
||||
cout<<"\n";
|
||||
}
|
||||
|
||||
ofstream hrcounts("hrcounts.csv");
|
||||
hrcounts<<"timestamp,dishcount\n";
|
||||
for(const auto& mc: maxcounts)
|
||||
hrcounts<<mc.first<<","<<mc.second<<"\n";
|
||||
|
||||
/////////////////////
|
||||
|
||||
g_stats.erase({2,14,1});
|
||||
g_stats.erase({2,18,1});
|
||||
g_stats.erase({2,14,5});
|
||||
g_stats.erase({2,18,5});
|
||||
/*
|
||||
g_stats[{2,19,1}];
|
||||
*/
|
||||
|
@ -568,7 +651,10 @@ int main(int argc, char **argv)
|
|||
else if(*i.second.unhealthy==3)
|
||||
testing++;
|
||||
else {
|
||||
if(i.second.sisa) {
|
||||
if(i.second.dataunhealthy && *i.second.dataunhealthy) { // this is 'working without guarantee'
|
||||
unhealthy++;
|
||||
}
|
||||
else if(i.second.sisa) {
|
||||
if(*i.second.sisa == 255)
|
||||
napa++;
|
||||
else
|
||||
|
@ -637,6 +723,9 @@ int main(int argc, char **argv)
|
|||
cout<<endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
cout<<"------------------------------------------------------------------------------------------"<<endl;
|
||||
cout<<fmt::sprintf("Tot: %6.2f%% unobserved, %6.2f%% unhealthy, %6.2f%% healthy, %6.2f%% testing, %6.2f%% napa, %6.2f%% ripe, %6.2f%% expired",
|
||||
100.0*(totunobserved)/maxintervals/g_stats.size(),
|
||||
|
@ -647,7 +736,7 @@ int main(int argc, char **argv)
|
|||
100.0*totripe/maxintervals/g_stats.size(),
|
||||
100.0*totexpired/maxintervals/g_stats.size());
|
||||
|
||||
texstream<<fmt::sprintf("Tot & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%%\\\\",
|
||||
texstream<<fmt::sprintf("\\hline\nTot & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%%\\\\",
|
||||
100.0*(totunobserved)/maxintervals/g_stats.size(),
|
||||
100.0*totunhealthy/maxintervals/g_stats.size(),
|
||||
100.0*tothealthy/maxintervals/g_stats.size(),
|
||||
|
@ -664,5 +753,9 @@ int main(int argc, char **argv)
|
|||
cout<<endl;
|
||||
|
||||
}
|
||||
|
||||
catch(exception& e)
|
||||
{
|
||||
cerr<<"Fatal error: "<<e.what()<<endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
|
3
rinex.cc
3
rinex.cc
|
@ -156,6 +156,9 @@ G02 2019 12 16 00 00 00-3.670863807201E-04-7.389644451905E-12 0.000000000000E+00
|
|||
for(int n=1 ; n < 7; ++n) {
|
||||
if(!gzgets(d_fp, line, sizeof(line)))
|
||||
return false;
|
||||
if(n==1) {
|
||||
entry.iodnav = getRINEXValue(line, 4);
|
||||
}
|
||||
if(n==3) {
|
||||
double toe = getRINEXValue(line, 4);
|
||||
entry.toe = toe;
|
||||
|
|
1
rinex.hh
1
rinex.hh
|
@ -18,6 +18,7 @@ struct RINEXEntry
|
|||
int health;
|
||||
int toe;
|
||||
int tow;
|
||||
int iodnav;
|
||||
double af0, af1, af2;
|
||||
double clkflags;
|
||||
double BGDE1E5a, BGDE1E5b;
|
||||
|
|
|
@ -9,6 +9,7 @@ struct Value
|
|||
optional<int> af0Inav;
|
||||
optional<int> af0Fnav;
|
||||
int af1;
|
||||
int iod;
|
||||
optional<int> BGDE1E5a;
|
||||
optional<int> BGDE1E5b;
|
||||
};
|
||||
|
@ -30,6 +31,7 @@ int main(int argc, char** argv)
|
|||
s.af1 = rint(ldexp(e.af1,46));
|
||||
s.BGDE1E5a = rint(ldexp(e.BGDE1E5a,32));
|
||||
s.BGDE1E5b = rint(ldexp(e.BGDE1E5b,32));
|
||||
s.iod = e.iodnav;
|
||||
}
|
||||
else {
|
||||
s.af0Fnav = rint(ldexp(e.af0,34));
|
||||
|
@ -41,10 +43,10 @@ int main(int argc, char** argv)
|
|||
}
|
||||
|
||||
}
|
||||
cout<<"timestamp sv af0fnav af0inav af1 bgde1e5a bgde1e5b\n";
|
||||
cout<<"timestamp sv iod af0fnav af0inav af1 bgde1e5a bgde1e5b\n";
|
||||
for(const auto& s : satmap) {
|
||||
if(s.second.af0Fnav.has_value() && s.second.af0Inav.has_value() && s.second.BGDE1E5a.has_value() && s.second.BGDE1E5b.has_value())
|
||||
cout << s.first.first<<" " <<s.first.second<<" " <<
|
||||
cout << s.first.first<<" " <<s.first.second<<" " << s.second.iod<<" "<<
|
||||
*s.second.af0Fnav << " " << *s.second.af0Inav <<" " << s.second.af1<<" " <<*s.second.BGDE1E5a <<" " << *s.second.BGDE1E5b << "\n";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,8 +85,6 @@ auto worker(HanderOuter<string>* ho)
|
|||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
||||
|
||||
ifstream filefile(argv[1]);
|
||||
string fname;
|
||||
deque<string> files;
|
||||
|
|
54
rtcm.cc
54
rtcm.cc
|
@ -6,7 +6,8 @@ using namespace std;
|
|||
|
||||
void RTCMMessage::parse(const std::string& str)
|
||||
{
|
||||
memset(&d_gm, 0, sizeof(d_gm));
|
||||
d_gm={};
|
||||
// memset(&d_gm, 0, sizeof(d_gm));
|
||||
auto gbu=[&str](int offset, int bits) {
|
||||
return getbitu((const unsigned char*)str.c_str(), offset, bits);
|
||||
};
|
||||
|
@ -262,7 +263,56 @@ DF 385: Full seconds since the beginning of the GPS week
|
|||
setbitu(rtcm->buff,i, 1,osdvs2 ); i+= 1; /* E1 DVS */
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
else if(type == 1059 || type == 1242) { // GPS/Galileo bias
|
||||
int off = 0;
|
||||
int msgnum = gbum(off, 12);
|
||||
|
||||
int gpstime = gbum(off, 20);
|
||||
int uinterval = gbum(off, 4);
|
||||
int mmi = gbum(off, 1);
|
||||
int iodssr = gbum(off, 4);
|
||||
int ssrprov = gbum(off, 16);
|
||||
int ssrsol = gbum(off, 4);
|
||||
int numsats = gbum(off, 6);
|
||||
|
||||
// cout <<"msgnum "<<msgnum<<" gpstime " << gpstime<<" numsats "<< numsats<<endl;
|
||||
d_dcbs.clear();
|
||||
for(int n=0; n < numsats; ++n) {
|
||||
int gpsid = gbum(off, 6);
|
||||
int numdcbs = gbum(off, 5);
|
||||
// cout<<" "<< (type==1059 ? "G" : "E") <<gpsid<<" has "<<numdcbs <<" DCBs\n";
|
||||
SatID id;
|
||||
id.gnss = (type==1059 ? 0 : 2); // GPS or Galileo
|
||||
id.sv = gpsid;
|
||||
for(int m = 0 ; m < numdcbs; ++m) {
|
||||
int sig = gbum(off, 5);
|
||||
id.sigid = sig;
|
||||
int dcb = gbsm(off, 14); // 0.01 meter
|
||||
d_dcbs[id] = 0.01*dcb;
|
||||
// cout<<" sig "<<sig <<" dcb " << dcb*0.01 << "\n";
|
||||
|
||||
/*
|
||||
Indicator to specify the GPS signal and tracking mode:
|
||||
0 - L1 C/A
|
||||
1- L1 P
|
||||
2- L1 Z-tracking and similar (AS on)
|
||||
3 - Reserved
|
||||
4 - Reserved
|
||||
5 - L2 C/A
|
||||
6 - L2 L1(C/A)+(P2-P1) (semi-codeless)
|
||||
7 - L2 L2C (M)
|
||||
8 - L2 L2C (L)
|
||||
9 - L2 L2C (M+L)
|
||||
10 - L2 P
|
||||
11 - L2 Z-tracking and similar (AS on)
|
||||
12 - Reserved
|
||||
13 - Reserved
|
||||
14 - L5 I
|
||||
15 - L5 Q
|
||||
>15 - Reserved.
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
rtcm.hh
2
rtcm.hh
|
@ -4,6 +4,7 @@
|
|||
#include "navmon.hh"
|
||||
#include <vector>
|
||||
#include "galileo.hh"
|
||||
#include <map>
|
||||
|
||||
struct RTCMFrame
|
||||
{
|
||||
|
@ -52,6 +53,7 @@ struct RTCMMessage
|
|||
|
||||
std::vector<EphemerisDelta> d_ephs;
|
||||
std::vector<ClockDelta> d_clocks;
|
||||
std::map<SatID, double> d_dcbs;
|
||||
GalileoMessage d_gm;
|
||||
int d_sv;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,603 @@
|
|||
#include <string>
|
||||
#include "navmon.hh"
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "bits.hh"
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "galileo.hh"
|
||||
#include "nmmsender.hh"
|
||||
#include "CLI/CLI.hpp"
|
||||
#include "swrappers.hh"
|
||||
#include "sclasses.hh"
|
||||
#include "version.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
static int sepsig2ubx(int sep)
|
||||
{
|
||||
if(sep == 1) // GPS L1P
|
||||
return 0;
|
||||
else if(sep == 2) // GPS L2P
|
||||
return 4;
|
||||
else if(sep== 4) // GPS L5
|
||||
return 7; // ??
|
||||
else if(sep == 17)
|
||||
return 1; // Galileo E1
|
||||
else if(sep == 19)
|
||||
return 8; // Galileo E6
|
||||
else if(sep == 20)
|
||||
return 6; // Galileo E5a
|
||||
else if(sep==21)
|
||||
return 5; // Galileo E5b
|
||||
else if(sep==22)
|
||||
return 6; // Galileo "AltBoc"
|
||||
|
||||
else if(sep==0)
|
||||
return 0; // GPS L1
|
||||
else if(sep==3)
|
||||
return 4; // GPS L2c
|
||||
else throw runtime_error("Asked to convert unknown Septentrio signal id "+to_string(sep));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct SEPMessage
|
||||
{
|
||||
SEPMessage(const std::basic_string<uint8_t>& str) : d_store(str) {}
|
||||
|
||||
uint16_t getID() // includes revision
|
||||
{
|
||||
return d_store[4] + 256*d_store[5];
|
||||
}
|
||||
|
||||
uint16_t getIDBare() // includes revision
|
||||
{
|
||||
return getID() & 0xFFF;
|
||||
}
|
||||
|
||||
|
||||
std::basic_string<uint8_t> getPayload()
|
||||
{
|
||||
return d_store.substr(8);
|
||||
}
|
||||
std::basic_string<uint8_t> d_store;
|
||||
};
|
||||
|
||||
/* format:
|
||||
|
||||
01 23 45 67
|
||||
$@ CRC blk-id len [len-8 bytes]
|
||||
|
|
||||
multiple of four
|
||||
|
||||
bits 0-12 of blk-id are the type
|
||||
*/
|
||||
|
||||
std::pair<SEPMessage, struct timeval> getSEPMessage(int fd, double* timeout)
|
||||
{
|
||||
uint8_t marker[2]={0};
|
||||
bool hadskip=false;
|
||||
for(;;) {
|
||||
marker[0] = marker[1];
|
||||
int res = readn2Timeout(fd, marker+1, 1, timeout);
|
||||
|
||||
if(res < 0) {
|
||||
cerr<<"Readn2Timeout failed: "<<strerror(errno)<<endl;
|
||||
throw EofException();
|
||||
}
|
||||
if(marker[0]=='$' && marker[1]=='@') { // bingo
|
||||
if(hadskip) {
|
||||
cerr<<"\n";
|
||||
hadskip=false;
|
||||
}
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
basic_string<uint8_t> msg;
|
||||
msg.append(marker, 2); // 0,1
|
||||
uint8_t b[6];
|
||||
readn2Timeout(fd, b, 6, timeout);
|
||||
msg.append(b, 6); // crc id len
|
||||
|
||||
// 0,1 = crc, 2-3 = marker, 4, 5
|
||||
// uint16_t blkid = htons(b[2] + 256*b[3]);
|
||||
uint16_t len = b[4] + 256*b[5];
|
||||
// cerr<<"Got message of type "<<getbitu((uint8_t*)&blkid, 0, 12)<<", revision "<<
|
||||
// getbitu((uint8_t*)&blkid, 12, 4)<<" ("<<ntohs(blkid)<<"), len= "<<len<<endl;
|
||||
|
||||
uint8_t buffer[len-8];
|
||||
res=readn2Timeout(fd, buffer, len-8, timeout);
|
||||
|
||||
msg.append(buffer, len - 8); // checksum
|
||||
return make_pair(SEPMessage(msg), tv);
|
||||
}
|
||||
else if(marker[1] != '$') {
|
||||
hadskip=true;
|
||||
cerr<<".";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static char program[]="septool";
|
||||
uint16_t g_srcid{0};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
time_t starttime=time(0);
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
vector<string> destinations;
|
||||
g_dtLS = 18;
|
||||
bool doVERSION{false}, doSTDOUT{false};
|
||||
CLI::App app(program);
|
||||
string sourceaddr;
|
||||
bool quiet{false};
|
||||
app.add_option("--source", sourceaddr, "Connect to this IP address:port to source SBF (otherwise stdin)");
|
||||
app.add_option("--destination,-d", destinations, "Send output to this IPv4/v6 address");
|
||||
app.add_option("--station", g_srcid, "Station id")->required();
|
||||
app.add_option("--quiet", quiet, "Don't emit noise");
|
||||
app.add_flag("--version", doVERSION, "show program version and copyright");
|
||||
app.add_flag("--stdout", doSTDOUT, "Emit output to stdout");
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch(const CLI::Error &e) {
|
||||
return app.exit(e);
|
||||
}
|
||||
|
||||
if(doVERSION) {
|
||||
showVersion(program, g_gitHash);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
NMMSender ns;
|
||||
ns.d_debug = true;
|
||||
for(const auto& s : destinations) {
|
||||
auto res=resolveName(s, true, true);
|
||||
if(res.empty()) {
|
||||
cerr<<"Unable to resolve '"<<s<<"' as destination for data, exiting"<<endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
ns.addDestination(s); // ComboAddress(s, 29603));
|
||||
}
|
||||
if(doSTDOUT)
|
||||
ns.addDestination(1);
|
||||
|
||||
int srcfd=0;
|
||||
if(!sourceaddr.empty()) {
|
||||
ComboAddress src(sourceaddr);
|
||||
|
||||
srcfd = socket(src.sin4.sin_family, SOCK_STREAM, 0);
|
||||
if(srcfd < 0)
|
||||
unixDie("making socket for SBF connection");
|
||||
cerr<<"Connecting to "<< src.toStringWithPort()<<" to source data..";
|
||||
SConnectWithTimeout(srcfd, src, 5);
|
||||
cerr<<" done"<<endl;
|
||||
}
|
||||
ns.d_compress = true;
|
||||
ns.launch();
|
||||
cerr<<"Station "<<g_srcid<<endl;
|
||||
for(;;) {
|
||||
double to=1000;
|
||||
auto res = getSEPMessage(srcfd, &to);
|
||||
if(!quiet)
|
||||
cerr<<res.first.getID()<<" - " <<res.first.getIDBare() << endl;
|
||||
if(res.first.getID() == 4023) { // I/NAV
|
||||
auto str = res.first.getPayload();
|
||||
struct SEPInav
|
||||
{
|
||||
uint32_t towMsec;
|
||||
uint16_t wn;
|
||||
uint8_t sv;
|
||||
uint8_t crcPassed;
|
||||
uint8_t viterbiCount;
|
||||
uint8_t src;
|
||||
uint8_t ign1;
|
||||
uint8_t rxChannel;
|
||||
uint8_t navBits[32];
|
||||
} __attribute__((packed));
|
||||
SEPInav si;
|
||||
memcpy(&si, str.c_str(), sizeof(si));
|
||||
// cerr<<"tow "<<si.towMsec /1000<<" wn "<<si.wn <<" sv " << (int) si.sv - 70<<" ";
|
||||
|
||||
if(!si.crcPassed) {
|
||||
cerr<<"I/NAV CRC error, skipping"<<endl;
|
||||
continue;
|
||||
}
|
||||
int sigid = si.src & 31;
|
||||
|
||||
std::string inav((char*)si.navBits, 32);
|
||||
// cerr<<makeHexDump(inav)<<endl;
|
||||
|
||||
|
||||
// byte order adjustment
|
||||
std::basic_string<uint8_t> payload;
|
||||
|
||||
for(unsigned int i = 0 ; i < 8; ++i) {
|
||||
payload.append(1, si.navBits[4 * i + 3]);
|
||||
payload.append(1, si.navBits[4 * i + 2]);
|
||||
payload.append(1, si.navBits[4 * i + 1]);
|
||||
payload.append(1, si.navBits[4 * i + 0]);
|
||||
}
|
||||
|
||||
|
||||
basic_string<uint8_t> inav2;
|
||||
// copy in the even page
|
||||
for(int n = 0 ; n < 14; ++n)
|
||||
inav2.append(1, getbitu(payload.c_str(), 2 + n*8, 8));
|
||||
// odd page
|
||||
for(int n = 0 ; n < 2; ++n)
|
||||
inav2.append(1, getbitu(payload.c_str(), 116 + n*8, 8));
|
||||
// cerr<<makeHexDump(inav2) << endl;
|
||||
|
||||
|
||||
basic_string<uint8_t> reserved1;
|
||||
for(int n=0; n < 5 ; ++n)
|
||||
reserved1.append(1, getbitu(payload.c_str(), 116 + 16 + n*8, 8));
|
||||
|
||||
NavMonMessage nmm;
|
||||
double t = utcFromGST(si.wn - 1024, si.towMsec / 1000.0);
|
||||
// cerr<<t<< " " <<si.wn - 1024 <<" " <<si.towMsec /1000.0 <<" " << g_dtLS<<endl;
|
||||
nmm.set_sourceid(g_srcid);
|
||||
nmm.set_type(NavMonMessage::GalileoInavType);
|
||||
|
||||
nmm.set_localutcseconds(t);
|
||||
nmm.set_localutcnanoseconds(0); // yeah XXX
|
||||
|
||||
nmm.mutable_gi()->set_gnsswn(si.wn - 1024);
|
||||
|
||||
nmm.mutable_gi()->set_gnsstow(si.towMsec/1000.0);
|
||||
nmm.mutable_gi()->set_gnssid(2);
|
||||
nmm.mutable_gi()->set_gnsssv(si.sv - 70);
|
||||
nmm.mutable_gi()->set_contents((const char*)&inav2[0], inav2.size());
|
||||
nmm.mutable_gi()->set_sigid(sepsig2ubx(sigid));
|
||||
nmm.mutable_gi()->set_reserved1((const char*)&reserved1[0], reserved1.size());
|
||||
ns.emitNMM( nmm);
|
||||
|
||||
}
|
||||
else if(res.first.getID() == 5914) {
|
||||
// current time
|
||||
}
|
||||
|
||||
else if(res.first.getID() == 4026) {
|
||||
// GLONASS
|
||||
}
|
||||
else if(res.first.getID() == 4017) { // GPS-CA
|
||||
|
||||
}
|
||||
else if(res.first.getID() == 4018) { // GPS-L2C
|
||||
|
||||
}
|
||||
else if(res.first.getID() == 4019) { // GPS raw L5
|
||||
|
||||
}
|
||||
else if(res.first.getID() == 4093) { // navic raw
|
||||
|
||||
}
|
||||
else if(res.first.getID() == 4022) { // F/NAV
|
||||
auto str = res.first.getPayload();
|
||||
struct SEPFnav
|
||||
{
|
||||
uint32_t towMsec;
|
||||
uint16_t wn;
|
||||
uint8_t sv;
|
||||
uint8_t crcPassed;
|
||||
uint8_t viterbiCount;
|
||||
uint8_t src;
|
||||
uint8_t ign1;
|
||||
uint8_t rxChannel;
|
||||
uint8_t navBits[32];
|
||||
} __attribute__((packed));
|
||||
SEPFnav sf;
|
||||
memcpy(&sf, str.c_str(), sizeof(sf));
|
||||
int sigid = sf.src & 31;
|
||||
// cerr<<"tow "<<sf.towMsec /1000<<" wn "<<sf.wn <<" sv " << (int) sf.sv - 70<<" sigid " << sigid <<" ";
|
||||
if(!sf.crcPassed) {
|
||||
cerr<<"F/NAV CRC error, skipping"<<endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string fnav((char*)sf.navBits, 32);
|
||||
// byte order adjustment
|
||||
std::basic_string<uint8_t> payload;
|
||||
|
||||
for(unsigned int i = 0 ; i < 8; ++i) {
|
||||
payload.append(1, sf.navBits[4 * i + 3]);
|
||||
payload.append(1, sf.navBits[4 * i + 2]);
|
||||
payload.append(1, sf.navBits[4 * i + 1]);
|
||||
payload.append(1, sf.navBits[4 * i + 0]);
|
||||
}
|
||||
|
||||
NavMonMessage nmm;
|
||||
double t = utcFromGST(sf.wn - 1024, sf.towMsec / 1000.0);
|
||||
// cerr<<t<< " " <<si.wn - 1024 <<" " <<si.towMsec /1000.0 <<" " << g_dtLS<<endl;
|
||||
nmm.set_sourceid(g_srcid);
|
||||
nmm.set_type(NavMonMessage::GalileoFnavType);
|
||||
|
||||
nmm.set_localutcseconds(t);
|
||||
nmm.set_localutcnanoseconds(0); // yeah XXX
|
||||
|
||||
nmm.mutable_gf()->set_gnsswn(sf.wn - 1024);
|
||||
|
||||
nmm.mutable_gf()->set_gnsstow(sf.towMsec/1000.0);
|
||||
nmm.mutable_gf()->set_gnssid(2);
|
||||
nmm.mutable_gf()->set_gnsssv(sf.sv - 70);
|
||||
nmm.mutable_gf()->set_contents((const char*)&payload[0], payload.size());
|
||||
nmm.mutable_gf()->set_sigid(sepsig2ubx(sigid));
|
||||
ns.emitNMM( nmm);
|
||||
}
|
||||
else if(res.first.getIDBare() == 4047) {
|
||||
// BDSRaw
|
||||
}
|
||||
else if(res.first.getIDBare() == 4006) {
|
||||
auto str = res.first.getPayload();
|
||||
struct PVTCartesian
|
||||
{
|
||||
uint32_t towMsec;
|
||||
uint16_t wn;
|
||||
uint8_t mode, error;
|
||||
double x, y, z;
|
||||
float undulation;
|
||||
float vx, vy, vz;
|
||||
} __attribute__((packed));
|
||||
PVTCartesian pc;
|
||||
memcpy(&pc, str.c_str(), sizeof(pc));
|
||||
|
||||
NavMonMessage nmm;
|
||||
nmm.set_type(NavMonMessage::ObserverPositionType);
|
||||
nmm.set_localutcseconds(utcFromGST(pc.wn - 1024, pc.towMsec / 1000.0));
|
||||
nmm.set_localutcnanoseconds(0);
|
||||
nmm.set_sourceid(g_srcid);
|
||||
|
||||
nmm.mutable_op()->set_x(pc.x);
|
||||
nmm.mutable_op()->set_y(pc.y);
|
||||
nmm.mutable_op()->set_z(pc.z);
|
||||
nmm.mutable_op()->set_acc(3.14);
|
||||
|
||||
ns.emitNMM( nmm);
|
||||
|
||||
{
|
||||
NavMonMessage nmm;
|
||||
nmm.set_sourceid(g_srcid);
|
||||
nmm.set_localutcseconds(utcFromGST(pc.wn - 1024, pc.towMsec / 1000.0));
|
||||
nmm.set_localutcnanoseconds(0);
|
||||
|
||||
nmm.set_type(NavMonMessage::ObserverDetailsType);
|
||||
nmm.mutable_od()->set_vendor("Septentrio");
|
||||
nmm.mutable_od()->set_hwversion("Mosaic");
|
||||
nmm.mutable_od()->set_swversion("");
|
||||
nmm.mutable_od()->set_serialno("3060601");
|
||||
nmm.mutable_od()->set_modules("");
|
||||
nmm.mutable_od()->set_clockoffsetns(0);
|
||||
nmm.mutable_od()->set_clockoffsetdriftns(0);
|
||||
nmm.mutable_od()->set_clockaccuracyns(0);
|
||||
nmm.mutable_od()->set_freqaccuracyps(0);
|
||||
|
||||
nmm.mutable_od()->set_owner("Septentrio");
|
||||
nmm.mutable_od()->set_remark("");
|
||||
nmm.mutable_od()->set_recvgithash(g_gitHash);
|
||||
nmm.mutable_od()->set_uptime(time(0) - starttime);
|
||||
ns.emitNMM( nmm);
|
||||
}
|
||||
|
||||
}
|
||||
else if(res.first.getIDBare() == 4024) { // GALRawCNAV
|
||||
struct SEPCnav
|
||||
{
|
||||
uint32_t towMsec;
|
||||
uint16_t wn;
|
||||
uint8_t sv;
|
||||
uint8_t crcPassed;
|
||||
uint8_t viterbiCount;
|
||||
uint8_t src;
|
||||
uint8_t ign1;
|
||||
uint8_t rxChannel;
|
||||
uint8_t navBits[64];
|
||||
} __attribute__((packed));
|
||||
|
||||
SEPCnav sc;
|
||||
|
||||
auto str = res.first.getPayload();
|
||||
memcpy(&sc, str.c_str(), sizeof(sc));
|
||||
int sigid = sc.src & 31;
|
||||
// cerr<<"C/NAV tow "<<sc.towMsec /1000<<" wn "<<sc.wn <<" sv " << (int) sc.sv - 70<<" sigid " << sigid <<" ";
|
||||
if(!sc.crcPassed) {
|
||||
cerr<<"C/NAV CRC error, skipping"<<endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string cnav((char*)sc.navBits, 64);
|
||||
// byte order adjustment
|
||||
std::basic_string<uint8_t> payload;
|
||||
|
||||
for(unsigned int i = 0 ; i < 16; ++i) {
|
||||
payload.append(1, sc.navBits[4 * i + 3]);
|
||||
payload.append(1, sc.navBits[4 * i + 2]);
|
||||
payload.append(1, sc.navBits[4 * i + 1]);
|
||||
payload.append(1, sc.navBits[4 * i + 0]);
|
||||
}
|
||||
|
||||
unsigned char crc_buff[58]={0};
|
||||
unsigned int i;
|
||||
for (i=0; i< 462;i++)
|
||||
setbitu(crc_buff, 2+i, 1,getbitu(payload.c_str(),i, 1));
|
||||
|
||||
int calccrc=rtk_crc24q(crc_buff,58);
|
||||
int realcrc= getbitu(payload.c_str(), 14+448, 24);
|
||||
if (calccrc != realcrc) {
|
||||
cerr << "CRC mismatch, " << calccrc << " != " << realcrc <<endl;
|
||||
}
|
||||
// else
|
||||
// cerr<<"Checksum Correct: "<<calccrc << " = " << realcrc<<endl;
|
||||
|
||||
|
||||
|
||||
NavMonMessage nmm;
|
||||
double t = utcFromGST(sc.wn - 1024, sc.towMsec / 1000.0);
|
||||
// cerr<<t<< " " <<si.wn - 1024 <<" " <<si.towMsec /1000.0 <<" " << g_dtLS<<endl;
|
||||
nmm.set_sourceid(g_srcid);
|
||||
nmm.set_type(NavMonMessage::GalileoCnavType);
|
||||
|
||||
nmm.set_localutcseconds(t);
|
||||
nmm.set_localutcnanoseconds(0); // yeah XXX
|
||||
|
||||
nmm.mutable_gc()->set_gnsswn(sc.wn - 1024);
|
||||
|
||||
nmm.mutable_gc()->set_gnsstow(sc.towMsec/1000.0);
|
||||
nmm.mutable_gc()->set_gnssid(2);
|
||||
nmm.mutable_gc()->set_gnsssv(sc.sv - 70);
|
||||
nmm.mutable_gc()->set_contents((const char*)&payload[0], payload.size());
|
||||
nmm.mutable_gc()->set_sigid(sepsig2ubx(sigid));
|
||||
ns.emitNMM( nmm);
|
||||
}
|
||||
else if(res.first.getIDBare() == 4027) {
|
||||
auto str = res.first.getPayload();
|
||||
struct MeasEpoch
|
||||
{
|
||||
uint32_t towMsec;
|
||||
uint16_t wn;
|
||||
uint8_t n1;
|
||||
uint8_t sb1len;
|
||||
uint8_t sb2len;
|
||||
uint8_t commonFlags;
|
||||
uint8_t clkJumps;
|
||||
uint8_t res1;
|
||||
} __attribute__((packed));
|
||||
MeasEpoch me;
|
||||
memcpy(&me, str.c_str(), sizeof(me));
|
||||
// cerr<<"Got "<<(int)me.n1<<" signal statuses, block1 "<<(int)me.sb1len<<", block2 "<<(int)me.sb2len<<endl;
|
||||
|
||||
struct Block1
|
||||
{
|
||||
uint8_t rxchannel, type, sv, misc; // misc contains 4 bits of codeMSB
|
||||
uint32_t codeLSB;
|
||||
int32_t doppler;
|
||||
uint16_t carrierLSB;
|
||||
int8_t carrierMSB;
|
||||
uint8_t cn0;
|
||||
uint16_t lockTime;
|
||||
uint8_t obsinfo;
|
||||
uint8_t n2;
|
||||
} __attribute__((packed));
|
||||
struct Block2
|
||||
{
|
||||
uint8_t type, locktime, cn0, offsetMSB;
|
||||
int8_t carrierMSB;
|
||||
uint8_t obsinfo;
|
||||
uint16_t codeoffsetLSB;
|
||||
uint16_t carrierLSB;
|
||||
uint16_t dopplerOffsetLSB;
|
||||
} __attribute__((packed));
|
||||
|
||||
int pos = sizeof(me);
|
||||
for(int n = 0 ; n < me.n1; ++n) {
|
||||
Block1 b1;
|
||||
memcpy(&b1, str.c_str() + pos, sizeof(b1));
|
||||
uint8_t sigid = b1.type & 31;
|
||||
// cerr<<"sv "<<(int)b1.sv<<" sigid "<< (int)sigid <<" cn0 ";
|
||||
double db;
|
||||
if(sigid==1 || sigid ==2)
|
||||
db = b1.cn0 *0.25;
|
||||
else db = b1.cn0 * 0.25 + 10;
|
||||
// cerr<<" "<<db;
|
||||
// cerr<<" n2 "<< (int)b1.n2;
|
||||
// cerr<<endl;
|
||||
pos += me.sb1len;
|
||||
|
||||
if(b1.sv <= 36 || (b1.sv > 70 && b1.sv <= 106)) {
|
||||
NavMonMessage nmm;
|
||||
nmm.set_sourceid(g_srcid);
|
||||
nmm.set_localutcseconds(utcFromGST(me.wn - 1024, me.towMsec / 1000.0));
|
||||
nmm.set_localutcnanoseconds(0);
|
||||
|
||||
nmm.set_type(NavMonMessage::ReceptionDataType);
|
||||
nmm.mutable_rd()->set_gnssid(b1.sv > 70 ? 2 : 0 );
|
||||
nmm.mutable_rd()->set_gnsssv(b1.sv <= 37 ? b1.sv : b1.sv - 70);
|
||||
nmm.mutable_rd()->set_db(db);
|
||||
nmm.mutable_rd()->set_el(0);
|
||||
nmm.mutable_rd()->set_azi(0);
|
||||
nmm.mutable_rd()->set_prres(-1);
|
||||
nmm.mutable_rd()->set_qi(7);
|
||||
|
||||
try {
|
||||
nmm.mutable_rd()->set_sigid(sepsig2ubx(sigid));
|
||||
ns.emitNMM(nmm);
|
||||
}
|
||||
catch(...){}
|
||||
|
||||
/*
|
||||
LSB of the pseudorange. The pseudorange expressed in meters
|
||||
is computed as follows:
|
||||
PR type1 [m] = ( CodeMSB *4294967296+ CodeLSB )*0.001
|
||||
|
||||
codeMSB hides in bits 0-3 of misc.
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
for(int m = 0 ; m < b1.n2; ++m) {
|
||||
Block2 b2;
|
||||
memcpy(&b2, str.c_str() + pos, sizeof(b2));
|
||||
pos += me.sb2len;
|
||||
sigid = b2.type & 31;
|
||||
// cerr<<"\t sigid "<<(int)sigid<<" cn0 ";
|
||||
if(sigid==1 || sigid ==2)
|
||||
db= b2.cn0 *0.25;
|
||||
else db = b2.cn0 * 0.25 + 10;
|
||||
// cerr<<db;
|
||||
// cerr<<endl;
|
||||
|
||||
if(b1.sv <= 36 || (b1.sv > 70 && b1.sv <= 106)) {
|
||||
NavMonMessage nmm;
|
||||
nmm.set_sourceid(g_srcid);
|
||||
nmm.set_localutcseconds(utcFromGST(me.wn - 1024, me.towMsec / 1000.0));
|
||||
nmm.set_localutcnanoseconds(0);
|
||||
|
||||
nmm.set_type(NavMonMessage::ReceptionDataType);
|
||||
nmm.mutable_rd()->set_gnssid(b1.sv > 70 ? 2 : 0 );
|
||||
nmm.mutable_rd()->set_gnsssv(b1.sv <= 37 ? b1.sv : b1.sv - 70);
|
||||
nmm.mutable_rd()->set_db(db);
|
||||
nmm.mutable_rd()->set_el(0);
|
||||
nmm.mutable_rd()->set_azi(0);
|
||||
nmm.mutable_rd()->set_prres(0);
|
||||
nmm.mutable_rd()->set_qi(7);
|
||||
try {
|
||||
nmm.mutable_rd()->set_sigid(sepsig2ubx(sigid));
|
||||
ns.emitNMM(nmm);
|
||||
}catch(...){} // might be unknown signal type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
if(!quiet)
|
||||
cerr<<"Unknown message "<<res.first.getID() << " / " <<res.first.getIDBare()<<" ("<<res.first.d_store.size()<<" bytes)"<<endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
cerr<<"Fatal: "<<e.what()<<endl;
|
||||
}
|
||||
catch(EofException& e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
NAVBits contains the 234 bits of an I/NAV navigation page (in nominal
|
||||
or alert mode). Note that the I/NAV page is transmitted as two sub-pages
|
||||
(the so-called even and odd pages) of duration 1 second each (120 bits
|
||||
each).
|
||||
|
||||
In this block, the even and odd pages are concatenated, even page
|
||||
first and odd page last. The 6 tails bits at the end of the even page are
|
||||
removed (hence a total of 234 bits). If the even and odd pages have been
|
||||
received from two different carriers (E5b and L1), bit 5 of the Source
|
||||
field is set.
|
||||
*/
|
||||
|
29
ubx.cc
29
ubx.cc
|
@ -3,6 +3,7 @@
|
|||
#include "fmt/format.h"
|
||||
#include "fmt/printf.h"
|
||||
#include "bits.hh"
|
||||
#include "navmon.hh"
|
||||
|
||||
using namespace std;
|
||||
uint16_t calcUbxChecksum(uint8_t ubxClass, uint8_t ubxType, std::basic_string_view<uint8_t> str)
|
||||
|
@ -60,7 +61,12 @@ std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, st
|
|||
return msg;
|
||||
}
|
||||
|
||||
basic_string<uint8_t> getInavFromSFRBXMsg(std::basic_string_view<uint8_t> msg)
|
||||
basic_string<uint8_t> getInavFromSFRBXMsg(std::basic_string_view<uint8_t> msg,
|
||||
basic_string<uint8_t>& reserved1,
|
||||
basic_string<uint8_t>& reserved2,
|
||||
basic_string<uint8_t>& sar,
|
||||
basic_string<uint8_t>& spare,
|
||||
basic_string<uint8_t>& crc)
|
||||
{
|
||||
// byte order adjustment
|
||||
std::basic_string<uint8_t> payload;
|
||||
|
@ -74,10 +80,14 @@ basic_string<uint8_t> getInavFromSFRBXMsg(std::basic_string_view<uint8_t> msg)
|
|||
for (i=0,j= 4;i<15;i++,j+=8) setbitu(crc_buff,j,8,getbitu(payload.c_str() ,i*8,8));
|
||||
for (i=0,j=118;i<11;i++,j+=8) setbitu(crc_buff,j,8,getbitu(payload.c_str()+16,i*8,8));
|
||||
if (rtk_crc24q(crc_buff,25) != getbitu(payload.c_str()+16,82,24)) {
|
||||
cout << "CRC mismatch, " << rtk_crc24q(crc_buff, 25) << " != " << getbitu(payload.c_str()+16,82,24) <<endl;
|
||||
cerr << "CRC mismatch, " << rtk_crc24q(crc_buff, 25) << " != " << getbitu(payload.c_str()+16,82,24) <<endl;
|
||||
throw CRCMismatch();
|
||||
}
|
||||
|
||||
crc.clear();
|
||||
for(i=0; i < 3; ++i)
|
||||
crc.append(1, getbitu(payload.c_str()+16,82+i*8,8));
|
||||
|
||||
std::basic_string<uint8_t> inav;
|
||||
|
||||
for (i=0,j=2; i<14; i++, j+=8)
|
||||
|
@ -85,6 +95,21 @@ basic_string<uint8_t> getInavFromSFRBXMsg(std::basic_string_view<uint8_t> msg)
|
|||
for (i=0,j=2; i< 2; i++, j+=8)
|
||||
inav.append(1, (unsigned char)getbitu(payload.c_str()+16,j,8));
|
||||
|
||||
reserved1.clear();
|
||||
for(i=0, j=18; i < 5 ; i++, j+=8)
|
||||
reserved1.append(1, (unsigned char)getbitu(payload.c_str()+16, j, 8));
|
||||
// cerr<<"reserved1: "<<makeHexDump(reserved1)<<endl;
|
||||
|
||||
sar.clear();
|
||||
for(i=0, j=58; i < 3 ; i++, j+=8) // you get 24 bits
|
||||
sar.append(1, (unsigned char)getbitu(payload.c_str()+16, j, 8));
|
||||
|
||||
spare.clear();
|
||||
spare.append(1, (unsigned char)getbitu(payload.c_str()+16, 80, 2));
|
||||
|
||||
reserved2.clear();
|
||||
reserved2.append(1, (unsigned char)getbitu(payload.c_str()+16, 106, 8));
|
||||
|
||||
return inav;
|
||||
}
|
||||
|
||||
|
|
8
ubx.hh
8
ubx.hh
|
@ -6,7 +6,13 @@ std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, st
|
|||
|
||||
std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, const std::initializer_list<uint8_t>& str);
|
||||
|
||||
std::basic_string<uint8_t> getInavFromSFRBXMsg(std::basic_string_view<uint8_t> msg);
|
||||
std::basic_string<uint8_t> getInavFromSFRBXMsg(std::basic_string_view<uint8_t> msg,
|
||||
std::basic_string<uint8_t>& reserved1,
|
||||
std::basic_string<uint8_t>& reserved2,
|
||||
std::basic_string<uint8_t>& sar,
|
||||
std::basic_string<uint8_t>& spare,
|
||||
std::basic_string<uint8_t>& crc);
|
||||
|
||||
std::basic_string<uint8_t> getGPSFromSFRBXMsg(std::basic_string_view<uint8_t> msg);
|
||||
std::basic_string<uint8_t> getGlonassFromSFRBXMsg(std::basic_string_view<uint8_t> msg);
|
||||
std::basic_string<uint8_t> getBeidouFromSFRBXMsg(std::basic_string_view<uint8_t> msg);
|
||||
|
|
14
ubxtool.cc
14
ubxtool.cc
|
@ -496,7 +496,7 @@ struct TIMEGPS
|
|||
// ubxtool device srcid
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
time_t starttime=time(0);
|
||||
auto starttime = std::chrono::steady_clock::now();
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
|
||||
CLI::App app(program);
|
||||
|
@ -927,7 +927,7 @@ int main(int argc, char** argv)
|
|||
enableUBXMessageOnPort(fd, 0x02, 0x59, ubxport); // UBX-RXM-RLM
|
||||
}
|
||||
|
||||
if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-MON-HW"<<endl; } // SAR
|
||||
if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-MON-HW"<<endl; }
|
||||
enableUBXMessageOnPort(fd, 0x0A, 0x09, ubxport, 16); // UBX-MON-HW
|
||||
|
||||
|
||||
|
@ -1385,7 +1385,8 @@ int main(int argc, char** argv)
|
|||
ns.emitNMM( nmm);
|
||||
}
|
||||
else if(id.first ==2) { // GALILEO
|
||||
auto inav = getInavFromSFRBXMsg(payload);
|
||||
basic_string<uint8_t> reserved1, reserved2, sar, spare, crc;
|
||||
auto inav = getInavFromSFRBXMsg(payload, reserved1, reserved2, sar, spare, crc);
|
||||
unsigned int wtype = getbitu(&inav[0], 0, 6);
|
||||
|
||||
uint32_t satTOW;
|
||||
|
@ -1466,6 +1467,11 @@ int main(int argc, char** argv)
|
|||
nmm.mutable_gi()->set_gnsssv(id.second);
|
||||
nmm.mutable_gi()->set_sigid(sigid);
|
||||
nmm.mutable_gi()->set_contents((const char*)&inav[0], inav.size());
|
||||
nmm.mutable_gi()->set_reserved1((const char*)&reserved1[0], reserved1.size());
|
||||
nmm.mutable_gi()->set_reserved2((const char*)&reserved2[0], reserved2.size());
|
||||
nmm.mutable_gi()->set_sar((const char*) &sar[0], sar.size());
|
||||
nmm.mutable_gi()->set_crc((const char*) &crc[0], crc.size());
|
||||
nmm.mutable_gi()->set_spare((const char*)&spare[0], spare.size());
|
||||
|
||||
ns.emitNMM( nmm);
|
||||
}
|
||||
|
@ -1697,7 +1703,7 @@ int main(int argc, char** argv)
|
|||
nmm.mutable_od()->set_owner(owner);
|
||||
nmm.mutable_od()->set_remark(remark);
|
||||
nmm.mutable_od()->set_recvgithash(g_gitHash);
|
||||
nmm.mutable_od()->set_uptime(time(0) - starttime);
|
||||
nmm.mutable_od()->set_uptime(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now()-starttime).count());
|
||||
|
||||
|
||||
ns.emitNMM( nmm);
|
||||
|
|
12
zstdwrap.cc
12
zstdwrap.cc
|
@ -149,17 +149,21 @@ void ZStdReader::worker()
|
|||
|
||||
for(;;) {
|
||||
input.pos=0;
|
||||
input.size=read(d_sourcefd, (char*)input.src, inputcapacity);
|
||||
if(input.size <= 0) {
|
||||
int ret = read(d_sourcefd, (char*)input.src, inputcapacity);
|
||||
if(ret <= 0) {
|
||||
cerr<<"Got EOF on input fd "<<d_sourcefd<<", terminating thread"<<endl;
|
||||
break;
|
||||
}
|
||||
input.size = ret; // this is unsigned, so we need 'ret' to see the error
|
||||
while(input.pos != input.size) {
|
||||
output.pos=0;
|
||||
output.size=outputcapacity;
|
||||
ZSTD_decompressStream(z, &output, &input);
|
||||
int res = ZSTD_decompressStream(z, &output, &input);
|
||||
if(ZSTD_isError(res)) {
|
||||
cerr<<"Error decompressing ZSTD data"<<endl;
|
||||
break;
|
||||
}
|
||||
|
||||
int res;
|
||||
res = writen(d_writepipe, output.dst, output.pos);
|
||||
if(!res) // we are history
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue