Compare commits

..

No commits in common. "master" and "eph-store" have entirely different histories.

85 changed files with 13146 additions and 6021 deletions

View file

@ -4,15 +4,15 @@ on: [push]
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v1
with:
submodules: "recursive"
- name: deps - name: deps
run: | 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
sudo apt-get update - name: submodules
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 libfec-dev libfmt-dev run: git submodule update --init --recursive
- name: config - name: config
run: echo WSLAY=-lwslay > Makefile.local run: echo WSLAY=-lwslay > Makefile.local
- name: make - name: make

View file

@ -24,7 +24,7 @@ jobs:
- name: Run buildx - name: Run buildx
run: | run: |
docker buildx build \ docker buildx build \
--tag berthubert/galmon \ --tag galmon/galmon \
--platform linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 \ --platform linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 \
--output "type=registry" \ --output "type=registry" \
--build-arg MAKE_FLAGS=-j1 \ --build-arg MAKE_FLAGS=-j1 \

View file

@ -1,38 +1,25 @@
# FROM ubuntu:eoan
# First stage - builder
#
FROM debian:10-slim AS builder
ENV DEBIAN_FRONTEND noninteractive ENV DEBIAN_FRONTEND noninteractive
ENV LC_ALL C.UTF-8 ENV LC_ALL C.UTF-8
# This allows you to use a local Debian mirror # This allows you to use a local Ubuntu mirror
ARG APT_URL=http://deb.debian.org/debian/ ARG APT_URL=
ARG MAKE_FLAGS=-j2 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
RUN sed -i "s%http://deb.debian.org/debian/%${APT_URL}%" /etc/apt/sources.list \
&& apt-get update && apt-get -y upgrade \ # Update packages and install dependencies
&& apt-get install -y protobuf-compiler libh2o-dev libcurl4-openssl-dev \ RUN apt-get update && apt-get -y upgrade && apt-get -y clean
libssl-dev libprotobuf-dev libh2o-evloop-dev libwslay-dev \ RUN apt-get install -y protobuf-compiler libh2o-dev libcurl4-openssl-dev \
libeigen3-dev libzstd-dev libfmt-dev libncurses-dev \ libssl-dev libprotobuf-dev libh2o-evloop-dev libwslay-dev libeigen3-dev libzstd-dev \
make gcc g++ git build-essential curl autoconf automake help2man make gcc g++ git build-essential curl autoconf automake libfmt-dev libncurses5-dev \
&& apt-get -y clean
# Build # Build
ADD . /galmon-src/ ARG MAKE_FLAGS=-j2
RUN cd /galmon-src/ \ ADD . /galmon/
&& make $MAKE_FLAGS \ WORKDIR /galmon/
&& prefix=/galmon make install RUN make $MAKE_FLAGS
ENV PATH=/galmon:${PATH}
#
# 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

18
Dockerfile-pi Normal file
View file

@ -0,0 +1,18 @@
#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}

View file

@ -1,7 +1,7 @@
CFLAGS = -O3 -Wall -ggdb CFLAGS = -O3 -Wall -ggdb
CXXFLAGS:= -std=gnu++17 -Wall -O3 -ggdb -MMD -MP -fno-omit-frame-pointer -Iext/CLI11 \ CXXFLAGS:= -std=gnu++17 -Wall -O3 -ggdb -MMD -MP -fno-omit-frame-pointer -Iext/CLI11 \
-Iext/powerblog/ext/simplesocket -Iext/powerblog/ext/ \ -Iext/fmt-6.1.2/include/ -Iext/powerblog/ext/simplesocket -Iext/powerblog/ext/ \
-I/usr/local/opt/openssl/include/ \ -I/usr/local/opt/openssl/include/ \
-Iext/sgp4/libsgp4/ \ -Iext/sgp4/libsgp4/ \
-I/usr/local/include -I/usr/local/include
@ -21,10 +21,11 @@ else ifneq (,$(wildcard ubxsec.o))
EXTRADEP = ubxsec.o EXTRADEP = ubxsec.o
endif endif
CHEAT_ARG := $(shell ./update-git-hash-if-necessary) CHEAT_ARG := $(shell ./update-git-hash-if-necessary)
PROGRAMS = navparse ubxtool navnexus navcat navrecv navdump testrunner navdisplay tlecatch reporter sp3feed \ PROGRAMS = navparse ubxtool navnexus navcat navrecv navdump testrunner navdisplay tlecatch reporter sp3feed \
galmonmon rinreport rinjoin rtcmtool gndate septool navmerge galmonmon rinreport rinjoin rtcmtool gndate
all: navmon.pb.cc $(PROGRAMS) all: navmon.pb.cc $(PROGRAMS)
@ -42,10 +43,9 @@ navmon.pb.cc: navmon.proto
H2OPP=ext/powerblog/h2o-pp.o H2OPP=ext/powerblog/h2o-pp.o
SIMPLESOCKETS=ext/powerblog/ext/simplesocket/swrappers.o ext/powerblog/ext/simplesocket/sclasses.o ext/powerblog/ext/simplesocket/comboaddress.o SIMPLESOCKETS=ext/powerblog/ext/simplesocket/swrappers.o ext/powerblog/ext/simplesocket/sclasses.o ext/powerblog/ext/simplesocket/comboaddress.o
clean: clean:
rm -f *~ *.o *.d ext/*/*.o ext/*/*.d $(PROGRAMS) navmon.pb.h navmon.pb.cc $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) $(H2OPP) $(SIMPLESOCKETS) rm -f *~ *.o *.d ext/*/*.o ext/*/*.d $(PROGRAMS) navmon.pb.h navmon.pb.cc $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) $(H2OPP) $(SIMPLESOCKETS)
rm -f ext/sgp4/libsgp4/*.d ext/powerblog/ext/simplesocket/*.d rm -f ext/fmt-6.1.2/src/format.[do] ext/sgp4/libsgp4/*.d ext/powerblog/ext/simplesocket/*.d
help2man: help2man:
$(INSTALL) -m 755 -d $(DESTDIR)$(prefix)/share/man/man1 $(INSTALL) -m 755 -d $(DESTDIR)$(prefix)/share/man/man1
@ -73,76 +73,66 @@ download-raspbian-package:
echo "deb https://ota.bike/raspbian/ buster main" > /etc/apt/sources.list.d/galmon.list echo "deb https://ota.bike/raspbian/ buster main" > /etc/apt/sources.list.d/galmon.list
apt-get update && apt-get install -y galmon apt-get update && apt-get install -y galmon
decrypt: decrypt.o bits.o decrypt: decrypt.o bits.o ext/fmt-6.1.2/src/format.o
$(CXX) -std=gnu++17 $^ -o $@ -lfmt $(CXX) -std=gnu++17 $^ -o $@
navparse: navparse.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 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
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -L/usr/local/opt/openssl/lib/ -lh2o-evloop -lssl -lcrypto -lz -lcurl -lprotobuf $(WSLAY) -lfmt $(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 $(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 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
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl -lfmt $(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl
sp3feed: sp3feed.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 influxpush.o githash.o sp3.o sp3feed: sp3feed.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 influxpush.o githash.o sp3.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl -lfmt $(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl
tracker: tracker.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 tracker: tracker.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
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl -lfmt $(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl
galmonmon: galmonmon.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 galmonmon: galmonmon.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
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl -lfmt $(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl
# rs.o fixhunter.o 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 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
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf -lz -lfmt
# -lfec
navdisplay: navdisplay.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o ephemeris.o navmon.o osen.o githash.o 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 -lfmt $(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf -lncurses
navnexus: navnexus.o $(SIMPLESOCKETS) bits.o navmon.pb.o storage.o githash.o navnexus: navnexus.o ext/fmt-6.1.2/src/format.o $(SIMPLESOCKETS) ubx.o bits.o navmon.pb.o storage.o githash.o
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf -lfmt $(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf
navcat: navcat.o $(SIMPLESOCKETS) ubx.o bits.o navmon.pb.o storage.o navmon.o githash.o 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
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf -lfmt $(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf
navrecv: navrecv.o $(SIMPLESOCKETS) navmon.pb.o storage.o githash.o zstdwrap.o navmon.o 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 -lfmt $(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf -lzstd
navmerge: navmerge.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 -lfmt
tlecatch: tlecatch.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) githash.o tlecatch: tlecatch.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) githash.o
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf $(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf
rinreport: rinreport.o rinex.o githash.o navmon.o ephemeris.o osen.o rinreport: rinreport.o rinex.o githash.o navmon.o ext/fmt-6.1.2/src/format.o ephemeris.o osen.o
$(CXX) -std=gnu++17 $^ -o $@ -lz -pthread -lfmt $(CXX) -std=gnu++17 $^ -o $@ -lz -pthread
rinjoin: rinjoin.o rinex.o githash.o navmon.o ephemeris.o osen.o rinjoin: rinjoin.o rinex.o githash.o navmon.o ext/fmt-6.1.2/src/format.o ephemeris.o osen.o
$(CXX) -std=gnu++17 $^ -o $@ -lz -pthread -lfmt $(CXX) -std=gnu++17 $^ -o $@ -lz -pthread
rtcmtool: rtcmtool.o navmon.pb.o githash.o bits.o nmmsender.o $(SIMPLESOCKETS) navmon.o rtcm.o zstdwrap.o rtcmtool: rtcmtool.o navmon.pb.o githash.o ext/fmt-6.1.2/src/format.o bits.o nmmsender.o $(SIMPLESOCKETS) navmon.o rtcm.o zstdwrap.o
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lz -pthread -lprotobuf -lzstd -lfmt $(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lz -pthread -lprotobuf -lzstd
ubxtool: navmon.pb.o ubxtool.o ubx.o bits.o galileo.o gps.o beidou.o navmon.o ephemeris.o $(SIMPLESOCKETS) osen.o githash.o nmmsender.o zstdwrap.o 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 -lfmt $(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lprotobuf -pthread -lzstd
septool: navmon.pb.o septool.o bits.o galileo.o gps.o beidou.o navmon.o ephemeris.o $(SIMPLESOCKETS) osen.o githash.o nmmsender.o zstdwrap.o 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 -pthread -lzstd -lfmt $(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lprotobuf -lz
gndate: gndate.o githash.o ext/fmt-6.1.2/src/format.o navmon.o
testrunner: navmon.pb.o testrunner.o ubx.o bits.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
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lprotobuf -lz -pthread -lfmt
gndate: gndate.o githash.o navmon.o
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lfmt
check: testrunner check: testrunner
./testrunner ./testrunner

136
README.md
View file

@ -4,17 +4,14 @@ galileo/GPS/GLONASS/BeiDou open source monitoring. GPL3 licensed.
Live website: https://galmon.eu/ Live website: https://galmon.eu/
Multi-vendor, with support for U-blox 8 and 9 chipsets and many Septentrio Theoretically multi-vendor, although currently only the U-blox 8 and 9
devices. Navilock NL-8012U receiver works really well, as does the U-blox chipsets are supported. Navilock NL-8012U receiver works really well, as
evaluation kit for the 8MT. In addition, many stations have reported does the U-blox evaluation kit for the 8MT. In addition, many stations have
success with this very cheap [AliExpress sourced reported success with this very cheap [AliExpress sourced
device](https://www.aliexpress.com/item/32816656706.html). 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
For ublox, there is good support for the F9P, several of us use the F9P, several of us use the
[ArdusimpleRTK2B](https://www.ardusimple.com/simplertk2b/) board. It adds [ArdusimpleRTK2B](https://www.ardusimple.com/simplertk2b/) board.
the Galileo E5b band.
Septentrio devices support even more bands.
An annotated presentation about our project aimed at GNSS professionals can An annotated presentation about our project aimed at GNSS professionals can
be found [here](https://berthub.eu/galileo/The%20galmon.eu%20project.pdf). be found [here](https://berthub.eu/galileo/The%20galmon.eu%20project.pdf).
@ -27,15 +24,14 @@ be found [here](https://berthub.eu/galileo/The%20galmon.eu%20project.pdf).
To deliver data to the project, please read To deliver data to the project, please read
[The Galmon GNSS Monitoring Project](https://berthub.eu/articles/posts/galmon-project/) [The Galmon GNSS Monitoring Project](https://berthub.eu/articles/posts/galmon-project/)
and consult the rules outlined in [the operator and consult the rules outlined in [the operator
guidelines](https://github.com/berthubert/galmon/blob/master/Operator.md). guidelines](https://github.com/ahupowerdns/galmon/blob/master/Operator.md).
Highlights Highlights
---------- ----------
* Support for Septentrio and U-blox.
* Processes raw frames/strings/words from GPS, GLONASS, BeiDou and Galileo * Processes raw frames/strings/words from GPS, GLONASS, BeiDou and Galileo
* All-band support (E1, E5a, E5b, B1I, B2I, Glonass L1, Glonass L2, GPS L1C/A) * All-band support (E1, E5b, B1I, B2I, Glonass L1, Glonass L2, GPS L1C/A)
so far. so far, GPS L2C and Galileo E5a pending).
* Calculate ephemeris positions * Calculate ephemeris positions
* Comparison of ephemerides to independent SP3 data to determine SISE * Comparison of ephemerides to independent SP3 data to determine SISE
* Globally, locally, worst user location * Globally, locally, worst user location
@ -53,7 +49,7 @@ Highlights
Data is made available as JSON, as a user-friendly website and as a Data is made available as JSON, as a user-friendly website and as a
time-series database. This time-series database is easily mated to the time-series database. This time-series database is easily mated to the
industry standard Matplotlib/Pandas/Jupyter combination (details industry standard Matplotlib/Pandas/Jupyter combination (details
[here](https://github.com/berthubert/galmon/blob/master/influxdb.md). [here](https://github.com/ahupowerdns/galmon/blob/master/influxdb.md).
There is also tooling to extract raw frames/strings/words from specific There is also tooling to extract raw frames/strings/words from specific
timeframes. timeframes.
@ -72,8 +68,8 @@ Goals:
Works on Linux (including Raspbian Buster on Pi Zero W), OSX and OpenBSD. Works on Linux (including Raspbian Buster on Pi Zero W), OSX and OpenBSD.
Build locally (Linux, Debian, Ubuntu) Build locally
------------------------------------- -------------
To get started, make sure you have a C++17 compiler (like g++ 8 or higher), 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 git, protobuf-compiler. Then run 'make ubxtool navdump' to build the
@ -83,8 +79,8 @@ To build everything, including the webserver, try:
``` ```
apt-get install protobuf-compiler libh2o-dev libcurl4-openssl-dev libssl-dev libprotobuf-dev \ 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 g++ libfmt-dev libh2o-evloop-dev libwslay-dev libncurses5-dev libeigen3-dev libzstd-dev
git clone https://github.com/berthubert/galmon.git --recursive git clone https://github.com/ahupowerdns/galmon.git --recursive
cd galmon cd galmon
make make
``` ```
@ -96,41 +92,25 @@ library installed. If you get an error about 'wslay', do the following, and run
echo WSLAY=-lwslay > Makefile.local 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/berthubert/galmon.git --recursive
cd galmon
make
```
Running in Docker Running in Docker
----------------- -----------------
We publish official Docker images for galmon on We publish official Docker images for galmon on
[docker hub](https://hub.docker.com/r/berthubert/galmon) for multiple architectures. [docker hub](https://hub.docker.com/r/faucet/faucet) for multiple architectures.
To run a container with a shell in there (this will also expose a port so you 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 - can view the UI too and assumes a ublox GPS device too -
you may need to tweak as necessary): you may need to tweak as necessary):
``` ```
docker run -it --rm --device=/dev/ttyACM0 -p 10000:10000 berthubert/galmon docker run -it --rm --device=/dev/ttyACM0 -p 10000:10000 galmon/galmon
``` ```
Running a daemonized docker container reporting data to a remote server Running a daemonized docker container reporting data to a remote server
might look like: might look like:
``` ```
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] 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]
``` ```
To make your docker container update automatically you could use a tool such as To make your docker container update automatically you could use a tool such as
@ -138,15 +118,10 @@ To make your docker container update automatically you could use a tool such as
Running Running
------- -------
On u-blox:
Once compiled, run for example `./ubxtool --wait --port /dev/ttyACM0 Once compiled, run for example `./ubxtool --wait --port /dev/ttyACM0
--station 1 --stdout --galileo | ./navparse --bind [::1]:10000` --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 Next up, browse to http://[::1]:10000 (or try http://localhost:10000/ and
you should be in business. ubxtool changes (non-permanently) the you should be in business. ubxtool changes (non-permanently) the
configuration of your u-blox receiver so it emits the required frames for configuration of your u-blox receiver so it emits the required frames for
@ -181,13 +156,9 @@ to stdout, add `--stdout`.
Tooling: Tooling:
* ubxtool: can configure a u-blox 8/9/10 chipset, parses its output & will * ubxtool: can configure a u-blox 8 chipset, parses its output & will
convert it into a protbuf stream of GNSS NAV frames + metadata convert it into a protbuf stream of GNSS NAV frames + metadata
Adds 64-bit timestamps plus origin information to each message 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 * xtool: if you have another chipset, build something that extracts NAV
frames & metadata. Not done yet. frames & metadata. Not done yet.
* navrecv: receives GNSS NAV frames and stores them on disk, split out per * navrecv: receives GNSS NAV frames and stores them on disk, split out per
@ -200,11 +171,6 @@ Tooling:
computations on ephemerides. computations on ephemerides.
* grafana dashboard: makes pretty graphs * grafana dashboard: makes pretty graphs
Per device notes
----------------
The "SparkFun GNSS L1/L5 Breakout - NEO-F10N, SMA" needs '-u1 -b 38400
--wait'.
Linux Systemd Linux Systemd
------------- -------------
First make sure 'ubxtool' has been compiled (run: make ubxtool). Then, as First make sure 'ubxtool' has been compiled (run: make ubxtool). Then, as
@ -263,47 +229,6 @@ 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. if you have an influxdb running on localhost with a galileo database in there.
The default URL is http://127.0.0.1:29599/ 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 Internals
--------- ---------
The transport format consists of repeats of: The transport format consists of repeats of:
@ -328,10 +253,9 @@ Documents
* [GLONASS CDMA](http://russianspacesystems.ru/wp-content/uploads/2016/08/ICD-GLONASS-CDMA-General.-Edition-1.0-2016.pdf) * [GLONASS CDMA](http://russianspacesystems.ru/wp-content/uploads/2016/08/ICD-GLONASS-CDMA-General.-Edition-1.0-2016.pdf)
not actually relevant for the CDMA aspects, but has appendices on more not actually relevant for the CDMA aspects, but has appendices on more
precise orbit determinations. precise orbit determinations.
* [GPS](https://www.navcen.uscg.gov/sites/default/files/pdf/gps/IS-GPS-200N.pdf) * [GPS](https://www.gps.gov/technical/icwg/IS-GPS-200K.pdf)
* [U-blox 8 interface specification](https://www.u-blox.com/sites/default/files/products/documents/u-blox8-M8_ReceiverDescrProtSpec_%28UBX-13003221%29_Public.pdf) * [U-blox 8 interface specification](https://www.u-blox.com/sites/default/files/products/documents/u-blox8-M8_ReceiverDescrProtSpec_%28UBX-13003221%29_Public.pdf)
* [U-blox 9 interface specification](https://www.u-blox.com/sites/default/files/u-blox_ZED-F9P_InterfaceDescription_%28UBX-18010854%29.pdf) * [U-blox 9 interface specification](https://www.u-blox.com/sites/default/files/u-blox_ZED-F9P_InterfaceDescription_%28UBX-18010854%29.pdf)
* [U-blox 10 interface specification](https://content.u-blox.com/sites/default/files/documents/u-blox-F10-SPG-6.00_InterfaceDescription_UBX-23002975.pdf)
* [U-blox 9 integration manual](https://www.u-blox.com/sites/default/files/ZED-F9P_IntegrationManual_%28UBX-18010802%29.pdf) * [U-blox 9 integration manual](https://www.u-blox.com/sites/default/files/ZED-F9P_IntegrationManual_%28UBX-18010802%29.pdf)
Data sources Data sources
@ -351,26 +275,16 @@ The software can interpret SP3 files, good sources:
to have less of a delay than the ESA ESM series. to have less of a delay than the ESA ESM series.
* GBU = ultra rapid, still a few days delay, but much more recent. * 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: To get SP3 GBM from GFZ Potsdam for GPS week number 2111:
``` ```
WN=2111 WN=2111
lftp -c "mget ftp://ftp.gfz-potsdam.de/GNSS/products/mgnss/${WN}/gbm*sp3.Z" 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 RTCM
---- ----

View file

@ -1,8 +1,6 @@
#include "beidou.hh" #include "beidou.hh"
#include "bits.hh" #include "bits.hh"
#include <iostream> #include <iostream>
#include <vector>
#include "navmon.hh"
using namespace std; using namespace std;
@ -31,7 +29,7 @@ static int checkbds(int bits)
return 1; return 1;
} }
std::vector<uint8_t> getCondensedBeidouMessage(const std::vector<uint8_t>& payload) std::basic_string<uint8_t> getCondensedBeidouMessage(std::basic_string_view<uint8_t> payload)
{ {
// payload consists of 32 bit words where we have to ignore the first 2 bits of every word // payload consists of 32 bit words where we have to ignore the first 2 bits of every word
@ -58,7 +56,7 @@ std::vector<uint8_t> getCondensedBeidouMessage(const std::vector<uint8_t>& paylo
setbitu(buffer, 26+22*(w-1), 22, getbitu(&payload[0], 2 + w*32, 22)); setbitu(buffer, 26+22*(w-1), 22, getbitu(&payload[0], 2 + w*32, 22));
} }
return makeVec(buffer, 28); return std::basic_string<uint8_t>(buffer, 28);
} }

View file

@ -4,10 +4,9 @@
#include "bits.hh" #include "bits.hh"
#include <math.h> #include <math.h>
#include <stdexcept> #include <stdexcept>
#include <vector>
#include "ephemeris.hh" #include "ephemeris.hh"
std::vector<uint8_t> getCondensedBeidouMessage(const std::vector<uint8_t>& payload); std::basic_string<uint8_t> getCondensedBeidouMessage(std::basic_string_view<uint8_t> payload);
int beidouBitconv(int their); int beidouBitconv(int their);
/* Geostationary, so D2, so not to be parsed by this parser: /* Geostationary, so D2, so not to be parsed by this parser:
@ -22,7 +21,7 @@ struct BeidouMessage : GPSLikeEphemeris
{ {
uint8_t strtype; uint8_t strtype;
std::vector<uint8_t> g_cond; std::basic_string_view<uint8_t> g_cond;
int bbitu(int bit, int len) int bbitu(int bit, int len)
{ {
return getbitu(&g_cond[0], beidouBitconv(bit), len); return getbitu(&g_cond[0], beidouBitconv(bit), len);
@ -35,7 +34,7 @@ struct BeidouMessage : GPSLikeEphemeris
int fraid{-1}, sow{-1}; // part of every message (thanks!) int fraid{-1}, sow{-1}; // part of every message (thanks!)
int parse(const std::vector<uint8_t>& cond, uint8_t* pageno) int parse(std::basic_string_view<uint8_t> cond, uint8_t* pageno)
{ {
g_cond = cond; g_cond = cond;
if(pageno) if(pageno)
@ -90,7 +89,7 @@ struct BeidouMessage : GPSLikeEphemeris
return {factor * cur, factor * trend}; return {factor * cur, factor * trend};
} }
void parse1(const std::vector<uint8_t>& cond) void parse1(std::basic_string_view<uint8_t> cond)
{ {
sath1 = bbitu(43,1); sath1 = bbitu(43,1);
aodc = bbitu(31+13, 5); aodc = bbitu(31+13, 5);
@ -125,7 +124,7 @@ struct BeidouMessage : GPSLikeEphemeris
return -1; return -1;
} }
void parse2(const std::vector<uint8_t>& cond) void parse2(std::basic_string_view<uint8_t> cond)
{ {
deltan = bbits(43, 16); deltan = bbits(43, 16);
cuc = bbits(67, 18); cuc = bbits(67, 18);
@ -152,7 +151,7 @@ struct BeidouMessage : GPSLikeEphemeris
double getOmega0() const { return ldexp(Omega0 * M_PI, -31); } // radians double getOmega0() const { return ldexp(Omega0 * M_PI, -31); } // radians
double getIdot() const { return ldexp(idot * M_PI, -43); } // radians/s double getIdot() const { return ldexp(idot * M_PI, -43); } // radians/s
double getOmega() const { return ldexp(omega * M_PI, -31); } // radians double getOmega() const { return ldexp(omega * M_PI, -31); } // radians
void parse3(const std::vector<uint8_t>& cond) void parse3(std::basic_string_view<uint8_t> cond)
{ {
t0eLSB = bbitu(43, 15); t0eLSB = bbitu(43, 15);
i0 = bbits(66, 32); i0 = bbits(66, 32);
@ -208,7 +207,7 @@ struct BeidouMessage : GPSLikeEphemeris
} alma; } alma;
// 4 is all almanac // 4 is all almanac
int parse4(const std::vector<uint8_t>& cond) int parse4(std::basic_string_view<uint8_t> cond)
{ {
alma.sqrtA = bbitu(51, 24); alma.sqrtA = bbitu(51, 24);
alma.a1 = bbits(91, 11); alma.a1 = bbits(91, 11);
@ -229,8 +228,7 @@ struct BeidouMessage : GPSLikeEphemeris
// 2^-30 2^-50 // 2^-30 2^-50
int a0gps, a1gps, a0gal, a1gal, a0glo, a1glo, a0utc, a1utc; int a0gps, a1gps, a0gal, a1gal, a0glo, a1glo, a0utc, a1utc;
int8_t deltaTLS, deltaTLSF; int8_t deltaTLS;
uint8_t wnLSF, dn;
// in Beidou the offset is a0utc + SOW * a1utc // in Beidou the offset is a0utc + SOW * a1utc
std::pair<double, double> getUTCOffset(int tow) const std::pair<double, double> getUTCOffset(int tow) const
@ -255,7 +253,7 @@ struct BeidouMessage : GPSLikeEphemeris
} }
int parse5(const std::vector<uint8_t>& cond) int parse5(std::basic_string_view<uint8_t> cond)
{ {
alma.pageno = bbitu(44, 7); alma.pageno = bbitu(44, 7);
if(alma.pageno == 9) { if(alma.pageno == 9) {
@ -270,9 +268,6 @@ struct BeidouMessage : GPSLikeEphemeris
a0utc = bbits(91, 32); a0utc = bbits(91, 32);
a1utc = bbits(131, 24); a1utc = bbits(131, 24);
deltaTLS = bbits(31+12+1+7, 8); deltaTLS = bbits(31+12+1+7, 8);
deltaTLSF = bbits(61+6, 8);
wnLSF = bbits(61+6+8, 8);
dn = bbits(151+12, 8);
} }
else { else {
alma.sqrtA = bbitu(51, 24); alma.sqrtA = bbitu(51, 24);

View file

@ -89,7 +89,7 @@ double getCoordinates(double tow, const T& iod, Point* p, bool quiet=true)
cerr << "sqrtA = "<< sqrtA << endl; cerr << "sqrtA = "<< sqrtA << endl;
cerr << "deltan = "<< deltan << endl; cerr << "deltan = "<< deltan << endl;
cerr << "t0e = "<< t0e << "( rel "<<(tow - t0e)<<")"<<endl; cerr << "t0e = "<< t0e << endl;
cerr << "m0 = "<< m0 << " ("<<todeg(m0)<<")"<<endl; cerr << "m0 = "<< m0 << " ("<<todeg(m0)<<")"<<endl;
cerr << "e = "<< e << endl; cerr << "e = "<< e << endl;
cerr << "omega = " << omega << " ("<<todeg(omega)<<")"<<endl; cerr << "omega = " << omega << " ("<<todeg(omega)<<")"<<endl;
@ -122,7 +122,7 @@ double getCoordinates(double tow, const T& iod, Point* p, bool quiet=true)
double M = m0 + n * tk; double M = m0 + n * tk;
if(!quiet) if(!quiet)
cerr << " M = m0 + n * tk = "<<m0 << " + " << n << " * " << tk << " = " <<M <<endl; cerr << " M = m0 + n * tk = "<<m0 << " + " << n << " * " << tk <<endl;
double E = M; double E = M;
double newE; double newE;
for(int k =0 ; k < 10; ++k) { for(int k =0 ; k < 10; ++k) {

File diff suppressed because it is too large Load diff

27
ext/fmt-6.1.2/LICENSE.rst Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2012 - present, Victor Zverovich
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- Optional exception to the license ---
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into a machine-executable object form of such
source code, you may redistribute such embedded portions in such object form
without including the above copyright and permission notices.

501
ext/fmt-6.1.2/README.rst Normal file
View file

@ -0,0 +1,501 @@
{fmt}
=====
.. image:: https://travis-ci.org/fmtlib/fmt.png?branch=master
:target: https://travis-ci.org/fmtlib/fmt
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
:target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
:alt: fmt is continuously fuzzed att oss-fuzz
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dlibfmt&can=1
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
:alt: Ask questions at StackOverflow with the tag fmt
:target: http://stackoverflow.com/questions/tagged/fmt
**{fmt}** is an open-source formatting library for C++.
It can be used as a safe and fast alternative to (s)printf and iostreams.
`Documentation <https://fmt.dev/latest/>`__
Q&A: ask questions on `StackOverflow with the tag fmt <http://stackoverflow.com/questions/tagged/fmt>`_.
Features
--------
* Replacement-based `format API <https://fmt.dev/dev/api.html>`_ with
positional arguments for localization.
* `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
of `str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
in Python.
* Safe `printf implementation
<https://fmt.dev/latest/api.html#printf-formatting>`_ including
the POSIX extension for positional arguments.
* Implementation of `C++20 std::format <https://fmt.dev/Text%20Formatting.html>`__.
* Support for user-defined types.
* High performance: faster than common standard library implementations of
`printf <http://en.cppreference.com/w/cpp/io/c/fprintf>`_ and
iostreams. See `Speed tests`_ and `Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
* Small code size both in terms of source code (the minimum configuration
consists of just three header files, ``core.h``, ``format.h`` and
``format-inl.h``) and compiled code. See `Compile time and code bloat`_.
* Reliability: the library has an extensive set of `unit tests
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is continuously fuzzed.
* Safety: the library is fully type safe, errors in format strings can be
reported at compile time, automatic memory management prevents buffer overflow
errors.
* Ease of use: small self-contained code base, no external dependencies,
permissive MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
consistent output across platforms and support for older compilers.
* Clean warning-free codebase even on high warning levels
(``-Wall -Wextra -pedantic``).
* Support for wide strings.
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro.
See the `documentation <https://fmt.dev/latest/>`_ for more details.
Examples
--------
Print ``Hello, world!`` to ``stdout``:
.. code:: c++
fmt::print("Hello, {}!", "world"); // Python-like format string syntax
fmt::printf("Hello, %s!", "world"); // printf format string syntax
Format a string and use positional arguments:
.. code:: c++
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
// s == "I'd rather be happy than right."
Check a format string at compile time:
.. code:: c++
// test.cc
#include <fmt/format.h>
std::string s = format(FMT_STRING("{2}"), 42);
.. code::
$ c++ -Iinclude -std=c++14 test.cc
...
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
std::string s = format(FMT_STRING("{2}"), 42);
^
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
ErrorHandler::on_error(message);
^
include/fmt/format.h:2226:16: note: in call to '&checker.context_->on_error(&"argument index out of range"[0])'
context_.on_error("argument index out of range");
^
Use {fmt} as a safe portable replacement for ``itoa``
(`godbolt <https://godbolt.org/g/NXmpU4>`_):
.. code:: c++
fmt::memory_buffer buf;
format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10)
format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16)
// access the string with to_string(buf) or buf.data()
Format objects of user-defined types via a simple `extension API
<https://fmt.dev/latest/api.html#formatting-user-defined-types>`_:
.. code:: c++
#include "fmt/format.h"
struct date {
int year, month, day;
};
template <>
struct fmt::formatter<date> {
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const date& d, FormatContext& ctx) {
return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
}
};
std::string s = fmt::format("The date is {}", date{2012, 12, 9});
// s == "The date is 2012-12-9"
Create your own functions similar to `format
<https://fmt.dev/latest/api.html#format>`_ and
`print <https://fmt.dev/latest/api.html#print>`_
which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
.. code:: c++
// Prints formatted error message.
void vreport_error(const char* format, fmt::format_args args) {
fmt::print("Error: ");
fmt::vprint(format, args);
}
template <typename... Args>
void report_error(const char* format, const Args & ... args) {
vreport_error(format, fmt::make_format_args(args...));
}
report_error("file not found: {}", path);
Note that ``vreport_error`` is not parameterized on argument types which can
improve compile times and reduce code size compared to a fully parameterized
version.
Benchmarks
----------
Speed tests
~~~~~~~~~~~
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
libc printf 1.04
libc++ std::ostream 3.05
{fmt} 6.1.1 fmt::print 0.75
Boost Format 1.67 boost::format 7.24
Folly Format folly::format 2.23
================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.6 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of
three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
further details refer to the `source
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
{fmt} is 10x faster than ``std::ostringstream`` and ``sprintf`` on floating-point
formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and as fast as `double-conversion <https://github.com/google/double-conversion>`_:
.. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
:target: https://fmt.dev/unknown_mac64_clang10.0.html
Compile time and code bloat
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The script `bloat-test.py
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
tests compile time and code bloat for nontrivial projects.
It generates 100 translation units and uses ``printf()`` or its alternative
five times in each to simulate a medium sized project. The resulting
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
macOS Sierra, best of three) is shown in the following tables.
**Optimized build (-O3)**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.6 29 26
printf+string 16.4 29 26
iostreams 31.1 59 55
{fmt} 19.0 37 34
Boost Format 91.9 226 203
Folly Format 115.7 101 88
============= =============== ==================== ==================
As you can see, {fmt} has 60% less overhead in terms of resulting binary code
size compared to iostreams and comes pretty close to ``printf``. Boost Format
and Folly Format have the largest overheads.
``printf+string`` is the same as ``printf`` but with extra ``<string>``
include to measure the overhead of the latter.
**Non-optimized build**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.2 33 30
printf+string 16.0 33 30
iostreams 28.3 56 52
{fmt} 18.2 59 50
Boost Format 54.1 365 303
Folly Format 79.9 445 430
============= =============== ==================== ==================
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to
compare formatting function overhead only. Boost Format is a
header-only library so it doesn't provide any linkage options.
Running the tests
~~~~~~~~~~~~~~~~~
Please refer to `Building the library`__ for the instructions on how to build
the library and run the unit tests.
__ https://fmt.dev/latest/usage.html#building-the-library
Benchmarks reside in a separate repository,
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
so to run the benchmarks you first need to clone this repository and
generate Makefiles with CMake::
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
$ cd format-benchmark
$ cmake .
Then you can run the speed test::
$ make speed-test
or the bloat test::
$ make bloat-test
Projects using this library
---------------------------
* `0 A.D. <http://play0ad.com/>`_: A free, open-source, cross-platform real-time
strategy game
* `AMPL/MP <https://github.com/ampl/mp>`_:
An open-source library for mathematical programming
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
operations suite
* `Celestia <https://celestia.space/>`_: Real-time 3D visualization of space
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system
* `ccache <https://ccache.dev/>`_: A compiler cache
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks
* `KBEngine <http://kbengine.org/>`_: An open-source MMOG server engine
* `Keypirinha <http://keypirinha.com/>`_: A semantic launcher for Windows
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
* `Drake <http://drake.mit.edu/>`_: A planning, control, and analysis toolbox
for nonlinear dynamical systems (MIT)
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
(Lyft)
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
* `MongoDB <https://mongodb.com/>`_: Distributed document database
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
generate randomized datasets
* `OpenSpace <http://openspaceproject.com/>`_: An open-source astrovisualization
framework
* `PenUltima Online (POL) <http://www.polserver.com/>`_:
An MMO server, compatible with most Ultima Online clients
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
associative database
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
proxy
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
library
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:
Small crossplatform 2D graphic engine
* `Salesforce Analytics Cloud <http://www.salesforce.com/analytics-cloud/overview/>`_:
Business intelligence software
* `Scylla <http://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
that can handle 1 million transactions per second on a single server
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++
framework for high-performance server applications on modern hardware
* `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library
* `Stellar <https://www.stellar.org/>`_: Financial platform
* `Touch Surgery <https://www.touchsurgery.com/>`_: Surgery simulator
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
MMORPG framework
`More... <https://github.com/search?q=fmtlib&type=Code>`_
If you are aware of other projects using this library, please let me know
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
`issue <https://github.com/fmtlib/fmt/issues>`_.
Motivation
----------
So why yet another formatting library?
There are plenty of methods for doing this task, from standard ones like
the printf family of function and iostreams to Boost Format and FastFormat
libraries. The reason for creating a new library is that every existing
solution that I found either had serious issues or didn't provide
all the features I needed.
printf
~~~~~~
The good thing about ``printf`` is that it is pretty fast and readily available
being a part of the C standard library. The main drawback is that it
doesn't support user-defined types. ``printf`` also has safety issues although
they are somewhat mitigated with `__attribute__ ((format (printf, ...))
<http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
There is a POSIX extension that adds positional arguments required for
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
to ``printf`` but it is not a part of C99 and may not be available on some
platforms.
iostreams
~~~~~~~~~
The main issue with iostreams is best illustrated with an example:
.. code:: c++
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
which is a lot of typing compared to printf:
.. code:: c++
printf("%.2f\n", 1.23456);
Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams
don't support positional arguments by design.
The good part is that iostreams support user-defined types and are safe although
error handling is awkward.
Boost Format
~~~~~~~~~~~~
This is a very powerful library which supports both ``printf``-like format
strings and positional arguments. Its main drawback is performance. According to
various benchmarks it is much slower than other methods considered here. Boost
Format also has excessive build times and severe code bloat issues (see
`Benchmarks`_).
FastFormat
~~~~~~~~~~
This is an interesting library which is fast, safe and has positional
arguments. However it has significant limitations, citing its author:
Three features that have no hope of being accommodated within the
current design are:
* Leading zeros (or any other non-space padding)
* Octal/hexadecimal encoding
* Runtime width/alignment specification
It is also quite big and has a heavy dependency, STLSoft, which might be
too restrictive for using it in some projects.
Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~
This is not really a formatting library but I decided to include it here for
completeness. As iostreams, it suffers from the problem of mixing verbatim text
with arguments. The library is pretty fast, but slower on integer formatting
than ``fmt::format_int`` on Karma's own benchmark,
see `Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
FAQ
---
Q: how can I capture formatting arguments and format them later?
A: use ``std::tuple``:
.. code:: c++
template <typename... Args>
auto capture(const Args&... args) {
return std::make_tuple(args...);
}
auto print_message = [](const auto&... args) {
fmt::print(args...);
};
// Capture and store arguments:
auto args = capture("{} {}", 42, "foo");
// Do formatting:
std::apply(print_message, args);
License
-------
{fmt} is distributed under the MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
The `Format String Syntax
<https://fmt.dev/latest/syntax.html>`_
section in the documentation is based on the one from Python `string module
documentation <https://docs.python.org/3/library/string.html#module-string>`_
adapted for the current library. For this reason the documentation is
distributed under the Python Software Foundation license available in
`doc/python-license.txt
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
It only applies if you distribute the documentation of fmt.
Acknowledgments
---------------
The {fmt} library is maintained by Victor Zverovich (`vitaut
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
<https://github.com/foonathan>`_) with contributions from many other people.
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
Let us know if your contribution is not listed or mentioned incorrectly and
we'll make it right.
The benchmark section of this readme file and the performance tests are taken
from the excellent `tinyformat <https://github.com/c42f/tinyformat>`_ library
written by Chris Foster. Boost Format library is acknowledged transitively
since it had some influence on tinyformat.
Some ideas used in the implementation are borrowed from `Loki
<http://loki-lib.sourceforge.net/>`_ SafeFormat and `Diagnostic API
<http://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
`Clang <http://clang.llvm.org/>`_.
Format string syntax and the documentation are based on Python's `str.format
<https://docs.python.org/3/library/stdtypes.html#str.format>`_.
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
comments and contribution to the design of the type-safe API and
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary
formatting. Thanks `Ruslan Baratov <https://github.com/ruslo>`_ for comprehensive
`comparison of integer formatting algorithms <https://github.com/ruslo/int-dec-format-tests>`_
and useful comments regarding performance, `Boris Kaul <https://github.com/localvoid>`_ for
`C++ counting digits benchmark <https://github.com/localvoid/cxx-benchmark-count-digits>`_.
Thanks to `CarterLi <https://github.com/CarterLi>`_ for contributing various
improvements to the code.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,570 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COLOR_H_
#define FMT_COLOR_H_
#include "format.h"
FMT_BEGIN_NAMESPACE
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
enum class terminal_color : uint8_t {
black = 30,
red,
green,
yellow,
blue,
magenta,
cyan,
white,
bright_black = 90,
bright_red,
bright_green,
bright_yellow,
bright_blue,
bright_magenta,
bright_cyan,
bright_white
};
enum class emphasis : uint8_t {
bold = 1,
italic = 1 << 1,
underline = 1 << 2,
strikethrough = 1 << 3
};
// rgb is a struct for red, green and blue colors.
// Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb {
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
FMT_CONSTEXPR rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
uint8_t r;
uint8_t g;
uint8_t b;
};
namespace internal {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color);
}
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
}
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
value{} {
value.term_color = static_cast<uint8_t>(term_color);
}
bool is_rgb;
union color_union {
uint8_t term_color;
uint32_t rgb_color;
} value;
};
} // namespace internal
// Experimental text formatting support.
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
const text_style& rhs) {
return lhs |= rhs;
}
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
const text_style& rhs) {
return lhs &= rhs;
}
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
return set_foreground_color;
}
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
return set_background_color;
}
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color;
internal::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace internal {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
bool is_background = esc == internal::data::background_color;
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
std::size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return;
}
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
std::size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
}
buffer[index++] = static_cast<Char>(0);
}
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
return buffer + std::strlen(buffer);
}
private:
Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::background_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em);
}
template <typename Char>
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
std::fputs(chars, stream);
}
template <>
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
std::fputws(chars, stream);
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream);
}
template <typename Char>
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
}
template <typename Char>
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
auto background =
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
vformat_to(buf, format_str, args);
if (has_style) {
internal::reset_color<Char>(buf);
}
}
} // namespace internal
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
internal::fputs(buf.data(), f);
}
/**
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
Example:
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
}
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting.
Example:
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return vformat(ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_

View file

@ -0,0 +1,585 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#include <vector>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
enum class kind { arg_index, arg_name, text, replacement };
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
kind part_kind;
union value {
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(int index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
};
template <typename Char> struct part_counter {
unsigned num_parts = 0;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++brace_counter;
} else if (*begin == '}') {
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
}
FMT_CONSTEXPR void on_error(const char*) {}
};
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR void on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it;
}
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename Range, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned num_format_parts = 1;
#endif
using parts_container = format_part<char_type>[num_format_parts];
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve
return copy_str<Char>(data.begin(), data.end(), out);
}
};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return format_default<Char>(out, arg);
}
};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str;
if constexpr (str[POS] == '{') {
if (POS + 1 == str.size())
throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) {
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else {
return unknown_format();
}
} else {
return unknown_format();
}
} else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size())
throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
}
}
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0);
} else {
constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
}
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(std::back_inserter(buffer), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
{make_format_args<context>(args...)});
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>(
range(out), cf, {make_format_args<context>(args...)});
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf,
const Args&... args) {
auto it =
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(internal::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,77 @@
// Formatting library for C++ - std::locale support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(const std::locale& loc,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
}
} // namespace internal
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
return internal::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
loc, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
const S& format_str,
format_args_t<OutputIt, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
}
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_

View file

@ -0,0 +1,141 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
using int_type = typename std::basic_streambuf<Char>::int_type;
using traits_type = typename std::basic_streambuf<Char>::traits_type;
buffer<Char>& buffer_;
public:
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
// Hide all operator<< from std::basic_ostream<Char>.
void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
};
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
void_t<>>::value>
test(int);
template <typename> static std::false_type test(...);
using result = decltype(test<T>(0));
public:
static const bool value = result::value;
};
// Write the content of buf to os.
template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
if (loc) output.imbue(loc.get<std::locale>());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buf.resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: formatter<basic_string_view<Char>, Char> {
template <typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
};
} // namespace internal
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_

View file

@ -0,0 +1,321 @@
// A C++ interface to POSIX functions.
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <cerrno>
#include <clocale> // for locale_t
#include <cstdio>
#include <cstdlib> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void* buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void* buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) { _free_locale(locale); }
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
public:
using type = locale_t;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_POSIX_H_

View file

@ -0,0 +1,711 @@
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "ostream.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
unsigned max = max_value<int>();
return value <= max;
}
static bool fits_in_int(bool) { return true; }
};
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
};
class printf_precision_handler {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big"));
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) {
FMT_THROW(format_error("precision is not integer"));
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
class is_zero_int {
public:
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
template <typename T, typename Context> class arg_converter {
private:
using char_type = typename Context::char_type;
basic_format_arg<Context>& arg_;
char_type type_;
public:
arg_converter(basic_format_arg<Context>& arg, char_type type)
: arg_(arg), type_(type) {}
void operator()(bool value) {
if (type_ != 's') operator()<bool>(value);
}
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = internal::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} else {
if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = internal::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
}
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
void operator()(U) {} // No conversion needed for non-integral types.
};
// Converts an integer argument to T for printf, if T is an integral type.
// If T is void, the argument is converted to corresponding signed or unsigned
// type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
}
// Converts an integer argument to char for printf.
template <typename Context> class char_converter {
private:
basic_format_arg<Context>& arg_;
public:
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = internal::make_arg<Context>(
static_cast<typename Context::char_type>(value));
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
void operator()(T) {} // No conversion needed for non-integral types.
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
private:
using format_specs = basic_format_specs<Char>;
format_specs& specs_;
public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) {
FMT_THROW(format_error("width is not integer"));
return 0;
}
};
template <typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
}
template <typename OutputIt, typename Char, typename Context>
internal::truncating_iterator<OutputIt> printf(
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
basic_format_args<Context> args) {
return Context(it, format, args).format();
}
} // namespace internal
using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter;
template <typename OutputIt, typename Char> class basic_printf_context;
/**
\rst
The ``printf`` argument formatter.
\endrst
*/
template <typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
public:
using iterator = typename Range::iterator;
private:
using char_type = typename Range::value_type;
using base = internal::arg_formatter_base<Range>;
using context_type = basic_printf_context<iterator, char_type>;
context_type& context_;
void write_null_pointer(char) {
this->specs()->type = 0;
this->write("(nil)");
}
void write_null_pointer(wchar_t) {
this->specs()->type = 0;
this->write(L"(nil)");
}
public:
using format_specs = typename base::format_specs;
/**
\rst
Constructs an argument formatter object.
*buffer* is a reference to the output buffer and *specs* contains format
specifier information for standard argument types.
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
fmt_specs.type = 0;
this->write(value != 0);
} else if (std::is_same<T, char_type>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.align = align::right;
return base::operator()(value);
} else {
return base::operator()(value);
}
return this->out();
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) {
return base::operator()(value);
}
/** Formats a null-terminated C string. */
iterator operator()(const char* value) {
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
return this->out();
}
/** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t* value) {
if (value)
base::operator()(value);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
return this->out();
}
iterator operator()(basic_string_view<char_type> value) {
return base::operator()(value);
}
iterator operator()(monostate value) { return base::operator()(value); }
/** Formats a pointer. */
iterator operator()(const void* value) {
if (value) return base::operator()(value);
this->specs()->type = 0;
write_null_pointer(char_type());
return this->out();
}
/** Formats an argument of a custom (user-defined) type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(context_.parse_context(), context_);
return this->out();
}
};
template <typename T> struct printf_formatter {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value);
return ctx.out();
}
};
/** This template formats data and writes the output to a writer. */
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
using format_specs = basic_format_specs<char_type>;
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_format_parse_context<Char> parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index.
int parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments and
the writer are stored in the context object so make sure they have
appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args), parse_ctx_(format_str) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
format_arg arg(int id) const { return args_.get(id); }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
OutputIt format();
};
template <typename OutputIt, typename Char>
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
const Char*& it,
const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
specs.align = align::left;
break;
case '+':
specs.sign = sign::plus;
break;
case '0':
specs.fill[0] = '0';
break;
case ' ':
specs.sign = sign::space;
break;
case '#':
specs.alt = true;
break;
default:
return;
}
}
}
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
int basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
int arg_index = -1;
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
internal::error_handler eh;
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value;
} else {
if (c == '0') specs.fill[0] = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
specs.width = value;
return arg_index;
}
}
}
parse_flags(specs, it, end);
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
internal::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
}
template <typename OutputIt, typename Char>
template <typename ArgFormatter>
OutputIt basic_printf_context<OutputIt, Char>::format() {
auto out = this->out();
const Char* start = parse_ctx_.begin();
const Char* end = parse_ctx_.end();
auto it = start;
while (it != end) {
char_type c = *it++;
if (c != '%') continue;
if (it != end && *it == c) {
out = std::copy(start, it, out);
start = ++it;
continue;
}
out = std::copy(start, it - 1, out);
format_specs specs;
specs.align = align::right;
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
internal::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') {
++it;
specs.precision =
static_cast<int>(visit_format_arg(internal::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic())
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0;
using internal::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
++it;
t = it != end ? *it : 0;
convert_arg<signed char>(arg, t);
} else {
convert_arg<short>(arg, t);
}
break;
case 'l':
if (t == 'l') {
++it;
t = it != end ? *it : 0;
convert_arg<long long>(arg, t);
} else {
convert_arg<long>(arg, t);
}
break;
case 'j':
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<std::size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--it;
convert_arg<void>(arg, c);
}
// Parse type.
if (it == end) FMT_THROW(format_error("invalid format string"));
specs.type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
switch (specs.type) {
case 'i':
case 'u':
specs.type = 'd';
break;
case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
arg);
break;
}
}
start = it;
// Format argument.
visit_format_arg(ArgFormatter(out, specs, *this), arg);
}
return std::copy(start, it, out);
}
template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template <typename... Args>
inline format_arg_store<printf_context, Args...> make_printf_args(
const Args&... args) {
return {args...};
}
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... Args>
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
const Args&... args) {
return {args...};
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf(
const S& format, basic_format_args<basic_printf_context_t<Char>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
return to_string(buffer);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), {make_format_args<context>(args...)});
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format),
{make_format_args<context>(args...)});
}
template <typename S, typename Char = char_t<S>>
inline int vprintf(const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
return vfprintf(stdout, to_string_view(format), args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
{make_format_args<context>(args...)});
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
internal::write(os, buffer);
return static_cast<int>(buffer.size());
}
/** Formats arguments and writes the output to the range. */
template <typename ArgFormatter, typename Char,
typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
basic_string_view<Char> format_str,
basic_format_args<Context> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
{make_format_args<context>(args...)});
}
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_

View file

@ -0,0 +1,365 @@
// Formatting library for C++ - experimental range support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <type_traits>
#include "format.h"
// output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif
FMT_BEGIN_NAMESPACE
template <typename Char> struct formatting_base {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
Char delimiter;
Char postfix;
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
template <typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char> {
Char prefix;
Char delimiter;
Char postfix;
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace internal {
template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIterator>
OutputIterator copy(const char* str, OutputIterator out) {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIterator>
OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch;
return out;
}
/// Return true value if T has std::string interface, like std::string_view.
template <typename T> class is_like_std_string {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
template <typename T>
struct is_range_<
T, conditional_t<false,
conditional_helper<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>,
void>> : std::true_type {};
#endif
/// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p)
-> decltype(std::tuple_size<U>::value,
(void)std::declval<typename std::tuple_element<0, U>::type>(),
int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
};
template <std::size_t... N>
using index_sequence = integer_sequence<std::size_t, N...>;
template <typename T, std::size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
using std::get;
// using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
(void)_; // blocks warnings
}
template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
T const&) {
return {};
}
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
return add_space ? " {}" : "{}";
}
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
return add_space ? L" \"{}\"" : L"\"{}\"";
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
return add_space ? " '{}'" : "'{}'";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace internal
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
};
template <typename TupleT, typename Char>
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
private:
// C++11 generic lambda for format()
template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) {
if (i > 0) {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
out = internal::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
std::size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
public:
formatting_tuple<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
return ctx.out();
}
};
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
struct formatter<RangeT, Char,
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
formatting_range<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
auto out = internal::copy(formatting.prefix, ctx.out());
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
out = format_to(out, " ... <other elements>");
break;
}
}
if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
template <typename FormatContext>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out();
out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
return {tuple, sep};
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

176
ext/fmt-6.1.2/src/format.cc Normal file
View file

@ -0,0 +1,176 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
struct sprintf_specs {
int precision;
char type;
bool alt : 1;
template <typename Char>
constexpr sprintf_specs(basic_format_specs<Char> specs)
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
constexpr bool has_precision() const { return precision >= 0; }
};
// This is deprecated and is kept only to preserve ABI compatibility.
template <typename Double>
char* sprintf_format(Double value, internal::buffer<char>& buf,
sprintf_specs specs) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
// Build format string.
enum { max_format_size = 10 }; // longest format: %#-*.*Lg
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
if (specs.alt || !specs.type) *format_ptr++ = '#';
if (specs.precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
char type = specs.type;
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
#if FMT_MSC_VER
if (type == 'F') {
// MSVC's printf doesn't support 'F'.
type = 'f';
}
#endif
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
char* start = nullptr;
char* decimal_point_pos = nullptr;
for (;;) {
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result =
format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0) {
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity()) {
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-') ++p;
if (specs.type != 'a' && specs.type != 'A') {
while (p < end && *p >= '0' && *p <= '9') ++p;
if (p < end && *p != 'e' && *p != 'E') {
decimal_point_pos = p;
if (!specs.type) {
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0') ++p;
while (p != end && *p >= '1' && *p <= '9') ++p;
char* where = p;
while (p != end && *p == '0') ++p;
if (p == end || *p < '0' || *p > '9') {
if (p != end) std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
}
}
}
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
}
buf.reserve(n + 1);
} else {
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf.reserve(buf.capacity() + 1);
}
}
return decimal_point_pos;
}
} // namespace internal
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
sprintf_specs);
template FMT_API char* internal::sprintf_format(long double,
internal::buffer<char>&,
sprintf_specs);
template struct FMT_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs,
internal::buffer<char>&) =
internal::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char*, const char*);
template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context>& args);
template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API int internal::snprintf_float(double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE

237
ext/fmt-6.1.2/src/posix.cc Normal file
View file

@ -0,0 +1,237 @@
// A C++ interface to POSIX functions.
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
// Disable bogus MSVC warnings.
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/posix.h"
#include <climits>
#if FMT_USE_FCNTL
#include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32
# include <unistd.h>
#else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# include <windows.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#endif // FMT_USE_FCNTL
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
using RWResult = int;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
// Return type of read and write functions.
using RWResult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
} // namespace
FMT_BEGIN_NAMESPACE
buffered_file::~buffered_file() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file");
}
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
nullptr);
if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
}
void buffered_file::close() {
if (!file_) return;
int result = FMT_SYSTEM(fclose(file_));
file_ = nullptr;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd;
}
#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
#else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
#endif
if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
}
file::~file() FMT_NOEXCEPT {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
report_system_error(errno, "cannot close file");
}
void file::close() {
if (fd_ == -1) return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
long long file::size() const {
#ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
// Both functions support large file sizes.
DWORD size_upper = 0;
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
using Stat = struct stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes"));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough");
return file_stat.st_size;
#endif
}
std::size_t file::read(void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
return internal::to_unsigned(result);
}
std::size_t file::write(const void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
return internal::to_unsigned(result);
}
file file::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
return file(new_fd);
}
void file::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
fd_, fd));
}
}
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) ec = error_code(errno);
}
void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
#ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
#else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
#endif
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));
buffered_file bf(f);
fd_ = -1;
return bf;
}
long getpagesize() {
#ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
#else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
return size;
#endif
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

@ -1 +1 @@
Subproject commit 8d626a24125264dee5cfa549a59b0a7a7a732e6a Subproject commit f944a5992515d8bbee7c2be0642a0fe37a230519

View file

@ -1,218 +0,0 @@
#include "fixhunter.hh"
#include <iostream>
#include "bits.hh"
#include "rs.hh"
#include "ephemeris.hh"
#include "galileo.hh"
using namespace std;
void FixHunter::reportInav(const vector<uint8_t>& inav, int32_t gst)
{
int wtype = getbitu(&inav[0], 0, 6);
if(wtype >= 1 && wtype <=4) {
d_latestiod = getbitu(&inav[0], 6, 10);
if(wtype == 1) inav1 = inav;
else if(wtype == 2) inav2 = inav;
else if(wtype == 3) inav3 = inav;
else if(wtype == 4) inav4 = inav;
}
else if(wtype == 16) inav16 = inav;
else if(wtype == 17) inav17 = inav;
else if(wtype == 18) inav18 = inav;
else if(wtype == 19) inav19 = inav;
else if(wtype == 20) inav20 = inav;
else return;
if(wtype == 16) {
GalileoMessage gm;
gm.parse(inav);
int32_t t0r = 1 + gst - (gst%30);
d_inav16t0r = t0r;
cout<<" redced af0red "<< 1000000000.0*ldexp(gm.af0red, -26)<<" ns, "<<3600.0*(1000000000.0/(1<<20))*ldexp(gm.af1red, -15)<<" ns/hour ("<<gm.af1red<<") t0r "<<t0r<<" ";
//(30*((nmm.gi().gnsstow()-2)/30)+1) % 604800; // page 56 of the OSS ICD 2.0
REDCEDAdaptor rca(gm, t0r);
Point pointRed;
cout<<"eyred "<<gm.eyred<<" exred "<<gm.exred<<"\nlambda0red in rad "<< ldexp(M_PI*gm.lambda0red, -22)<<" atan2 " <<atan2(1.0*gm.eyred, 1.0*gm.exred)<<" deltaAred "<<gm.deltaAred<<endl;
getCoordinates(gst, rca, &pointRed, false);
cout<<"Reduced coordinates: "<<pointRed<<endl;
}
tryFix(gst);
}
void FixHunter::tryFix(int32_t gst)
{
RSCodec rsc({0, 2, 3, 4, 8}, 1, 1, 60, 137);
string in;
vector<unsigned int> corr;
int cnt=0;
for(const auto& theinav : {inav4, inav3, inav2, inav1, inav20, inav19, inav18, inav17})
if(!theinav.empty()) ++cnt;
if(cnt < 4)
return;
auto blankOut = [&in, &corr](int amount) {
for(int p=0; p < amount ; ++p) {
corr.push_back(in.size());
in.append(1, (char)0); // we just don't have the information
}
};
int wtype=4;
int navcount=0;
for(const auto& theinav : {inav4, inav3, inav2}) {
if(!theinav.empty()) {
++navcount;
cout<<"Have wtype "<<wtype<<endl;
unsigned char tmp[14];
for(int i=0; i < 14; i++)
setbitu(tmp, i*8, 8, getbitu(&theinav[0], 16+i*8, 8));
std::reverse(begin(tmp), end(tmp));
in.append((char*)tmp, 14);
}
else {
cout<<"Blanking wtype "<<wtype<<endl;
blankOut(14);
}
wtype--;
}
if(!inav1.empty()) {
cout<<"Have wtype "<<wtype<<endl;
++navcount;
unsigned char tmp[16];
setbitu(tmp, 0, 6, 1); // wtype 1 somehow
setbitu(tmp, 6, 2, getbitu(&inav1[0], 14, 2)); // last 2 bits of iodnav
setbitu(tmp, 8, 8, getbitu(&inav1[0], 6, 8)); // first 8 bits of iodnav
for(int i=0; i < 14; ++i)
setbitu(tmp, 16+i*8, 8, getbitu(&inav1[0], 16+i*8, 8));
std::reverse(begin(tmp), end(tmp));
in.append((char*)tmp, 16);
}
else {
cout<<"Blanking wtype "<< wtype<<", which is special"<<endl;
blankOut(14); // 14 symbols we don't have
unsigned char tmp[2];
// and two that we can fake:
setbitu(tmp, 0, 6, 1); // wtype 1 somehow
setbitu(tmp, 6, 2, d_latestiod & 3); // last 2 bits of iodnav
setbitu(tmp, 8, 8, d_latestiod >> 2);
std::reverse(begin(tmp), end(tmp));
in.append((char*)tmp, 2);
}
// now the parity bits
wtype=20;
for(const auto& theinav : {inav20, inav19, inav18, inav17}) {
if(!theinav.empty()) {
cout<<"Have wtype "<<wtype<<endl;
GalileoMessage gm;
gm.parse(theinav);
std::reverse(gm.rsparity.begin(), gm.rsparity.end());
in += gm.rsparity;
}
else {
cout<<"Blanking wtype "<<wtype<<endl;
blankOut(15);
}
--wtype;
}
if(corr.size() >= 60 && navcount != 4) {
cout<<"Too many erasures ("<<corr.size()<<"), can't correct"<<endl;
return;
}
for(auto& c : corr)
c = 137 + c;
string out;
cout<<"Input size "<<in.size()<<", corrections/erasure size "<<corr.size()<<", decode status: "<<endl;
try {
if(corr.size() < 60) {
cout<<"Got a reconstructed ephemeris! With "<<rsc.decode(in, out, &corr)<<" corrections"<<endl;
for(const auto& c : corr) {
cout<<c-137<<" "; // padding!
}
cout<<endl;
}
else {
cout<<"Had all 4 nav words but no parity, not doing corrections"<<endl;
out=in;
}
struct GalileoMessage gm=fillGMFromRS(out);
Point point;
getCoordinates(gst, gm, &point, false);
cout<<"full coordinates: "<<point<<endl;
if(!inav16.empty()) {
GalileoMessage gm16;
gm16.parse(inav16);
REDCEDAdaptor rca(gm16, d_inav16t0r);
Point pointRed;
cout<<"eyred "<<gm.eyred<<" exred "<<gm.exred<<"\nlambda0red in rad "<< ldexp(M_PI*gm.lambda0red, -22)<<" atan2 " <<atan2(1.0*gm.eyred, 1.0*gm.exred)<<" deltaAred "<<gm.deltaAred<<endl;
getCoordinates(gst, rca, &pointRed);
cout<<"Reduced coordinates: "<<pointRed<<", distance: ";
Vector dist(pointRed, point);
cout<<"Distance: "<<dist<<", length "<<dist.length()<<", clockdiff "<<
(rca.getAtomicOffset(gst).first - gm.getAtomicOffset(gst).first)/3<<"m"<<endl;
}
}
catch(...)
{
cout << "failed"<<endl;
}
}
struct GalileoMessage FixHunter::fillGMFromRS(const std::string& out)
{
// we need to reconstruct words 1, 2, 3 and 4 and feed them to the parser
vector<uint8_t> inav[5];
string inavraw[5];
inavraw[4] = out.substr(0, 14);
inavraw[3] = out.substr(14, 14);
inavraw[2] = out.substr(28, 14);
inavraw[1] = out.substr(42, 16);
for(int n=1; n<=4; ++n)
reverse(inavraw[n].begin(), inavraw[n].end());
uint8_t tmp[16];
setbitu(tmp, 0, 6, 1); // wtype 1
setbitu(tmp, 6, 2, getbitu((unsigned char*) inavraw[1].c_str(), 14, 2));
setbitu(tmp, 8, 8, getbitu((unsigned char*) inavraw[1].c_str(), 6, 8));
for(int n=0; n < 14; ++n)
setbitu(tmp, 16 + n*8, 8, getbitu((unsigned char*) inavraw[1].c_str(), 16 + n*8, 8));
inav[1]= makeVec(tmp, 16);
struct GalileoMessage gm={};
gm.parse(inav[1]);
cout<<"wtype: "<<(int)gm.wtype<<", iod "<<gm.iodnav<<endl;
cout<<"T0e "<<gm.getT0e()<<" e "<<gm.getE()<<" sqrtA " << gm.getSqrtA() << endl;
for(int i = 2; i <= 4; ++i) {
setbitu(tmp, 0, 6, i);
setbitu(tmp, 6, 10, getbitu((unsigned char*) inav[1].c_str(), 6, 10)); // fake in IOD from inav1
for(int n=0; n < 14; ++n)
setbitu(tmp, 16 + n*8, 8, getbitu((unsigned char*) inavraw[i].c_str(), n*8, 8));
inav[i]= makeVec(tmp, 16);
gm.parse(inav[i]);
cout<<"wtype: "<<(int)gm.wtype<<", iod "<<gm.iodnav<<endl;
}
return gm;
}

View file

@ -1,15 +0,0 @@
#pragma once
#include <string>
#include "galileo.hh"
class FixHunter
{
public:
void reportInav(const std::vector<uint8_t>& inav, int32_t gst);
private:
void tryFix(int32_t gst);
struct GalileoMessage fillGMFromRS(const std::string& out);
std::vector<uint8_t> inav1, inav2, inav3, inav4, inav16, inav17, inav18, inav19, inav20;
int d_latestiod;
uint32_t d_inav16t0r;
};

View file

@ -1,7 +1,7 @@
#include "bits.hh" #include "bits.hh"
#include "galileo.hh" #include "galileo.hh"
bool getTOWFromInav(const std::vector<uint8_t>& inav, uint32_t *satTOW, uint16_t *wn) bool getTOWFromInav(std::basic_string_view<uint8_t> inav, uint32_t *satTOW, uint16_t *wn)
{ {
unsigned int wtype = getbitu(&inav[0], 0, 6); unsigned int wtype = getbitu(&inav[0], 0, 6);
if(wtype==0) { if(wtype==0) {
@ -24,139 +24,3 @@ bool getTOWFromInav(const std::vector<uint8_t>& inav, uint32_t *satTOW, uint16_t
return false; return false;
} }
int GalileoMessage::parseFnav(const std::vector<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);
}
else if(wtype == 5) { // almanac1, containing 1.5 satellites
ioda = gbum(4);
alma1.wnalmanac = gbum(2);
alma1.t0almanac = gbum(10);
alma1.svid = gbum(6);
alma1.deltaSqrtA = gbsm(13);
alma1.e = gbum(11);
alma1.omega = gbum(16);
alma1.deltai = gbum(11);
alma1.Omega0 = gbum(16);
alma1.Omegadot = gbum(11);
alma1.M0 = gbum(16);
alma1.af0 = gbsm(16);
alma1.af1 = gbsm(13);
alma1.e5ahs = gbum(2);
alma2.svid = gbum(6);
alma2.deltaSqrtA = gbsm(13);
alma2.e = gbum(11);
alma2.omega = gbum(16);
alma2.deltai = gbum(11);
alma2.Omega0 = gbum(4);
// omega02 .. is partial
}
else if(wtype == 6) { // almanac2, containing 1.5 satellites
ioda = gbum(4);
alma2.Omega0 = gbum(12); // PARTIAL, does not really work like this
alma2.Omegadot = gbum(11);
alma2.M0 = gbum(16);
alma2.af0 = gbsm(16);
alma2.af1 = gbsm(13);
alma2.e5ahs = gbum(2);
alma3.svid = gbum(6);
alma3.deltaSqrtA = gbsm(13);
alma3.e = gbum(11);
alma3.omega = gbum(16);
alma3.deltai = gbum(11);
alma3.Omega0 = gbum(4);
alma3.M0 = gbum(16);
alma3.af0 = gbsm(16);
alma3.af1 = gbsm(13);
alma3.e5ahs = gbum(2);
}
return wtype;
}

View file

@ -2,52 +2,44 @@
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include <functional> #include <functional>
#include "ephemeris.hh" #include "ephemeris.hh"
#include "bits.hh" #include "bits.hh"
bool getTOWFromInav(const std::vector<uint8_t>& inav, uint32_t *satTOW, uint16_t *wn); bool getTOWFromInav(std::basic_string_view<uint8_t> inav, uint32_t *satTOW, uint16_t *wn);
struct GalileoMessage : GPSLikeEphemeris struct GalileoMessage : GPSLikeEphemeris
{ {
uint8_t wtype; uint8_t wtype;
typedef void (GalileoMessage::*func_t)(const std::vector<uint8_t>& page); typedef void (GalileoMessage::*func_t)(std::basic_string_view<uint8_t> page);
std::map<int, func_t> parsers{ std::vector<func_t> parsers{
{0, &GalileoMessage::parse0}, &GalileoMessage::parse0,
{1, &GalileoMessage::parse1}, &GalileoMessage::parse1,
{2, &GalileoMessage::parse2}, &GalileoMessage::parse2,
{3, &GalileoMessage::parse3}, &GalileoMessage::parse3,
{4, &GalileoMessage::parse4}, &GalileoMessage::parse4,
{5, &GalileoMessage::parse5}, &GalileoMessage::parse5,
{6, &GalileoMessage::parse6}, &GalileoMessage::parse6,
{7, &GalileoMessage::parse7}, &GalileoMessage::parse7,
{8, &GalileoMessage::parse8}, &GalileoMessage::parse8,
{9, &GalileoMessage::parse9}, &GalileoMessage::parse9,
{10, &GalileoMessage::parse10}, &GalileoMessage::parse10
{16, &GalileoMessage::parse16},
{17, &GalileoMessage::parseRS},
{18, &GalileoMessage::parseRS},
{19, &GalileoMessage::parseRS},
{20, &GalileoMessage::parseRS}
}; };
int parse(const std::vector<uint8_t>& page) int parse(std::basic_string_view<uint8_t> page)
{ {
wtype = getbitu(&page[0], 0, 6); wtype = getbitu(&page[0], 0, 6);
if(!parsers.count(wtype)) { if(wtype >= parsers.size()) {
// std::cerr<<"Asked for impossible galileo type "<<(int)wtype<<std::endl; // std::cerr<<"Asked for impossible galileo type "<<(int)wtype<<std::endl;
return wtype; return wtype;
} }
std::invoke(parsers[wtype], this, page); std::invoke(parsers.at(wtype), this, page);
return wtype; return wtype;
} }
int parseFnav(const std::vector<uint8_t>& page);
uint8_t sparetime{0}; uint8_t sparetime{0};
uint16_t wn{0}; uint16_t wn{0};
uint32_t tow{0}; uint32_t tow{0};
@ -58,7 +50,7 @@ struct GalileoMessage : GPSLikeEphemeris
// spare word, only contains a WN and a TOW, but only if the 'time' field is set to 2 // spare word, only contains a WN and a TOW, but only if the 'time' field is set to 2
void parse0(const std::vector<uint8_t>& page) void parse0(std::basic_string_view<uint8_t> page)
{ {
sparetime = getbitu(&page[0], 6, 2); sparetime = getbitu(&page[0], 6, 2);
if(sparetime == 2) { if(sparetime == 2) {
@ -71,13 +63,13 @@ struct GalileoMessage : GPSLikeEphemeris
{ {
} }
uint8_t e5ahs{0}, e5bhs{0}, e1bhs{0}; uint8_t e5bhs{0}, e1bhs{0};
uint8_t gpshealth{0}; uint8_t gpshealth{0};
uint16_t ai0{0}; uint16_t ai0{0};
int16_t ai1{0}, ai2{0}; int16_t ai1{0}, ai2{0};
bool sf1{0}, sf2{0}, sf3{0}, sf4{0}, sf5{0}; bool sf1{0}, sf2{0}, sf3{0}, sf4{0}, sf5{0};
int BGDE1E5a{0}, BGDE1E5b{0}; int BGDE1E5a{0}, BGDE1E5b{0};
bool e5advs{false}, e5bdvs{false}, e1bdvs{false}; bool e5bdvs{false}, e1bdvs{false};
bool disturb1{false}, disturb2{false}, disturb3{false}, disturb4{false}, disturb5{false}; bool disturb1{false}, disturb2{false}, disturb3{false}, disturb4{false}, disturb5{false};
// //
@ -107,29 +99,17 @@ struct GalileoMessage : GPSLikeEphemeris
uint16_t iodnav; uint16_t iodnav;
int16_t deltaAred; // 2^8 meters
int16_t exred; // 2^-22 dimensionless
int16_t eyred; // 2^-22 dimensionless
int32_t deltai0red; // 2^-22 semi-circles
int32_t omega0red; // 2^-22 semi-circles
int32_t lambda0red; // 2^-22 semi-circles
int32_t af0red; // 2^-26 s
int32_t af1red; // 2^-35 s/s
uint8_t rs2bitiod;
std::string rsparity;
int getIOD() const int getIOD() const
{ {
return iodnav; return iodnav;
} }
int ioda{-1}; // iod almanac
struct Almanac struct Almanac
{ {
int svid{-1}; int svid{-1};
int t0almanac, wnalmanac; int t0almanac, wnalmanac;
int af0, af1; int af0, af1;
int e1bhs, e5bhs, e5ahs; int e1bhs, e5bhs;
uint32_t e, deltaSqrtA; uint32_t e, deltaSqrtA;
int32_t M0, Omega0, deltai, omega, Omegadot; int32_t M0, Omega0, deltai, omega, Omegadot;
@ -164,7 +144,7 @@ struct GalileoMessage : GPSLikeEphemeris
// an ephemeris word // an ephemeris word
void parse1(const std::vector<uint8_t>& page) void parse1(std::basic_string_view<uint8_t> page)
{ {
iodnav = getbitu(&page[0], 6, 10); iodnav = getbitu(&page[0], 6, 10);
t0e = getbitu(&page[0], 16, 14); t0e = getbitu(&page[0], 16, 14);
@ -174,7 +154,7 @@ struct GalileoMessage : GPSLikeEphemeris
} }
// another ephemeris word // another ephemeris word
void parse2(const std::vector<uint8_t>& page) void parse2(std::basic_string_view<uint8_t> page)
{ {
iodnav = getbitu(&page[0], 6, 10); iodnav = getbitu(&page[0], 6, 10);
omega0 = getbits(&page[0], 16, 32); omega0 = getbits(&page[0], 16, 32);
@ -184,7 +164,7 @@ struct GalileoMessage : GPSLikeEphemeris
} }
// yet another ephemeris word // yet another ephemeris word
void parse3(const std::vector<uint8_t>& page) void parse3(std::basic_string_view<uint8_t> page)
{ {
iodnav = getbitu(&page[0], 6, 10); iodnav = getbitu(&page[0], 6, 10);
omegadot = getbits(&page[0], 16, 24); omegadot = getbits(&page[0], 16, 24);
@ -244,7 +224,7 @@ struct GalileoMessage : GPSLikeEphemeris
{ {
int dw = (int)(wn%64) - (int)(wn0g%64); int dw = (int)(wn%64) - (int)(wn0g%64);
if(dw > 31) if(dw > 31)
dw = dw - 64; dw = 31- dw;
int delta = dw*7*86400 + tow - getT0g(); // NOT ephemeris age tricks int delta = dw*7*86400 + tow - getT0g(); // NOT ephemeris age tricks
// 2^-35 2^-51 3600 // 2^-35 2^-51 3600
@ -264,7 +244,7 @@ struct GalileoMessage : GPSLikeEphemeris
// can't get enough of that ephemeris // can't get enough of that ephemeris
void parse4(const std::vector<uint8_t>& page) void parse4(std::basic_string_view<uint8_t> page)
{ {
iodnav = getbitu(&page[0], 6, 10); iodnav = getbitu(&page[0], 6, 10);
cic = getbits(&page[0], 22, 16); cic = getbits(&page[0], 22, 16);
@ -277,7 +257,7 @@ struct GalileoMessage : GPSLikeEphemeris
} }
// ionospheric disturbance, health, group delay, time // ionospheric disturbance, health, group delay, time
void parse5(const std::vector<uint8_t>& page) void parse5(std::basic_string_view<uint8_t> page)
{ {
ai0 = getbitu(&page[0], 6, 11); ai0 = getbitu(&page[0], 6, 11);
ai1 = getbits(&page[0], 17, 11); // ai1 & 2 are signed, 0 not ai1 = getbits(&page[0], 17, 11); // ai1 & 2 are signed, 0 not
@ -300,7 +280,7 @@ struct GalileoMessage : GPSLikeEphemeris
} }
// time stuff // time stuff
void parse6(const std::vector<uint8_t>& page) void parse6(std::basic_string_view<uint8_t> page)
{ {
a0 = getbits(&page[0], 6, 32); a0 = getbits(&page[0], 6, 32);
a1 = getbits(&page[0], 38, 24); a1 = getbits(&page[0], 38, 24);
@ -315,7 +295,7 @@ struct GalileoMessage : GPSLikeEphemeris
} }
// almanac // almanac
void parse7(const std::vector<uint8_t>& page) void parse7(std::basic_string_view<uint8_t> page)
{ {
iodalmanac = getbitu(&page[0], 6, 4); iodalmanac = getbitu(&page[0], 6, 4);
alma1.wnalmanac = wnalmanac = getbitu(&page[0], 10, 2); alma1.wnalmanac = wnalmanac = getbitu(&page[0], 10, 2);
@ -331,7 +311,7 @@ struct GalileoMessage : GPSLikeEphemeris
} }
// almanac // almanac
void parse8(const std::vector<uint8_t>& page) void parse8(std::basic_string_view<uint8_t> page)
{ {
iodalmanac = getbitu(&page[0], 6, 4); iodalmanac = getbitu(&page[0], 6, 4);
alma1.af0 = getbits(&page[0], 10, 16); alma1.af0 = getbits(&page[0], 10, 16);
@ -350,7 +330,7 @@ struct GalileoMessage : GPSLikeEphemeris
} }
// almanac // almanac
void parse9(const std::vector<uint8_t>& page) void parse9(std::basic_string_view<uint8_t> page)
{ {
iodalmanac = getbitu(&page[0], 6, 4); iodalmanac = getbitu(&page[0], 6, 4);
alma2.wnalmanac = wnalmanac = getbitu(&page[0], 10, 2); alma2.wnalmanac = wnalmanac = getbitu(&page[0], 10, 2);
@ -371,7 +351,7 @@ struct GalileoMessage : GPSLikeEphemeris
} }
// almanac + more time stuff (GPS) // almanac + more time stuff (GPS)
void parse10(const std::vector<uint8_t>& page) void parse10(std::basic_string_view<uint8_t> page)
{ {
iodalmanac = getbitu(&page[0], 6, 4); iodalmanac = getbitu(&page[0], 6, 4);
alma3.Omega0 = getbits(&page[0], 10, 16); alma3.Omega0 = getbits(&page[0], 10, 16);
@ -389,35 +369,6 @@ struct GalileoMessage : GPSLikeEphemeris
wn0g = getbitu(&page[0], 122, 6); wn0g = getbitu(&page[0], 122, 6);
} }
// reduced clock and ephemeris data (redced)
void parse16(const std::vector<uint8_t>& page)
{
deltaAred = getbits(&page[0], 6, 5);
exred = getbits(&page[0], 11, 13);
eyred = getbits(&page[0], 24, 13);
deltai0red = getbits(&page[0], 37, 17);
omega0red = getbits(&page[0], 54, 23);
lambda0red = getbits(&page[0], 77, 23);
af0red = getbits(&page[0], 100, 22);
af1red = getbits(&page[0], 122, 6);
}
// reed-solomon data
void parseRS(const std::vector<uint8_t>& page)
{
// see 5.1.13.2 of the Galileo SIS ICD 2.0
rs2bitiod = getbitu(&page[0], 6+8, 2);
rsparity.clear();
rsparity.append(1, getbitu(&page[0], 6, 8)); // first octet is different
for(int n = 0; n < 14; ++n)
rsparity.append(1, getbitu(&page[0], 16+n*8, 8));
}
double getMu() const double getMu() const
{ {
return 3.986004418 * pow(10.0, 14.0); return 3.986004418 * pow(10.0, 14.0);
@ -441,53 +392,6 @@ struct GalileoMessage : GPSLikeEphemeris
double getOmega0() const { return ldexp(omega0 * M_PI, -31); } // radians double getOmega0() const { return ldexp(omega0 * M_PI, -31); } // radians
double getIdot() const { return ldexp(idot * M_PI, -43); } // radians/s double getIdot() const { return ldexp(idot * M_PI, -43); } // radians/s
double getOmega() const { return ldexp(omega * M_PI, -31); } // radians double getOmega() const { return ldexp(omega * M_PI, -31); } // radians
};
struct REDCEDAdaptor
{
REDCEDAdaptor(const GalileoMessage& gm, int32_t t0r) : d_gm(gm), d_t0r(t0r)
{}
double getMu() const
{
return 3.986004418 * pow(10.0, 14.0);
} // m^3/s^2
// same for galileo & gps
double getOmegaE() const { return 7.2921151467 * pow(10.0, -5.0);} // rad/s
uint32_t getT0e() const { return d_t0r; }
static constexpr double Anominal{29600000.0};
double getSqrtA() const { return sqrt(Anominal + ldexp(1.0*d_gm.deltaAred, 8)); }
double getE() const { return ldexp(sqrt(1.0*d_gm.exred*d_gm.exred + 1.0*d_gm.eyred*d_gm.eyred), -22); }
double getCuc() const { return 0; } // radians
double getCus() const { return 0; } // radians
double getCrc() const { return 0; } // meters
double getCrs() const { return 0; } // meters
double getM0() const { return ldexp(M_PI * d_gm.lambda0red, -22) - getOmega(); } // lambda0red - omega, both radians
double getDeltan()const { return 0; } //radians/s
static constexpr double iNominal{56.0};
double getI0() const { return M_PI*iNominal/180.0 + ldexp(d_gm.deltai0red * M_PI, -22); } // radians
double getCic() const { return 0; } // radians
double getCis() const { return 0; } // radians
double getOmegadot() const { return 0; } // radians/s
double getOmega0() const { return ldexp(d_gm.omega0red * M_PI, -22); } // radians
double getIdot() const { return 0; } // radians/s
double getOmega() const { return atan2(d_gm.eyred, d_gm.exred); } // radians
// pair of nanosecond, nanosecond/s
std::pair<double, double> getAtomicOffset(int tow) const
{
int delta = ephAge(tow, d_t0r);
// 2^-26 2^-35
double cur = d_gm.af0red + ldexp(1.0*delta*d_gm.af1red, -9);
double trend = ldexp(d_gm.af1red, -9);
// now in units of 2^-26 seconds, which are ~14.9 nanoseconds each
double factor = ldexp(1000000000.0, -26);
return {factor * cur, factor * trend};
}
const GalileoMessage d_gm;
int32_t d_t0r;
}; };

View file

@ -1,4 +1,4 @@
#include <optional>
#include "minicurl.hh" #include "minicurl.hh"
#include <iostream> #include <iostream>
#include "navmon.hh" #include "navmon.hh"
@ -45,6 +45,7 @@ public:
std::optional<string> reportState(string_view thing, string_view name, var_t state, const std::string& state_text=""); 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> getState(string_view thing, string_view name);
std::optional<string> getPrevState(string_view thing, string_view name); std::optional<string> getPrevState(string_view thing, string_view name);
struct State struct State
@ -165,7 +166,23 @@ std::optional<string> StateKeeper::reportState(string_view thing, string_view na
StateKeeper g_sk; 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) void sendTweet(const string& tweet)
{ {
string etweet = tweet; string etweet = tweet;
@ -180,8 +197,8 @@ int main(int argc, char **argv)
{ {
MiniCurl mc; MiniCurl mc;
MiniCurl::MiniCurlHeaders mch; MiniCurl::MiniCurlHeaders mch;
string url="https://galmon.eu/"; // string url="https://galmon.eu/svs.json";
// string url="http://[::1]:29599/"; string url="http://[::1]:29599/";
bool doVERSION{false}; bool doVERSION{false};
CLI::App app(program); CLI::App app(program);
@ -205,7 +222,6 @@ int main(int argc, char **argv)
g_sk.setBoolNames("health", "healthy", "unhealthy"); g_sk.setBoolNames("health", "healthy", "unhealthy");
g_sk.setBoolNames("eph-too-old", "ephemeris fresh", "ephemeris aged"); g_sk.setBoolNames("eph-too-old", "ephemeris fresh", "ephemeris aged");
g_sk.setBoolNames("silent", "observed", "not observed"); g_sk.setBoolNames("silent", "observed", "not observed");
g_sk.setBoolNames("osnma", "OFF", "ON");
std::variant<bool, string> tst; std::variant<bool, string> tst;
@ -258,7 +274,6 @@ int main(int argc, char **argv)
res = mc.getURL(url+"svs.json"); res = mc.getURL(url+"svs.json");
j = nlohmann::json::parse(res); j = nlohmann::json::parse(res);
bool first=true; bool first=true;
bool globalOsnma=false;
for(const auto& sv : j) { for(const auto& sv : j) {
if(!sv.count("gnssid") || !sv.count("fullName") || !sv.count("sigid")) { if(!sv.count("gnssid") || !sv.count("fullName") || !sv.count("sigid")) {
cout<<"Skipping "<< sv.count("gnssid") <<", "<< sv.count("fullName") <<", " <<sv.count("sigid") <<endl; cout<<"Skipping "<< sv.count("gnssid") <<", "<< sv.count("fullName") <<", " <<sv.count("sigid") <<endl;
@ -268,9 +283,9 @@ int main(int argc, char **argv)
string fullName = sv["fullName"]; string fullName = sv["fullName"];
if(!(gnssid == 2 && sigid==1) && if(!(gnssid == 2 && sigid==1) &&
!(gnssid == 0 && sigid==0) /* && !(gnssid == 0 && sigid==0) &&
!(gnssid == 3 && sigid==0) && !(gnssid == 3 && sigid==0) &&
!(gnssid == 6 && sigid==0) */) !(gnssid == 6 && sigid==0))
continue; continue;
int numfresh=0; int numfresh=0;
@ -281,7 +296,6 @@ int main(int argc, char **argv)
continue; continue;
} }
for(const auto& recv : sv["perrecv"]) { for(const auto& recv : sv["perrecv"]) {
if(!recv.count("last-seen-s")) { if(!recv.count("last-seen-s")) {
cout<<"Missing last-seen-s"<<endl; cout<<"Missing last-seen-s"<<endl;
@ -291,10 +305,8 @@ int main(int argc, char **argv)
numfresh++; numfresh++;
if((int)recv["last-seen-s"] < 3600) if((int)recv["last-seen-s"] < 3600)
notseen=false; notseen=false;
}
if(sv.count("osnma") && sv["osnma"]==true)
globalOsnma |= 1;
}
auto healthchange = g_sk.reportState(fullName, "health", sv["healthissue"]!=0); auto healthchange = g_sk.reportState(fullName, "health", sv["healthissue"]!=0);
std::optional<string> tooOldChange; std::optional<string> tooOldChange;
@ -398,17 +410,6 @@ int main(int argc, char **argv)
cout<<humanTimeNow() <<" " << tweet << endl; 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<<".";
cout.flush(); cout.flush();
} }

View file

@ -3,7 +3,6 @@
#include <string.h> #include <string.h>
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include "navmon.hh"
using std::cout; using std::cout;
using std::endl; using std::endl;
@ -13,7 +12,7 @@ static const double J2 = 1082625.75E-9; // IERS: 1.0826359
static const double oe = 7.2921151467E-5; // rad/s // IERS: 7.292115 static const double oe = 7.2921151467E-5; // rad/s // IERS: 7.292115
// this strips out spare bits + parity, and leaves 10 clean 24 bit words // this strips out spare bits + parity, and leaves 10 clean 24 bit words
std::vector<uint8_t> getGlonassMessage(const std::vector<uint8_t>& payload) std::basic_string<uint8_t> getGlonassMessage(std::basic_string_view<uint8_t> payload)
{ {
uint8_t buffer[4*4]; uint8_t buffer[4*4];
@ -21,7 +20,7 @@ std::vector<uint8_t> getGlonassMessage(const std::vector<uint8_t>& payload)
setbitu(buffer, 32*w, 32, getbitu(&payload[0], w*32, 32)); setbitu(buffer, 32*w, 32, getbitu(&payload[0], w*32, 32));
} }
return makeVec(buffer, 16); return std::basic_string<uint8_t>(buffer, 16);
} }
@ -111,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; return std::chrono::duration_cast<std::chrono::microseconds>(now - then).count()/1000.0;
} }
#if 0
static double passedMsec(const Clock::time_point& then) static double passedMsec(const Clock::time_point& then)
{ {
return passedMsec(then, Clock::now()); return passedMsec(then, Clock::now());
} }
#endif
double getCoordinates(double tow, const GlonassMessage& eph, Point* p) 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), 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)}; ldexp(eph.dx, -20), ldexp(eph.dy, -20), ldexp(eph.dz, -20)};
@ -138,7 +137,7 @@ double getCoordinates(double tow, const GlonassMessage& eph, Point* p)
rk4step (A, y0, h); rk4step (A, y0, h);
*p = Point (1E3*y0[0], 1E3*y0[1], 1E3*y0 [2]); *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; // cout<<"Took: "<<(total+=passedMsec(start))<<" ms" <<endl;
return 0; return 0;
} }

View file

@ -4,17 +4,14 @@
#include "bits.hh" #include "bits.hh"
#include <iostream> #include <iostream>
#include <math.h> #include <math.h>
#include <stdint.h>
#include <vector>
#include "minivec.hh" #include "minivec.hh"
std::vector<uint8_t> getGlonassessage(const std::vector<uint8_t>& payload); std::basic_string<uint8_t> getGlonassessage(std::basic_string_view<uint8_t> payload);
struct GlonassMessage struct GlonassMessage
{ {
uint8_t strtype; uint8_t strtype;
int parse(const std::vector<uint8_t>& gstr) int parse(std::basic_string_view<uint8_t> gstr)
{ {
strtype = getbitu(&gstr[0], 1, 4); strtype = getbitu(&gstr[0], 1, 4);
if(strtype == 1) { if(strtype == 1) {
@ -62,7 +59,7 @@ struct GlonassMessage
double getRadius() { return sqrt(getX()*getX() + getY()*getY() + getZ()*getZ()); } double getRadius() { return sqrt(getX()*getX() + getY()*getY() + getZ()*getZ()); }
void parse1(const std::vector<uint8_t>& gstr) void parse1(std::basic_string_view<uint8_t> gstr)
{ {
hour = getbitu(&gstr[0], 9, 5); hour = getbitu(&gstr[0], 9, 5);
minute = getbitu(&gstr[0], 14, 6); minute = getbitu(&gstr[0], 14, 6);
@ -80,7 +77,7 @@ struct GlonassMessage
An interval is 15 minutes long, plus a spacer of length described by P1. If P1 is zero, there is no spacer. An interval is 15 minutes long, plus a spacer of length described by P1. If P1 is zero, there is no spacer.
*/ */
void parse2(const std::vector<uint8_t>& gstr) void parse2(std::basic_string_view<uint8_t> gstr)
{ {
Bn = getbitu(&gstr[0], 85-80, 3); // Health bit, only look at MSB, ignore the rest. 0 is ok. Bn = getbitu(&gstr[0], 85-80, 3); // Health bit, only look at MSB, ignore the rest. 0 is ok.
Tb = getbitu(&gstr[0], 85-76, 7); Tb = getbitu(&gstr[0], 85-76, 7);
@ -95,7 +92,7 @@ struct GlonassMessage
bool l_n; bool l_n;
bool P, P3; bool P, P3;
uint16_t gamman; uint16_t gamman;
void parse3(const std::vector<uint8_t>& gstr) void parse3(std::basic_string_view<uint8_t> gstr)
{ {
z = getbitsglonass(&gstr[0], 85-35, 27); // 2^-11 z = getbitsglonass(&gstr[0], 85-35, 27); // 2^-11
dz = getbitsglonass(&gstr[0], 85-64, 24); // 2^-20 dz = getbitsglonass(&gstr[0], 85-64, 24); // 2^-20
@ -123,7 +120,7 @@ struct GlonassMessage
return 1000*ldexp(1000000.0*taun, -30); return 1000*ldexp(1000000.0*taun, -30);
} }
void parse4(const std::vector<uint8_t>& gstr) void parse4(std::basic_string_view<uint8_t> gstr)
{ {
NT = getbitu(&gstr[0], 85-26, 11); NT = getbitu(&gstr[0], 85-26, 11);
FT = getbitu(&gstr[0], 85-33, 4); FT = getbitu(&gstr[0], 85-33, 4);
@ -159,7 +156,7 @@ struct GlonassMessage
int32_t taugps; int32_t taugps;
int32_t tauc; int32_t tauc;
void parse5(const std::vector<uint8_t>& gstr) void parse5(std::basic_string_view<uint8_t> gstr)
{ {
n4=getbitu(&gstr[0], 85-36, 5); n4=getbitu(&gstr[0], 85-36, 5);
taugps = getbitsglonass(&gstr[0], 85-31, 22); taugps = getbitsglonass(&gstr[0], 85-31, 22);
@ -177,7 +174,7 @@ struct GlonassMessage
return ldexp(tlambdana, -5); return ldexp(tlambdana, -5);
} }
void parse7_9_11_13_15(const std::vector<uint8_t>& gstr) void parse7_9_11_13_15(std::basic_string_view<uint8_t> gstr)
{ {
l_n = getbitu(&gstr[0], 85 - 9, 1); l_n = getbitu(&gstr[0], 85 - 9, 1);
omegana = getbitsglonass(&gstr[0], 85-80, 16); omegana = getbitsglonass(&gstr[0], 85-80, 16);
@ -209,7 +206,7 @@ struct GlonassMessage
return M_PI*63.0/180 + ldexp(M_PI* deltaina, -20); return M_PI*63.0/180 + ldexp(M_PI* deltaina, -20);
} }
void parse6_8_10_12_14(const std::vector<uint8_t>& gstr) void parse6_8_10_12_14(std::basic_string_view<uint8_t> gstr)
{ {
CnA = getbitu(&gstr[0], 85-80, 1); CnA = getbitu(&gstr[0], 85-80, 1);
nA = getbitu(&gstr[0], 85-77, 5); nA = getbitu(&gstr[0], 85-77, 5);

View file

@ -7,21 +7,16 @@ extern const char* g_gitHash;
using namespace std; using namespace std;
int main(int argc, char** argv) int main(int argc, char** argv)
try
{ {
string program("gndate"); string program("gndate");
CLI::App app(program); CLI::App app(program);
string date; string date;
int galwn{-1}; bool doGPSWN{false}, doGALWN{false}, doVERSION{false}, doUTC{false};
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_flag("--version", doVERSION, "show program version and copyright");
app.add_option("--date,-d", date, "yyyy-mm-dd hh:mm[:ss] hh:mm yyyymmdd hhmm"); app.add_option("--date,-d", date, "yyyy-mm-dd hh:mm");
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("--utc,-u", doUTC, "Interpret --date,-d as UTC");
app.add_flag("--gps-wn", doGPSWN, "Print GPS week number"); app.add_flag("--gps-wn", doGPSWN, "Print GPS week number");
app.add_flag("--gal-wn", doGALWN, "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 { try {
app.parse(argc, argv); app.parse(argc, argv);
} catch(const CLI::Error &e) { } catch(const CLI::Error &e) {
@ -45,15 +40,6 @@ try
int wn, tow; 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) { if(doGPSWN) {
getGPSDateFromUTC(now, wn, tow); getGPSDateFromUTC(now, wn, tow);
cout<<wn<<endl; cout<<wn<<endl;
@ -62,20 +48,10 @@ try
getGalDateFromUTC(now, wn, tow); getGalDateFromUTC(now, wn, tow);
cout<<wn<<endl; cout<<wn<<endl;
} }
else if(doBEIDOUWN) {
getBeiDouDateFromUTC(now, wn, tow);
cout<<wn<<endl;
}
else { else {
getGPSDateFromUTC(now, wn, tow); getGPSDateFromUTC(now, wn, tow);
cout<<"GPS Week Number (non-wrapped): "<< wn << ", tow " << tow << endl; cout<<"GPS Week Number (non-wrapped): "<< wn << ", tow " << tow << endl;
getGalDateFromUTC(now, wn, tow); getGalDateFromUTC(now, wn, tow);
cout<<"Galileo Week Number: "<< wn << ", tow " << tow << endl; 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;
}

14
gps.cc
View file

@ -1,20 +1,20 @@
#include "gps.hh" #include "gps.hh"
// this strips out spare bits + parity, and leaves 10 clean 24 bit words // this strips out spare bits + parity, and leaves 10 clean 24 bit words
std::vector<uint8_t> getCondensedGPSMessage(const std::vector<uint8_t>& payload) std::basic_string<uint8_t> getCondensedGPSMessage(std::basic_string_view<uint8_t> payload)
{ {
uint8_t buffer[10*24/8]; uint8_t buffer[10*24/8];
// ingests 32 bit words, per word ignores first 2 bits, and then takes 24
for(int w = 0 ; w < 10; ++w) { for(int w = 0 ; w < 10; ++w) {
setbitu(buffer, 24*w, 24, getbitu(&payload[0], 2 + w*32, 24)); setbitu(buffer, 24*w, 24, getbitu(&payload[0], 2 + w*32, 24));
} }
return std::vector<uint8_t>(buffer, buffer+30); return std::basic_string<uint8_t>(buffer, 30);
} }
// expects input as 24 bit read to to use messages, returns frame number // expects input as 24 bit read to to use messages, returns frame number
int GPSState::parseGPSMessage(const std::vector<uint8_t>& cond, uint8_t* pageptr) int GPSState::parseGPSMessage(std::basic_string_view<uint8_t> cond, uint8_t* pageptr)
{ {
using namespace std; using namespace std;
int frame = getbitu(&cond[0], 24+19, 3); int frame = getbitu(&cond[0], 24+19, 3);
@ -38,8 +38,6 @@ int GPSState::parseGPSMessage(const std::vector<uint8_t>& cond, uint8_t* pageptr
wn = 2048 + getbitu(&cond[0], 2*24, 10); wn = 2048 + getbitu(&cond[0], 2*24, 10);
ura = getbitu(&cond[0], 2*24+12, 4); ura = getbitu(&cond[0], 2*24+12, 4);
gpshealth = getbitu(&cond[0], 2*24+16, 6); gpshealth = getbitu(&cond[0], 2*24+16, 6);
iodc = getbitu(&cond[0], 2*24 +22, 2) * 256;
iodc += getbitu(&cond[0], 7*24, 8);
// cerr<<"GPS Week Number: "<< wn <<", URA: "<< (int)ura<<", health: "<< // cerr<<"GPS Week Number: "<< wn <<", URA: "<< (int)ura<<", health: "<<
// (int)gpshealth <<endl; // (int)gpshealth <<endl;
@ -98,13 +96,9 @@ int GPSState::parseGPSMessage(const std::vector<uint8_t>& cond, uint8_t* pageptr
t0t = getbitu(&cond[0], 7*24 + 8, 8) * 4096; // WE SCALE THIS FOR THE USER! t0t = getbitu(&cond[0], 7*24 + 8, 8) * 4096; // WE SCALE THIS FOR THE USER!
wn0t = getbitu(&cond[0], 7*24 + 16, 8); wn0t = getbitu(&cond[0], 7*24 + 16, 8);
dtLS = getbits(&cond[0], 8*24, 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); 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<<": a0: "<<a0<<", a1: "<<a1<<", t0t: "<< t0t * (1<<12) <<", wn0t: "<< wn0t<<", rough offset: "<<ldexp(a0, -30)<<endl;
// cerr<<"deltaTLS: "<< (int)dtLS<<", post "<< (int)dtLSF<<endl; // cerr<<"deltaTLS: "<< (int)dtLS<<", post "<< (int)dtLSF<<endl;
return frame; // otherwise pageptr gets overwritten below return frame; // otherwise pageptr gets overwritten below

7
gps.hh
View file

@ -6,9 +6,8 @@
#include <iostream> #include <iostream>
#include <math.h> #include <math.h>
#include "ephemeris.hh" #include "ephemeris.hh"
#include <vector>
std::vector<uint8_t> getCondensedGPSMessage(const std::vector<uint8_t>& payload); std::basic_string<uint8_t> getCondensedGPSMessage(std::basic_string_view<uint8_t> payload);
struct GPSAlmanac : GPSLikeEphemeris struct GPSAlmanac : GPSLikeEphemeris
@ -134,17 +133,15 @@ struct GPSState : GPSLikeEphemeris
uint16_t wnLSF{0}; uint16_t wnLSF{0};
uint8_t dn; // leap second day number uint8_t dn; // leap second day number
// 1 2^-31 2^-43 2^-55 16 second // 1 2^-31 2^-43 2^-55 16 second
int iodc;
int ura; int ura;
int gpsiod{-1}; int gpsiod{-1};
int getIOD() const int getIOD() const
{ {
return gpsiod; return gpsiod;
} }
int parseGPSMessage(const std::vector<uint8_t>& cond, uint8_t* pageptr=0); int parseGPSMessage(std::basic_string_view<uint8_t> cond, uint8_t* pageptr=0);
}; };
template<typename T> template<typename T>

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <bitset> #include <bitset>
#include <string> #include <string>
#include <vector>
#include <map> #include <map>
#include "bits.hh" #include "bits.hh"
#include <iostream> #include <iostream>
@ -186,7 +185,7 @@ std::pair<double, double> getGPSCNavUTCOffset(int tow, int wn, const T& eph)
template<typename T> template<typename T>
int parseGPSCNavMessage(const std::vector<uint8_t>& msg, T& out) int parseGPSCNavMessage(std::basic_string_view<uint8_t> msg, T& out)
{ {
using namespace std; using namespace std;
int type = getbitu(&msg[0], 14, 6); int type = getbitu(&msg[0], 14, 6);

View file

@ -14,7 +14,7 @@ function maketable(str, arr)
enter(). enter().
append("tr"); append("tr");
var columns = ["sv", "iod", "eph-age-m", "orbit-disco", "time-disco", "sisa", "health", "alma-dist", "osnma", "impinav", "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", "delta-utc", "sources", "db", "rtcm-eph-delta-cm","rtcm-clock-dclock0", "prres", "elev", "last-seen-s"];
// append the header row // append the header row
thead.append("tr") thead.append("tr")
@ -61,7 +61,7 @@ function maketable(str, arr)
else if(row["gnssid"] == 6) else if(row["gnssid"] == 6)
img='ext/glo.png'; img='ext/glo.png';
ret.value = '<img width="16" height="16" src="https://berthub.eu/tmp/'+ img +'"/>'; ret.value = '<img width="16" height="16" src="https://ds9a.nl/tmp/'+ img +'"/>';
// ret.value=""; // ret.value="";
ret.value += "&nbsp;<a href='sv.html?gnssid="+row.gnssid+"&sv="+row.svid+"&sigid="+row.sigid+"'>"+row.sv+"</a>"; ret.value += "&nbsp;<a href='sv.html?gnssid="+row.gnssid+"&sv="+row.svid+"&sigid="+row.sigid+"'>"+row.sv+"</a>";
} }
@ -130,19 +130,6 @@ function maketable(str, arr)
else if(column == "norad") { else if(column == "norad") {
ret.value = row["best-tle-norad"]; ret.value = row["best-tle-norad"];
} }
else if(column == "osnma" && row["osnma"] != null) {
if(row["osnma"]==true)
ret.value="✅";
else
ret.value="";
}
else if(column == "impinav" && row["impinav"] != null) {
if(row["impinav"]==true)
ret.value="✅";
else
ret.value="";
}
else if(column == "delta-utc" && row["delta-utc"] != null) { 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.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'; ret.Class = 'CellWithComment';
@ -173,7 +160,7 @@ function maketable(str, arr)
ret.color="red"; ret.color="red";
} }
} }
else if(column == "orbit-disco" && row[column] != null) else if(column == "latest-disco" && row[column] != null)
ret.value = ((100*row[column]).toFixed(1))+" cm"; ret.value = ((100*row[column]).toFixed(1))+" cm";
else if(column == "time-disco" && row[column] != null) else if(column == "time-disco" && row[column] != null)
ret.value = row[column].toFixed(1)+" ns"; ret.value = row[column].toFixed(1)+" ns";
@ -301,8 +288,6 @@ function updateSats()
let wantIt = false; let wantIt = false;
if(d3.select("#GalE1").property("checked") && arr[n].gnssid==2 && arr[n].sigid == 1) if(d3.select("#GalE1").property("checked") && arr[n].gnssid==2 && arr[n].sigid == 1)
wantIt = true; 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) if(d3.select("#GalE5b").property("checked") && arr[n].gnssid==2 && arr[n].sigid == 5)
wantIt = true; wantIt = true;
if(d3.select("#GPSL1CA").property("checked") && arr[n].gnssid==0 && arr[n].sigid == 0) if(d3.select("#GPSL1CA").property("checked") && arr[n].gnssid==0 && arr[n].sigid == 0)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 B

View file

@ -4,14 +4,12 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>galmon.eu</title> <title>galmon.eu</title>
<link rel="stylesheet" type="text/css" href="style.css"> <link rel="stylesheet" type="text/css" href="style.css">
</head> </head>
<body> <body>
Last update: <span id="freshness"></span>. More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found <a href="https://berthub.eu/articles/posts/galmon-project/">here</a>. Live observer map <a href="geo">here</a>, status (coverage, DOP) map <a href="geo/coverage.html">here</a>. <b>Experimental Grafana dashboard on <a href="https://public.galmon.eu/">public.galmon.eu</a> (user: guest, password: guest)</b>. SBAS <a href="sbas.html">status</a>, <a href="sbstatus.html">per-satellite</a>. <span id="allstats"></span><br/> Last update: <span id="freshness"></span>. More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found <a href="https://berthub.eu/articles/posts/galmon-project/">here</a>. Live observer map <a href="geo">here</a>, status (coverage, DOP) map <a href="geo/coverage.html">here</a>. <b>Experimental Grafana dashboard on <a href="https://public.galmon.eu/">public.galmon.eu</a> (user: guest, password: guest)</b>. SBAS <a href="sbas.html">status</a>, <a href="sbstatus.html">per-satellite</a>. <span id="allstats"></span><br/>
<div class="centered"> <div class="centered">
<hr/> <hr/>
<input type="checkbox" id="GalE1" onclick="updateSats();"> <label for="GalE1">Galileo E1</label> &nbsp;&nbsp; <input type="checkbox" id="GalE1" onclick="updateSats();"> <label for="GalE1">Galileo E1</label> &nbsp;&nbsp;
<input type="checkbox" id="GalE5a" onclick="updateSats();"> <label for="GalE5a">Galileo E5a</label> &nbsp;&nbsp;
<input type="checkbox" id="GalE5b" onclick="updateSats();"> <label for="GalE5b">Galileo E5b</label> &nbsp;&nbsp; <input type="checkbox" id="GalE5b" onclick="updateSats();"> <label for="GalE5b">Galileo E5b</label> &nbsp;&nbsp;
<input type="checkbox" id="BeiDouB1I" onclick="updateSats();"> <label for="BeiDouB1I">BeiDou B1I</label> &nbsp;&nbsp; <input type="checkbox" id="BeiDouB1I" onclick="updateSats();"> <label for="BeiDouB1I">BeiDou B1I</label> &nbsp;&nbsp;
<input type="checkbox" id="BeiDouB2I" onclick="updateSats();"> <label for="BeiDouB2I">BeiDou B2I</label> &nbsp;&nbsp; <input type="checkbox" id="BeiDouB2I" onclick="updateSats();"> <label for="BeiDouB2I">BeiDou B2I</label> &nbsp;&nbsp;
@ -21,7 +19,6 @@
<input type="checkbox" id="GPSL2C" onclick="updateSats();"> <label for="GPSL2C">GPS L2C</label> <input type="checkbox" id="GPSL2C" onclick="updateSats();"> <label for="GPSL2C">GPS L2C</label>
</div> </div>
<hr/> <hr/>
<a rel="me" href="https://bot.country/@GNSS_Changes">Mastodon</a>
<table id="svs"></table> <table id="svs"></table>
<hr> <hr>
<p> <p>
@ -34,10 +31,8 @@
<a href="https://github.com/berthubert/galmon">GitHub</a>. <a href="https://github.com/berthubert/galmon">GitHub</a>.
</p> </p>
<p> <p>
Join us on our IRC channel (chat) via the 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>. <a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.
There is also a LIVE <a href="https://bot.country/@GNSS_Changes">bot on
Mastodon</a> that posts updates on relevant satellite changes.
</p> </p>
<p> <p>
The meaning of the fields is explained in <a href="https://berthub.eu/articles/posts/gps-gnss-how-do-they-work/">this document</a> and can be summarised as follows: The meaning of the fields is explained in <a href="https://berthub.eu/articles/posts/gps-gnss-how-do-they-work/">this document</a> and can be summarised as follows:
@ -62,20 +57,19 @@
<tr><td>lastseens</td><td>Time since we've last received from this SV.</td></tr> <tr><td>lastseens</td><td>Time since we've last received from this SV.</td></tr>
</table> </table>
<p> <p>
The official Galileo constellation status can be found on the <a href="https://www.gsc-europa.eu/system-service-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>. 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>
<p> <p>
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. 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.
</p> </p>
<p> <p>
Status updates on GPS can be found on <a 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.
href="https://www.navcen.uscg.gov/gps-constellation">this page</a>.
</p> </p>
<p> <p>
Information on the status of BeiDou can be found on <a href="http://www.csno-tarc.cn/system/constellation&amp;ce=english">this page</a> from the Chinese Test and Assessment Research Center of China Satellite Navigation Office. Information on the status of BeiDou can be found on <a href="http://www.csno-tarc.cn/system/constellation&amp;ce=english">this page</a> from the Chinese Test and Assessment Research Center of China Satellite Navigation Office.
</p> </p>
<p> <p>
Feedback is very welcome on bert@hubertnet.nl or <a href="https://twitter.com/bert_hu_bert">@bert_hu_bert</a>. Feedback is very welcome on bert@hubertnet.nl or <a href="https://twitter.com/PowerDNS_Bert">@PowerDNS_Bert</a>.
</p> </p>
<script src="d3.v4.min.js"></script> <script src="d3.v4.min.js"></script>
<script src="ext/moment-with-locales.js"></script> <script src="ext/moment-with-locales.js"></script>

View file

@ -6,7 +6,7 @@
<link rel="stylesheet" type="text/css" href="style.css"> <link rel="stylesheet" type="text/css" href="style.css">
</head> </head>
<body> <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://berthub.eu/">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://ds9a.nl/">me</a> if you want access to the Grafana dashboard.<br/>
<table> <table>
<tr style="background: #FFF"> <tr style="background: #FFF">
<td style="vertical-align:top"> <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 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>. <a href="https://github.com/ahuPowerDNS/galmon">GitHub</a>.
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>. Some technical detail behind this setup can be found in <a href="https://ds9a.nl/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 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>. <a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.

View file

@ -85,6 +85,7 @@ function componentDidMount() {
.text(function(d) { return d + "°"; }); .text(function(d) { return d + "°"; });
sats.select('g.satellites').remove(); sats.select('g.satellites').remove();
console.log(gnss_position);
let points = sats let points = sats
.insert("g") .insert("g")
@ -258,6 +259,7 @@ function update()
} }
console.log(window.location.href);
var url = new URL(window.location.href); var url = new URL(window.location.href);
observer = url.searchParams.get("observer"); observer = url.searchParams.get("observer");

View file

@ -6,14 +6,14 @@
<link rel="stylesheet" type="text/css" href="style.css"> <link rel="stylesheet" type="text/css" href="style.css">
</head> </head>
<body> <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://berthub.eu/">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://ds9a.nl/">me</a> if you want access to the Grafana dashboard.<br/>
<table id="galileo"></table> <table id="galileo"></table>
<p> <p>
This table shows live output from four Galileo/GPS/BeiDou/GLONASS receivers hosted in Nootdorp, The Netherlands and California, United States. 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 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>. <a href="https://github.com/ahuPowerDNS/galmon">GitHub</a>.
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>. Some technical detail behind this setup can be found in <a href="https://ds9a.nl/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 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>. <a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.

View file

@ -20,7 +20,7 @@ function makeTable(str, arr)
enter(). enter().
append("tr"); append("tr");
var columns= ["id", "last-seen", "latitude", "longitude", "owner", "remark", "serialno", "hwversion", "swversion", "impinav", "mods", "githash", "uptime", "clockdriftns", "clockacc", "freqacc", "h", "acc", "satellites"]; var columns= ["id", "last-seen", "latitude", "longitude", "owner", "remark", "serialno", "hwversion", "swversion", "mods", "githash", "uptime", "clockdriftns", "clockacc", "freqacc", "h", "acc", "satellites"];
// append the header row // append the header row
thead.append("tr") thead.append("tr")
@ -41,12 +41,6 @@ function makeTable(str, arr)
if(column == "id") { if(column == "id") {
ret.value='<a href="observer.html?observer='+row[column]+'">'+row[column]+"</a>"; ret.value='<a href="observer.html?observer='+row[column]+'">'+row[column]+"</a>";
} }
else if(column == "impinav") {
if(row["impinav"]==true)
ret.value="✅";
else
ret.value="";
}
else if(column == "last-seen") { else if(column == "last-seen") {
ret.value = moment(1000*row["last-seen"]).fromNow(); ret.value = moment(1000*row["last-seen"]).fromNow();
let lastSeen = moment(1000*row["last-seen"]); let lastSeen = moment(1000*row["last-seen"]);

View file

@ -6,14 +6,14 @@
<link rel="stylesheet" type="text/css" href="style.css"> <link rel="stylesheet" type="text/css" href="style.css">
</head> </head>
<body> <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://berthub.eu/">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://ds9a.nl/">me</a> if you want access to the Grafana dashboard.<br/>
<table id="galileo"></table> <table id="galileo"></table>
<p> <p>
This table shows live output from four Galileo/GPS/BeiDou/GLONASS receivers hosted in Nootdorp, The Netherlands and California, United States. 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 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>. <a href="https://github.com/ahuPowerDNS/galmon">GitHub</a>.
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>. Some technical detail behind this setup can be found in <a href="https://ds9a.nl/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 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>. <a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.

View file

@ -18,7 +18,7 @@
<table id="sbasstale"></table> <table id="sbasstale"></table>
<p> <p>
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>. Some technical detail behind this setup can be found in <a href="https://ds9a.nl/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 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>. <a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.

View file

@ -75,7 +75,7 @@ function maketable(str, arr)
ret.value = sbas + "&nbsp;"; ret.value = sbas + "&nbsp;";
if(img != "") if(img != "")
ret.value += '<img width="16" height="16" src="https://berthub.eu/tmp/'+ img +'"/>'; ret.value += '<img width="16" height="16" src="https://ds9a.nl/tmp/'+ img +'"/>';
else else
ret.value += ""; ret.value += "";
// ret.value=""; // ret.value="";

View file

@ -18,7 +18,7 @@
<table id="sbasstale"></table> <table id="sbasstale"></table>
<p> <p>
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>. Some technical detail behind this setup can be found in <a href="https://ds9a.nl/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 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>. <a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.

View file

@ -6,14 +6,14 @@
<link rel="stylesheet" type="text/css" href="style.css"> <link rel="stylesheet" type="text/css" href="style.css">
</head> </head>
<body> <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://berthub.eu/">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://ds9a.nl/">me</a> if you want access to the Grafana dashboard.<br/>
<table id="galileo"></table> <table id="galileo"></table>
<p> <p>
This table shows live output from four Galileo/GPS/BeiDou/GLONASS receivers hosted in Nootdorp, The Netherlands and California, United States. 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 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>. <a href="https://github.com/ahuPowerDNS/galmon">GitHub</a>.
Some technical detail behind this setup can be found in <a href="https://berthub.eu/articles/posts/galileo-notes/">this post</a>. Some technical detail behind this setup can be found in <a href="https://ds9a.nl/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 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>. <a href="https://webchat.oftc.net/?channels=galileo">web gateway</a>.

View file

@ -131,37 +131,9 @@ Observer and SV measurements:
* ele: calculated elevation for SV from this receiver * ele: calculated elevation for SV from this receiver
* prres: pseudorange residual according to receiver * prres: pseudorange residual according to receiver
* qi: 0-7, quality indicator according to receiver * qi: 0-7, quality indicator according to receiver
* used: did the receiver use this SV?
* ubx\_jamming * ubx\_jamming
* noise\_per\_ms: the Ublox noisePerMS field * noise\_per\_ms: the Ublox noisePerMS field
* agccnt: the Ublox automatic gain correction "count" * agccnt: the Ublox automatic gain correction "count"
* jamind: The Ublox jamming indicator * jamind: The Ublox jamming indicator
* flag: The Ublox jamming flag field * 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

View file

@ -52,8 +52,7 @@ MiniCurl::MiniCurl(const string& useragent)
MiniCurl::~MiniCurl() MiniCurl::~MiniCurl()
{ {
if(d_host_list) // NEEDS TO CLEAN HOSTLIST
curl_slist_free_all(d_host_list);
curl_easy_cleanup(d_curl); curl_easy_cleanup(d_curl);
} }
@ -66,7 +65,7 @@ size_t MiniCurl::write_callback(char *ptr, size_t size, size_t nmemb, void *user
using namespace std; using namespace std;
string extractHostFromURL(const std::string& url) static string extractHostFromURL(const std::string& url)
{ {
auto pos = url.find("://"); auto pos = url.find("://");
if(pos == string::npos) if(pos == string::npos)
@ -82,10 +81,7 @@ string extractHostFromURL(const std::string& url)
void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src) void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src)
{ {
if(rem) { if(rem) {
if(d_host_list) { struct curl_slist *hostlist = nullptr; // THIS SHOULD BE FREED
curl_slist_free_all(d_host_list);
d_host_list = nullptr;
}
// url = http://hostname.enzo/url // url = http://hostname.enzo/url
string host4=extractHostFromURL(str); string host4=extractHostFromURL(str);
@ -101,76 +97,40 @@ void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const C
} }
for (const auto& port : ports) { for (const auto& port : ports) {
string hcode = fmt::sprintf("%s:%u:[%s]", host4 , port , rem->toString()); string hcode = fmt::format("%s:%u:%s", host4 , port , rem->toString());
// fmt::print("hcode: {}\n", hcode); hostlist = curl_slist_append(hostlist, hcode.c_str());
d_host_list = curl_slist_append(d_host_list, hcode.c_str());
} }
curl_easy_setopt(d_curl, CURLOPT_RESOLVE, d_host_list); curl_easy_setopt(d_curl, CURLOPT_RESOLVE, hostlist);
} }
// should be a setting if(src) {
curl_easy_setopt(d_curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(d_curl, CURLOPT_INTERFACE, src->toString().c_str());
}
curl_easy_setopt(d_curl, CURLOPT_FOLLOWLOCATION, true);
/* only allow HTTP and HTTPS */ /* only allow HTTP and HTTPS */
curl_easy_setopt(d_curl, CURLOPT_PROTOCOLS_STR, "http,https"); curl_easy_setopt(d_curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYPEER, true); curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYHOST, true); curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYHOST, false);
// curl_easy_setopt(d_curl, CURLOPT_FAILONERROR, true); // curl_easy_setopt(d_curl, CURLOPT_FAILONERROR, true);
curl_easy_setopt(d_curl, CURLOPT_URL, str.c_str()); curl_easy_setopt(d_curl, CURLOPT_URL, str.c_str());
curl_easy_setopt(d_curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(d_curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(d_curl, CURLOPT_WRITEDATA, this); curl_easy_setopt(d_curl, CURLOPT_WRITEDATA, this);
curl_easy_setopt(d_curl, CURLOPT_TIMEOUT, 10L); curl_easy_setopt(d_curl, CURLOPT_TIMEOUT, 10L);
curl_easy_setopt(d_curl, CURLOPT_CERTINFO, 1L);
curl_easy_setopt(d_curl, CURLOPT_FILETIME, 1L);
if(src) {
curl_easy_setopt(d_curl, CURLOPT_INTERFACE, src->toString().c_str());
// XXX report errors!!
// fmt::print("Setting interface to '{}', ret {}\n", src->toString().c_str(),
// ret);
}
clearHeaders(); clearHeaders();
d_data.clear(); d_data.clear();
} }
std::string MiniCurl::getURL(const std::string& str, const bool nobody, MiniCurl::certinfo_t* ciptr, const ComboAddress* rem, const ComboAddress* src) std::string MiniCurl::getURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src)
{ {
setupURL(str, rem, src); setupURL(str, rem, src);
if (nobody)
curl_easy_setopt(d_curl, CURLOPT_NOBODY, 1L);
auto res = curl_easy_perform(d_curl); auto res = curl_easy_perform(d_curl);
if(d_host_list) { long http_code = 0;
curl_slist_free_all(d_host_list); curl_easy_getinfo(d_curl, CURLINFO_RESPONSE_CODE, &http_code);
d_host_list = nullptr;
}
if(res != CURLE_OK) {
throw std::runtime_error("Unable to retrieve URL "+str+ " - "+string(curl_easy_strerror(res)));
}
d_filetime=-1; if(res != CURLE_OK || http_code != 200) {
curl_easy_getinfo(d_curl, CURLINFO_FILETIME, &d_filetime); throw std::runtime_error("Unable to retrieve URL ("+std::to_string(http_code)+"): "+string(curl_easy_strerror(res)));
if(ciptr) {
struct curl_certinfo *ci;
res = curl_easy_getinfo(d_curl, CURLINFO_CERTINFO, &ci);
if(res) {
throw std::runtime_error(fmt::format("URL: {}, Error: {}\n", str, curl_easy_strerror(res)));
} }
int i;
for(i = 0; i < ci->num_of_certs; i++) {
struct curl_slist *slist;
for(slist = ci->certinfo[i]; slist; slist = slist->next) {
string data = slist->data;
if(auto pos = data.find(':'); pos != string::npos)
(*ciptr)[i][data.substr(0, pos)] = data.substr(pos+1);
}
}
}
d_http_code = 0;
curl_easy_getinfo(d_curl, CURLINFO_RESPONSE_CODE, &d_http_code);
std::string ret=d_data; std::string ret=d_data;
d_data.clear(); d_data.clear();
return ret; return ret;

View file

@ -42,23 +42,16 @@ public:
MiniCurl(const string& useragent="MiniCurl/0.0"); MiniCurl(const string& useragent="MiniCurl/0.0");
~MiniCurl(); ~MiniCurl();
MiniCurl& operator=(const MiniCurl&) = delete; MiniCurl& operator=(const MiniCurl&) = delete;
typedef std::map<int, std::map<std::string, std::string>> certinfo_t; std::string getURL(const std::string& str, const ComboAddress* rem=0, const ComboAddress* src=0);
std::string getURL(const std::string& str, const bool nobody=0, certinfo_t* ciptr=0, const ComboAddress* rem=0, const ComboAddress* src=0);
std::string postURL(const std::string& str, const std::string& postdata, MiniCurlHeaders& headers); std::string postURL(const std::string& str, const std::string& postdata, MiniCurlHeaders& headers);
std::string urlEncode(std::string_view str); std::string urlEncode(std::string_view str);
CURL *d_curl;
time_t d_filetime=-1;
long d_http_code=-1;
private: private:
std::string d_data; CURL *d_curl;
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata); static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
std::string d_data;
struct curl_slist* d_header_list = nullptr; struct curl_slist* d_header_list = nullptr;
struct curl_slist *d_host_list = nullptr;
void setupURL(const std::string& str, const ComboAddress* rem=0, const ComboAddress* src=0); void setupURL(const std::string& str, const ComboAddress* rem=0, const ComboAddress* src=0);
void setHeaders(const MiniCurlHeaders& headers); void setHeaders(const MiniCurlHeaders& headers);
void clearHeaders(); void clearHeaders();
}; };
std::string extractHostFromURL(const std::string& url);

View file

@ -1,6 +1,5 @@
#pragma once #pragma once
#include <math.h> #include <math.h>
#include <iostream>
struct Point struct Point
{ {
@ -11,13 +10,6 @@ struct Point
double x, y, z; double x, y, z;
}; };
inline std::ostream& operator<<(std::ostream& os, const Point& p)
{
os << '(' << p.x << ", " << p.y << ", " << p.z <<')';
return os;
}
struct Vector struct Vector
{ {
Vector() : x{0}, y{0}, z{0} {} Vector() : x{0}, y{0}, z{0} {}
@ -47,9 +39,4 @@ struct Vector
} }
}; };
inline std::ostream& operator<<(std::ostream& os, const Vector& p)
{
os << '(' << p.x << ", " << p.y << ", " << p.z <<')';
return os;
}

108
navcat.cc
View file

@ -16,7 +16,7 @@
#include <dirent.h> #include <dirent.h>
#include <inttypes.h> #include <inttypes.h>
#include "navmon.hh" #include "navmon.hh"
// #include <execution>
#include "CLI/CLI.hpp" #include "CLI/CLI.hpp"
#include "version.hh" #include "version.hh"
@ -26,19 +26,18 @@ using namespace std;
extern const char* g_gitHash; extern const char* g_gitHash;
// get all stations (numerical) from a directory
static vector<uint64_t> getSources(string_view dirname) vector<uint64_t> getSources(string_view dirname)
{ {
shared_ptr<DIR> dir; DIR *dir = opendir(&dirname[0]);
if(auto ptr = opendir(&dirname[0])) if(!dir)
dir=shared_ptr<DIR>(ptr, closedir);
else
unixDie("Listing metrics from statistics storage "+(string)dirname); unixDie("Listing metrics from statistics storage "+(string)dirname);
struct dirent *result=0; struct dirent *result=0;
vector<uint64_t> ret; vector<uint64_t> ret;
for(;;) { for(;;) {
errno=0; errno=0;
if(!(result = readdir(dir.get()))) { if(!(result = readdir(dir))) {
closedir(dir);
if(errno) if(errno)
unixDie("Reading directory entry "+(string)dirname); unixDie("Reading directory entry "+(string)dirname);
else else
@ -55,77 +54,56 @@ static vector<uint64_t> getSources(string_view dirname)
return ret; return ret;
} }
static bool operator==(const timespec& a, const timespec& b)
{
return a.tv_sec == b.tv_sec && a.tv_nsec && b.tv_nsec;
}
// send protobuf data from the named directories and stations, between start and stoptime void sendProtobuf(string_view dir, vector<uint64_t> stations, time_t startTime, time_t stopTime=0)
void sendProtobuf(const vector<string>& dirs, vector<uint64_t> stations, time_t startTime, time_t stopTime=0)
{ {
timespec start; timespec start;
start.tv_sec = startTime; start.tv_sec = startTime;
start.tv_nsec = 0; start.tv_nsec = 0;
// so we have a ton of files, and internally these are not ordered // so we have a ton of files, and internally these are not ordered
map<string,uint32_t> fpos;
vector<pair<timespec,string> > rnmms; vector<pair<timespec,string> > rnmms;
for(;;) { for(;;) {
rnmms.clear(); cerr<<"Gathering data"<<endl;
for(const auto& dir : dirs) {
cerr<<"Gathering data from "<<humanTime(start.tv_sec)<<" from "<<dir<<".. ";
vector<uint64_t> srcs = stations.empty() ? getSources(dir) : stations; vector<uint64_t> srcs = stations.empty() ? getSources(dir) : stations;
int count=0; rnmms.clear();
for(const auto& src: srcs) { for(const auto& src: srcs) {
string fname = getPath(dir, start.tv_sec, src); string fname = getPath(dir, start.tv_sec, src);
FILE* ptr = fopen(fname.c_str(), "r"); FILE* fp = fopen(fname.c_str(), "r");
shared_ptr<FILE> fp; if(!fp)
if(ptr) {
fp = shared_ptr<FILE>(ptr, fclose);
}
else {
fname+=".zst";
struct stat statbuf;
if(stat(fname.c_str(), &statbuf) == 0) {
ptr = popen(("zstdcat "+fname).c_str(), "r");
if(!ptr)
continue; continue;
fp = shared_ptr<FILE>(ptr, pclose); uint32_t offset= fpos[fname];
} if(fseek(fp, offset, SEEK_SET) < 0) {
else cerr<<"Error seeking: "<<strerror(errno) <<endl;
fclose(fp);
continue; continue;
} }
// cerr <<"Seeked to position "<<fpos[fname]<<" of "<<fname<<endl;
uint32_t looked=0;
string msg; string msg;
struct timespec ts; struct timespec ts;
unsigned int offset=0; while(getRawNMM(fp, ts, msg, offset)) {
while(getRawNMM(fp.get(), ts, msg, offset)) {
// don't drop data that is only 5 seconds too old // 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)) { if(make_pair(ts.tv_sec + 5, ts.tv_nsec) >= make_pair(start.tv_sec, start.tv_nsec)) {
rnmms.push_back({ts, msg}); rnmms.push_back({ts, msg});
++count;
} }
++looked;
} }
// cerr<<"Harvested "<<rnmms.size()<<" events out of "<<looked<<endl; // cerr<<"Harvested "<<rnmms.size()<<" events out of "<<looked<<endl;
// fp will close itself fpos[fname]=offset;
fclose(fp);
} }
cerr<<" added "<<count<<endl;
} cerr<<"Sorting data"<<endl;
// cerr<<"Sorting data"<<endl;
// sort(execution::par, rnmms.begin(), rnmms.end(), [](const auto& a, const auto& b)
sort(rnmms.begin(), rnmms.end(), [](const auto& a, const auto& b) sort(rnmms.begin(), rnmms.end(), [](const auto& a, const auto& b)
{ {
return std::tie(a.first.tv_sec, a.first.tv_nsec) return std::tie(a.first.tv_sec, a.first.tv_nsec)
< std::tie(b.first.tv_sec, b.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) { for(const auto& nmm: rnmms) {
if(nmm.first.tv_sec > stopTime) if(nmm.first.tv_sec > stopTime)
break; break;
@ -135,9 +113,8 @@ void sendProtobuf(const vector<string>& dirs, vector<uint64_t> stations, time_t
buf += nmm.second; buf += nmm.second;
//fwrite(buf.c_str(), 1, buf.size(), stdout); //fwrite(buf.c_str(), 1, buf.size(), stdout);
writen2(1, buf.c_str(), buf.size()); writen2(1, buf.c_str(), buf.size());
++count;
} }
cerr<<"Done sending " << count<<" messages"<<endl; cerr<<"Done sending"<<endl;
if(3600 + start.tv_sec - (start.tv_sec%3600) < stopTime) if(3600 + start.tv_sec - (start.tv_sec%3600) < stopTime)
start.tv_sec = 3600 + start.tv_sec - (start.tv_sec%3600); start.tv_sec = 3600 + start.tv_sec - (start.tv_sec%3600);
else { else {
@ -152,16 +129,14 @@ int main(int argc, char** argv)
bool doVERSION{false}; bool doVERSION{false};
CLI::App app(program); CLI::App app(program);
string beginarg, endarg; string beginarg, endarg, storage;
vector<string> storages; app.add_option("--storage,-s", storage, "Location of storage files");
int galwn{-1};
app.add_option("--storage,-s", storages, "Locations of storage files");
vector<uint64_t> stations; vector<uint64_t> stations;
app.add_flag("--version", doVERSION, "show program version and copyright"); 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)"); app.add_option("--begin,-b", beginarg, "Begin time (2020-01-01 00:00, or 12:30)")->required();
app.add_option("--end,-e", endarg, "End time. Now if omitted"); 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("--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); CLI11_PARSE(app, argc, argv);
@ -170,19 +145,9 @@ int main(int argc, char** argv)
exit(0); exit(0);
} }
time_t startTime, stopTime; time_t startTime = parseTime(beginarg);
if(galwn >=0) {
startTime=utcFromGST(galwn, 0); time_t stopTime = endarg.empty() ? time(0) : parseTime(endarg);
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; cerr<<"Emitting from "<<humanTime(startTime) << " to " << humanTime(stopTime) << endl;
if(!stations.empty()) { if(!stations.empty()) {
@ -190,6 +155,7 @@ int main(int argc, char** argv)
for(const auto& s : stations) for(const auto& s : stations)
cerr<<" "<<s; cerr<<" "<<s;
cerr<<endl; cerr<<endl;
} }
sendProtobuf(storages, stations, startTime, stopTime); sendProtobuf(storage, stations, startTime, stopTime);
} }

View file

@ -188,7 +188,7 @@ int main(int argc, char** argv)
static map<int, GalileoMessage> gms; static map<int, GalileoMessage> gms;
GalileoMessage& gm = gms[nmm.gi().gnsssv()]; GalileoMessage& gm = gms[nmm.gi().gnsssv()];
auto inav = makeVec((uint8_t*)nmm.gi().contents().c_str(), nmm.gi().contents().size()); basic_string<uint8_t> inav((uint8_t*)nmm.gi().contents().c_str(), nmm.gi().contents().size());
int wtype =gm.parse(inav); int wtype =gm.parse(inav);
wk.emitLine(sv, "src "+to_string(nmm.sourceid())+" wtype " + to_string(wtype)); wk.emitLine(sv, "src "+to_string(nmm.sourceid())+" wtype " + to_string(wtype));
// wk.setStatus(sv, "Hlth: "+std::to_string(getbitu(&inav[0], 67, 2)) +", el="+to_string(g_svstats[sv].el)+", db="+to_string(g_svstats[sv].db) ); // wk.setStatus(sv, "Hlth: "+std::to_string(getbitu(&inav[0], 67, 2)) +", el="+to_string(g_svstats[sv].el)+", db="+to_string(g_svstats[sv].db) );

View file

@ -3,7 +3,6 @@
#include <iostream> #include <iostream>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "fmt/format.h" #include "fmt/format.h"
#include "fmt/os.h"
#include "fmt/printf.h" #include "fmt/printf.h"
#include "CLI/CLI.hpp" #include "CLI/CLI.hpp"
#include <fstream> #include <fstream>
@ -26,14 +25,12 @@
#include "tle.hh" #include "tle.hh"
#include "sp3.hh" #include "sp3.hh"
#include "ubx.hh" #include "ubx.hh"
#include <optional>
#include <unistd.h> #include <unistd.h>
#include "sbas.hh" #include "sbas.hh"
#include "version.hh" #include "version.hh"
#include "gpscnav.hh" #include "gpscnav.hh"
#include "rinex.hh" #include "rinex.hh"
#include "rtcm.hh" #include "rtcm.hh"
#include "fixhunter.hh"
static char program[]="navdump"; static char program[]="navdump";
@ -260,22 +257,12 @@ try
bool doReceptionData{false}; bool doReceptionData{false};
bool doRFData{true}; bool doRFData{true};
bool doObserverPosition{false}; bool doObserverPosition{false};
bool doObserverDetails{false};
bool doTimeOffsets{false};
bool doVERSION{false}; bool doVERSION{false};
bool doJammingData{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("--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("--stations", stations, "Listen to specified stations.");
app.add_option("--positions,-p", doObserverPosition, "Print out observer positions (or not)"); 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("--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("--recdata", doReceptionData, "Print out reception data (or not)");
app.add_option("--jamdata", doJammingData, "Print out jamming 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"); app.add_flag("--version", doVERSION, "show program version and copyright");
try { try {
@ -312,18 +299,7 @@ try
sp3csv<<"timestamp gnssid sv ephAge sp3X sp3Y sp3Z ephX ephY ephZ sp3Clock ephClock distance along clockDelta E speed"<<endl; sp3csv<<"timestamp gnssid sv ephAge sp3X sp3Y sp3Z ephX ephY ephZ sp3Clock ephClock distance along clockDelta E speed"<<endl;
std::optional<RINEXNavWriter> rnw; // RINEXNavWriter rnw("test.rnx");
if(!rinexfname.empty())
rnw = RINEXNavWriter(rinexfname);
std::optional<ofstream> osnmacsv;
if(!osnmafname.empty()) {
osnmacsv = ofstream(osnmafname);
(*osnmacsv)<<"wn,tow,wtype,sv,osnma\n";
}
for(;;) { for(;;) {
char bert[4]; char bert[4];
int res = readn2(0, bert, 4); int res = readn2(0, bert, 4);
@ -387,11 +363,9 @@ try
} }
} }
else if(nmm.type() == NavMonMessage::GalileoInavType) { else if(nmm.type() == NavMonMessage::GalileoInavType) {
auto inav = makeVec((uint8_t*)nmm.gi().contents().c_str(), nmm.gi().contents().size()); basic_string<uint8_t> inav((uint8_t*)nmm.gi().contents().c_str(), nmm.gi().contents().size());
static map<int, GalileoMessage> gms; static map<int, GalileoMessage> gms;
static map<pair<int, int>, GalileoMessage> gmwtypes; static map<pair<int, int>, GalileoMessage> gmwtypes;
static map<int, FixHunter> fixhunters;
static map<int, GalileoMessage> oldgm4s; static map<int, GalileoMessage> oldgm4s;
int sv = nmm.gi().gnsssv(); int sv = nmm.gi().gnsssv();
if(!svfilter.check(2, sv, nmm.gi().sigid())) if(!svfilter.check(2, sv, nmm.gi().sigid()))
@ -413,41 +387,20 @@ try
GalileoMessage& gm = gms[sv]; GalileoMessage& gm = gms[sv];
int wtype = gm.parse(inav); int wtype = gm.parse(inav);
if(wtype != 0 && wtype != 5 && wtype != 6)
gm.tow = nmm.gi().gnsstow(); gm.tow = nmm.gi().gnsstow();
bool isnew = gmwtypes[{nmm.gi().gnsssv(), wtype}].tow != gm.tow;
gmwtypes[{nmm.gi().gnsssv(), wtype}] = gm; gmwtypes[{nmm.gi().gnsssv(), wtype}] = gm;
// fixhunters[nmm.gi().gnsssv()].reportInav(inav,nmm.gi().gnsstow() );
static map<int,GalileoMessage> oldEph; 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(); cout << "gal inav wtype "<<wtype<<" for "<<nmm.gi().gnssid()<<","<<nmm.gi().gnsssv()<<","<<nmm.gi().sigid()<<" pbwn "<<nmm.gi().gnsswn()<<" pbtow "<< nmm.gi().gnsstow();
if(nmm.gi().has_ssp()) {
cout<<" ssp "<<(unsigned int) nmm.gi().ssp();
}
static uint32_t tow; 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) { if(wtype == 4) {
// 2^-34 2^-46 // 2^-34 2^-46
cout <<" iodnav "<<gm.iodnav <<" af0 "<<gm.af0 <<" af1 "<<gm.af1 <<", af2 "<<(int)gm.af2<<" scaled: "<<1000000000.0*ldexp(1.0*gm.af0, 19-34)/(1<<19) <<" ns, "<<ldexp(1.0*gm.af1, 38-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) { if(tow && oldgm4s.count(nmm.gi().gnsssv()) && oldgm4s[nmm.gi().gnsssv()].iodnav != gm.iodnav) {
auto& oldgm4 = oldgm4s[nmm.gi().gnsssv()]; auto& oldgm4 = oldgm4s[nmm.gi().gnsssv()];
auto oldOffset = oldgm4.getAtomicOffset(tow); auto oldOffset = oldgm4.getAtomicOffset(tow);
auto newOffset = gm.getAtomicOffset(tow); auto newOffset = gm.getAtomicOffset(tow);
cout<<" Timejump: "<<oldOffset.first - newOffset.first<<" ns after "<<(gm.getT0c() - oldgm4.getT0c() )<<" seconds"; cout<<" Timejump: "<<oldOffset.first - newOffset.first<<" after "<<(gm.getT0c() - oldgm4.getT0c() )<<" seconds";
} }
oldgm4s[nmm.gi().gnsssv()] = gm; oldgm4s[nmm.gi().gnsssv()] = gm;
@ -473,7 +426,7 @@ try
if(!haveSeen.count({sv, bestSP3->t})) { if(!haveSeen.count({sv, bestSP3->t})) {
haveSeen.insert({sv, bestSP3->t}); haveSeen.insert({sv, bestSP3->t});
Point newPoint; Point newPoint;
double E=getCoordinates(gm.tow + (bestSP3->t - start), gm, &newPoint); double E=getCoordinates(gm.tow + (bestSP3->t - start), gm, &newPoint, false);
Point sp3Point(bestSP3->x, bestSP3->y, bestSP3->z); Point sp3Point(bestSP3->x, bestSP3->y, bestSP3->z);
Vector dist(newPoint, sp3Point); Vector dist(newPoint, sp3Point);
@ -500,9 +453,7 @@ try
if(!oldEph[sv].sqrtA) if(!oldEph[sv].sqrtA)
oldEph[sv] = gm; oldEph[sv] = gm;
else if(oldEph[sv].iodnav != gm.iodnav) { else if(oldEph[sv].iodnav != gm.iodnav) {
if(rnw) // rnw.emitEphemeris(sid, gm);
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"; 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; Point oldPoint, newPoint;
@ -537,7 +488,7 @@ try
} }
if(wtype == 0 || wtype == 5 || wtype == 6) { if(wtype == 0 || wtype == 5 || wtype == 6) {
if(wtype != 0 || gm.sparetime == 2) { if(wtype != 0 || gm.sparetime == 2) {
cout << " tow "<< gm.tow << " (%30=" << (gm.tow%30)<<") "; cout << " tow "<< gm.tow;
tow = gm.tow; tow = gm.tow;
} }
} }
@ -547,10 +498,8 @@ try
} }
if(wtype == 6) { if(wtype == 6) {
cout<<" a0 " << gm.a0 <<" a1 " << gm.a1 <<" t0t "<<gm.t0t << " dtLS "<<(int)gm.dtLS; 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;
} }
// if(wtype < 7) // if(wtype < 7)
// gm = GalileoMessage{}; // gm = GalileoMessage{};
@ -586,7 +535,7 @@ try
if(gm.alma3.e1bhs != 0) { if(gm.alma3.e1bhs != 0) {
cout <<" gm.tow "<<gm.tow<<" gmwtypes.tow "<< gmwtypes[{sv,9}].tow <<" "; cout <<" gm.tow "<<gm.tow<<" gmwtypes.tow "<< gmwtypes[{sv,9}].tow <<" ";
} }
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"; 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";
int dw = (int)(gm.wn%64) - (int)(gm.wn0g); int dw = (int)(gm.wn%64) - (int)(gm.wn0g);
if(dw > 31) if(dw > 31)
@ -594,154 +543,6 @@ try
int delta = dw*7*86400 + gm.tow - gm.getT0g(); // NOT ephemeris age tricks int delta = dw*7*86400 + gm.tow - gm.getT0g(); // NOT ephemeris age tricks
cout<<" wn%64 "<< (gm.wn%64) << " dw " << dw <<" delta " << delta; cout<<" wn%64 "<< (gm.wn%64) << " dw " << dw <<" delta " << delta;
} }
else if(wtype==16) {
// was -35
cout<<" redced af0red "<< 1000000000.0*ldexp(gm.af0red, -26)<<" ns, "<<3600.0*(1000000000.0/(1<<20))*ldexp(gm.af1red, -15)<<" ns/hour ("<<gm.af1red<<")";
int32_t t0r = 1+nmm.gi().gnsstow() - ((nmm.gi().gnsstow()-2)%30) -2;
cout<<" t0r "<<t0r<<" ";
//(30*((nmm.gi().gnsstow()-2)/30)+1) % 604800; // page 56 of the OSS ICD 2.0
REDCEDAdaptor rca(gm, t0r);
#if 0
static REDCEDAdaptor* orig=0;
if(!orig)
orig = new REDCEDAdaptor(gm, t0r);
static auto ofs = fmt::output_file("error.csv");
static auto redcedcsv = fmt::output_file("redced.csv");
static bool csvInit;
if(!csvInit) {
ofs.print("{},{},{},{},{},{},{},{},{},{},{},{},{},{}\n",
"tow", "gnssid", "iod", "t0r", "red_x", "red_y", "red_z", "full_x", "full_y", "full_z", "redorig_x", "redorig_y", "redorig_z", "dist");
redcedcsv.print("gnssid,tow,t0r,deltaAred,exred,eyred,lambda0red,deltai0red,omega0red,af0red,af1red,nsec,fixedtow,x,y,z,radcor\n");
csvInit = true;
}
auto fixedtow = t0r + 300;
fixedtow -= (fixedtow % 200);
Point fixedredpoint;
getCoordinates(fixedtow, rca, &fixedredpoint);
redcedcsv.print("{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}\n",
nmm.gi().gnsssv(),
nmm.gi().gnsstow(),
t0r,
gm.deltaAred,
gm.exred,
gm.eyred,
gm.lambda0red,
gm.deltai0red,
gm.omega0red,
gm.af0red,
gm.af1red,
rca.getAtomicOffset(fixedtow).first,
fixedtow,
fixedredpoint.x, fixedredpoint.y, fixedredpoint.z,
sqrt(fixedredpoint.x*fixedredpoint.x + fixedredpoint.y*fixedredpoint.y + fixedredpoint.z*fixedredpoint.z) - rca.getAtomicOffset(fixedtow).first/3.0
);
redcedcsv.flush();
#endif
cout<<"eyred "<<gm.eyred<<" exred "<<gm.exred<<"\nlambda0red in rad "<< ldexp(M_PI*gm.lambda0red, -22)<<" atan2 " <<atan2(1.0*gm.eyred, 1.0*gm.exred)<<" deltaAred "<<gm.deltaAred;
Point pointRed;
getCoordinates(nmm.gi().gnsstow(), rca, &pointRed);
cout<<" reduced coordinates: "<<pointRed;
#if 0
for(int letow = nmm.gi().gnsstow(); letow < nmm.gi().gnsstow() + 120; ++letow) {
getCoordinates(letow, *orig, &pointOrigRed);
cout<<"Reduced coordinates original CED: "<<pointOrigRed<<endl;
getCoordinates(letow + gm.getAtomicOffset(letow).first/1000000000.0, gm, &point);
cout<<"Full coordinates: "<<point<<endl;
Vector dist(pointRed, point);
ofs.print("{},{},{},{},{},{},{},{},{},{},{},{},{},{}\n",
letow,
nmm.gi().gnsssv(),
gm.iodnav,
t0r,
pointRed.x,
pointRed.y,
pointRed.z,
point.x,
point.y,
point.z,
pointOrigRed.x,
pointOrigRed.y,
pointOrigRed.z,
dist.length());
auto ourpos=g_srcpos[nmm.sourceid()];
Vector range1(ourpos, pointRed), range2(ourpos, point);
cout<<"Distance: "<<dist<<", length "<<dist.length()<<", range difference: "<<range1.length() - range2.length()<< " " <<ourpos<<endl;
}
#endif
}
else if(wtype>=17 && wtype <=20) {
cout<<" reed-solomon 2iod "<< (int) gm.rs2bitiod;
}
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) {
auto fnav = makeVec((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;
}
if(gm.wtype == 5) {
cout <<" iodalma "<<gm.ioda <<" svid1 "<< gm.alma1.svid << " e5ahs "<<(int)gm.alma1.e5ahs << " svid2 " <<gm.alma2.svid;
}
if(gm.wtype == 6) {
cout <<" iodalma "<<gm.ioda <<" svid2-e5ahs " << (int)gm.alma2.e5ahs<< " svid3 "<< gm.alma3.svid << " e5ahs "<<(int)gm.alma1.e5ahs;
}
cout<<endl; cout<<endl;
} }
@ -752,7 +553,7 @@ try
continue; continue;
etstamp(); etstamp();
auto cond = getCondensedGPSMessage(makeVec((uint8_t*)nmm.gpsi().contents().c_str(), nmm.gpsi().contents().size())); auto cond = getCondensedGPSMessage(std::basic_string<uint8_t>((uint8_t*)nmm.gpsi().contents().c_str(), nmm.gpsi().contents().size()));
struct GPSState gs; struct GPSState gs;
static map<int, GPSState> eph; static map<int, GPSState> eph;
@ -805,7 +606,7 @@ try
} }
int start = utcFromGPS(gpswn, (int)gs.tow); int start = utcFromGPS(gpswn, (int)gs.tow);
cout<<" sp3 start: "<<start<<" wn " << gpswn<<" tow " << gs.tow; cout<<"sp3 start: "<<start<<" wn " << gpswn<<" tow " << gs.tow << endl;
SP3Entry e{0, sv, start}; SP3Entry e{0, sv, start};
auto bestSP3 = lower_bound(g_sp3s.begin(), g_sp3s.end(), e, sp3Order); auto bestSP3 = lower_bound(g_sp3s.begin(), g_sp3s.end(), e, sp3Order);
@ -851,7 +652,7 @@ try
cout<<" 2nd-match "<<second.name << " dist "<<second.distance/1000<<" km t0e "<<gs.gpsalma.getT0e() << " t " <<nmm.localutcseconds(); cout<<" 2nd-match "<<second.name << " dist "<<second.distance/1000<<" km t0e "<<gs.gpsalma.getT0e() << " t " <<nmm.localutcseconds();
} }
if(page == 18) if(page == 18)
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; cout << " dtLS " << (int)gs.dtLS <<" dtLSF "<< (int)gs.dtLSF;
} }
else if(frame == 5) { else if(frame == 5) {
if(gs.gpsalma.sv <= 24) { if(gs.gpsalma.sv <= 24) {
@ -899,42 +700,19 @@ try
} }
} }
else if(rm.type == 1045 || rm.type == 1046) { 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; SatID sid;
sid.gnss = 2; sid.gnss = 2;
sid.sv = rm.d_sv; sid.sv = rm.d_sv;
sid.sigid = (rm.type == 1045) ? 5 : 1; sid.sigid = (rm.type == 1045) ? 5 : 1;
cout<< makeSatIDName(sid)<<" "; cout<< makeSatIDName(sid)<<" ";
if(rm.type == 1045) { 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 ";
cout<<"F/NAV"; else
} cout <<"I/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; 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 == 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 { else {
cout<<" len " << nmm.rm().contents().size() << endl; cout<<" len " << nmm.rm().contents().size() << endl;
@ -951,7 +729,7 @@ try
static map<int, GPSCNavState> states; static map<int, GPSCNavState> states;
auto& state = states[sv]; auto& state = states[sv];
int type = parseGPSCNavMessage( int type = parseGPSCNavMessage(
makeVec((uint8_t*)nmm.gpsc().contents().c_str(), std::basic_string<uint8_t>((uint8_t*)nmm.gpsc().contents().c_str(),
nmm.gpsc().contents().size()), nmm.gpsc().contents().size()),
state); state);
@ -969,9 +747,9 @@ try
continue; continue;
etstamp(); etstamp();
std::vector<uint8_t> cond; std::basic_string<uint8_t> cond;
try { try {
cond = getCondensedBeidouMessage(makeVec((uint8_t*)nmm.bid1().contents().c_str(), nmm.bid1().contents().size())); cond = getCondensedBeidouMessage(std::basic_string<uint8_t>((uint8_t*)nmm.bid1().contents().c_str(), nmm.bid1().contents().size()));
} }
catch(std::exception& e) { catch(std::exception& e) {
cout<<"Parsing error"<<endl; cout<<"Parsing error"<<endl;
@ -1003,6 +781,8 @@ try
cout<<" best-tle-match "<<match.name <<" dist "<<match.distance /1000<<" km"; cout<<" best-tle-match "<<match.name <<" dist "<<match.distance /1000<<" km";
cout<<" norad " <<match.norad <<" int-desig " << match.internat; cout<<" norad " <<match.norad <<" int-desig " << match.internat;
cout<<" 2nd-match "<<second.name << " dist "<<second.distance/1000<<" km"; cout<<" 2nd-match "<<second.name << " dist "<<second.distance/1000<<" km";
} }
else if((fraid == 4 && 1<= pageno && pageno <= 24) || else if((fraid == 4 && 1<= pageno && pageno <= 24) ||
(fraid == 5 && 1<= pageno && pageno <= 6) || (fraid == 5 && 1<= pageno && pageno <= 6) ||
@ -1033,7 +813,7 @@ try
cout<<" WNa "<<getbitu(&cond[0], beidouBitconv(190), 8)<<" t0a "<<getbitu(&cond[0], beidouBitconv(198), 8); cout<<" WNa "<<getbitu(&cond[0], beidouBitconv(190), 8)<<" t0a "<<getbitu(&cond[0], beidouBitconv(198), 8);
} }
else if(bm.fraid == 5 && pageno==10) { else if(bm.fraid == 5 && pageno==10) {
cout <<" dTLS "<< (int)bm.deltaTLS << " dTLSF " << (int) bm.deltaTLSF <<" wnLSF " << (unsigned int)bm.wnLSF <<" dn "<<(unsigned int) bm.dn<<endl; cout <<" dTLS "<< (int)bm.deltaTLS;
} }
else if(bm.fraid == 5 && pageno==24) { else if(bm.fraid == 5 && pageno==24) {
int AmID= getbitu(&cond[0], beidouBitconv(216), 2); int AmID= getbitu(&cond[0], beidouBitconv(216), 2);
@ -1050,7 +830,7 @@ try
continue; continue;
etstamp(); etstamp();
auto cond = getCondensedBeidouMessage(makeVec((uint8_t*)nmm.bid2().contents().c_str(), nmm.bid2().contents().size())); auto cond = getCondensedBeidouMessage(std::basic_string<uint8_t>((uint8_t*)nmm.bid2().contents().c_str(), nmm.bid2().contents().size()));
BeidouMessage bm; BeidouMessage bm;
uint8_t pageno; uint8_t pageno;
int fraid = bm.parse(cond, &pageno); int fraid = bm.parse(cond, &pageno);
@ -1065,7 +845,7 @@ try
static map<int, GlonassMessage> gms; static map<int, GlonassMessage> gms;
auto& gm = gms[nmm.gloi().gnsssv()]; auto& gm = gms[nmm.gloi().gnsssv()];
int strno = gm.parse(makeVec((uint8_t*)nmm.gloi().contents().c_str(), nmm.gloi().contents().size())); int strno = gm.parse(std::basic_string<uint8_t>((uint8_t*)nmm.gloi().contents().c_str(), nmm.gloi().contents().size()));
cout<<"Glonass R"<<nmm.gloi().gnsssv()<<" @ "<< ((int)nmm.gloi().freq()-7) <<" strno "<<strno; cout<<"Glonass R"<<nmm.gloi().gnsssv()<<" @ "<< ((int)nmm.gloi().freq()-7) <<" strno "<<strno;
if(strno == 1) { if(strno == 1) {
@ -1234,7 +1014,6 @@ try
} }
else if(nmm.type() == NavMonMessage::ObserverDetailsType) { else if(nmm.type() == NavMonMessage::ObserverDetailsType) {
if(doObserverDetails) {
etstamp(); etstamp();
cout<<"vendor "<<nmm.od().vendor()<<" hwversion " <<nmm.od().hwversion()<<" modules "<<nmm.od().modules()<<" swversion "<<nmm.od().swversion(); cout<<"vendor "<<nmm.od().vendor()<<" hwversion " <<nmm.od().hwversion()<<" modules "<<nmm.od().modules()<<" swversion "<<nmm.od().swversion();
cout<<" serial "<<nmm.od().serialno(); cout<<" serial "<<nmm.od().serialno();
@ -1247,21 +1026,18 @@ try
cout<<endl; cout<<endl;
} }
}
else if(nmm.type() == NavMonMessage::UbloxJammingStatsType) { else if(nmm.type() == NavMonMessage::UbloxJammingStatsType) {
if(doJammingData) {
etstamp(); etstamp();
cout<<"noisePerMS "<<nmm.ujs().noiseperms() << " agcCnt "<< cout<<"noisePerMS "<<nmm.ujs().noiseperms() << " agcCnt "<<
nmm.ujs().agccnt()<<" flags "<<nmm.ujs().flags()<<" jamind "<< nmm.ujs().agccnt()<<" flags "<<nmm.ujs().flags()<<" jamind "<<
nmm.ujs().jamind()<<endl; nmm.ujs().jamind()<<endl;
} }
}
else if(nmm.type() == NavMonMessage::SBASMessageType) { else if(nmm.type() == NavMonMessage::SBASMessageType) {
if(!svfilter.check(1, nmm.sbm().gnsssv(), 0)) if(!svfilter.check(1, nmm.sbm().gnsssv(), 0))
continue; continue;
etstamp(); etstamp();
auto sbas = makeVec((uint8_t*)nmm.sbm().contents().c_str(), nmm.sbm().contents().size()); basic_string<uint8_t> sbas((uint8_t*)nmm.sbm().contents().c_str(), nmm.sbm().contents().size());
cout<<" PRN "<<nmm.sbm().gnsssv()<<" SBAS message type "; cout<<" PRN "<<nmm.sbm().gnsssv()<<" SBAS message type ";
// Preamble sequence: // Preamble sequence:
@ -1315,12 +1091,10 @@ try
} }
else if(nmm.type() == NavMonMessage::DebuggingType) { else if(nmm.type() == NavMonMessage::DebuggingType) {
auto res = parseTrkMeas(makeVec((const uint8_t*)nmm.dm().payload().c_str(), nmm.dm().payload().size())); auto res = parseTrkMeas(basic_string<uint8_t>((const uint8_t*)nmm.dm().payload().c_str(), nmm.dm().payload().size()));
if(res.empty()) if(res.empty())
continue; continue;
etstamp(); etstamp();
cout<<"ublox debug message ";
uint64_t maxt=0; uint64_t maxt=0;
for(const auto& sv : res) { for(const auto& sv : res) {
if(sv.gnss != 2) continue; if(sv.gnss != 2) continue;
@ -1417,18 +1191,16 @@ try
hexstring+=fmt::sprintf("%x", (int)getbitu((unsigned char*)id.c_str(), 4 + 4*n, 4)); hexstring+=fmt::sprintf("%x", (int)getbitu((unsigned char*)id.c_str(), 4 + 4*n, 4));
cout<<"SAR RLM type "<< nmm.sr().type() <<" from gal sv "; cout<<" SAR RLM type "<< nmm.sr().type() <<" from gal sv ";
cout<< nmm.sr().gnsssv() << " beacon "<<hexstring <<" code "<<(int)nmm.sr().code()<<" params "<< makeHexDump(nmm.sr().params()) <<endl; cout<< nmm.sr().gnsssv() << " beacon "<<hexstring <<" code "<<(int)nmm.sr().code()<<" params "<< makeHexDump(nmm.sr().params()) <<endl;
} }
else if(nmm.type() == NavMonMessage::TimeOffsetType) { else if(nmm.type() == NavMonMessage::TimeOffsetType) {
if(doTimeOffsets) {
etstamp(); etstamp();
cout<<" got a time-offset message with "<< nmm.to().offsets().size()<<" offsets: "; cout<<" got a time-offset message with "<< nmm.to().offsets().size()<<" offsets: ";
for(const auto& o : nmm.to().offsets()) { for(const auto& o : nmm.to().offsets()) {
cout << "gnssid "<<o.gnssid()<<" offset " << o.offsetns() << " +- "<<o.tacc()<<" ("<<o.valid()<<") , "; cout << "gnssid "<<o.gnssid()<<" offset " << o.offsetns() << " +- "<<o.tacc()<<" ("<<o.valid()<<") , ";
} }
cout<<endl; cout<<endl;
}
} }
else { else {

View file

@ -1,286 +0,0 @@
#include "sclasses.hh"
#include "swrappers.hh"
#include <map>
#include "navmon.hh"
#include "navmon.pb.h"
#include <thread>
#include <signal.h>
#include "fmt/format.h"
#include "fmt/printf.h"
#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;
set<int> g_bsset;
// navmerge can also dedup its output, we keep track of recent messages here
// this means each Galileo message will only get set once
map<tuple<uint32_t, std::string, uint32_t, std::string, int16_t>, time_t> g_seen;
bool g_inavdedup{false}, g_gpsdedup{false};
/* Goal: do a number of TCP operations that have a combined timeout.
maybe some helper:
auto deadline = xSecondsFromNow(1.5);
SConnectWithDeadline(sock, addr, deadline);
resp=SReadWithDeadline(sock, 2, deadline);
..
resp2=SReadWithDeadline(sock, num, deadline);
//
// not getting 'num' bytes -> error
// exceeding timeout before getting 'num' bytes -> error
*/
static auto xSecondsFromNow(double seconds)
{
auto now = chrono::steady_clock::now();
now += std::chrono::milliseconds((unsigned int)(seconds*1000));
return now;
}
static int msecLeft(const std::chrono::steady_clock::time_point& deadline)
{
auto now = chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now).count();
}
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) {
auto deadline = xSecondsFromNow(600); //
string part=SReadWithDeadline(sock, 4, deadline);
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 = SReadWithDeadline(sock, 2, deadline);
out += part;
uint16_t len;
memcpy(&len, part.c_str(), 2);
len = htons(len);
part = SReadWithDeadline(sock, len, deadline); // XXXXX ???
if(part.size() != len) {
cerr<<"Mismatch, "<<part.size()<<", len "<<len<<endl;
// XX AND THEN WHAT??
}
out += part;
// if(msecLeft(deadline)/1000.0 < 119)
// cerr<<"Done with "<<msecLeft(deadline)/1000.0<<" seconds left\n";
NavMonMessage nmm;
nmm.ParseFromString(part);
if(g_bsset.count(nmm.sourceid()))
continue;
if(g_inavdedup) {
if(nmm.type() == NavMonMessage::GalileoInavType) {
std::lock_guard<std::mutex> mut(g_mut);
decltype(g_seen)::key_type tup(nmm.gi().gnsssv(),
nmm.gi().contents(),
nmm.gi().sigid(),
nmm.gi().reserved1(),
nmm.gi().has_ssp() ? nmm.gi().ssp() : -1);
if(!g_seen.count(tup))
g_buffer.insert({{nmm.localutcseconds(), nmm.localutcnanoseconds()}, part});
g_seen[tup]=time(0);
}
}
if(g_gpsdedup) {
if(nmm.type() == NavMonMessage::GPSInavType) {
std::lock_guard<std::mutex> mut(g_mut);
decltype(g_seen)::key_type tup(nmm.gpsi().gnsssv(),
nmm.gpsi().contents(),
nmm.gpsi().sigid(),
"",
0);
if(!g_seen.count(tup))
g_buffer.insert({{nmm.localutcseconds(), nmm.localutcnanoseconds()}, part});
g_seen[tup]=time(0);
}
}
if(!g_gpsdedup && !g_inavdedup) {
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;
}
static void cleanFilter()
{
time_t lim = time(0) - 60;
std::lock_guard<std::mutex> mut(g_mut);
for(auto iter = g_seen.begin(); iter!= g_seen.end() ;) {
if(iter->second < lim)
iter = g_seen.erase(iter);
else
++iter;
}
}
int main(int argc, char** argv)
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
vector<string> destinations;
vector<string> sources;
vector<string> listeners;
vector<int> badstations;
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_option("--drop-stations", badstations, "Drop these station numbers");
app.add_option("--listener,-l", listeners, "Make data available on this IPv4/v6 address");
app.add_flag("--inavdedup", g_inavdedup, "Only pass on Galileo I/NAV, and dedeup");
app.add_flag("--gpsdedup", g_gpsdedup, "Only pass on GPS, and dedeup");
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);
}
if(sources.empty()) {
cerr<< "No sources defined. Exiting."<<endl;
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));
}
for(const auto& l : listeners) {
ComboAddress ca(l, 29604);
cerr<<"Adding listener on "<<ca.toStringWithPort()<<endl;
ns.addListener(l); // ComboAddress(s, 29603));
}
if(doSTDOUT)
ns.addDestination(1);
for(const auto& bs : badstations) {
g_bsset.insert(bs);
cerr<<"Dropping station "<<bs<<endl;
}
for(const auto& s : sources) {
ComboAddress oneaddr(s, 29601);
std::thread one(recvSession, oneaddr);
one.detach();
}
ns.launch();
time_t start=time(0);
int counter=0;
for(;;) {
usleep(500000);
vector<string> tosend;
{
std::lock_guard<std::mutex> mut(g_mut);
time_t now = time(0);
if(now - start < 30) { // was 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) {
if(!((counter++) % 32768))
cleanFilter();
ns.emitNMM(m);
}
}
}

View file

@ -159,18 +159,6 @@ std::string humanTimeShort(time_t t)
return buffer; 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) std::string humanTime(time_t t, uint32_t nanoseconds)
{ {
@ -292,33 +280,22 @@ std::string makeSatPartialName(const SatID& satid)
return fmt::sprintf("%c%02d", getGNSSChar(satid.gnss), satid.sv); 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) void getGPSDateFromUTC(time_t t, int& wn, int& tow)
{ {
t -= 315964800; t -= 315964800;
t += 18; // XXXXXX LEAP SECOND t += 18;
wn = t/(7*86400); wn = t/(7*86400);
tow = t%(7*86400); tow = t%(7*86400);
} }
void getGalDateFromUTC(time_t t, int& wn, int& tow) void getGalDateFromUTC(time_t t, int& wn, int& tow)
{ {
t -= 935280000; t -= 935280000;
t += 18; // XXXXXXX LEAP SECOND t += 18;
wn = t/(7*86400); wn = t/(7*86400);
tow = t%(7*86400); tow = t%(7*86400);
} }
void getBeiDouDateFromUTC(time_t t, int&wn, int& sow) int g_dtLS{18}, g_dtLSBeidou{4};
{
// 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) uint64_t utcFromGST(int wn, int tow)
{ {
return (935280000 + wn * 7*86400 + tow - g_dtLS); return (935280000 + wn * 7*86400 + tow - g_dtLS);
@ -347,19 +324,6 @@ string makeHexDump(const string& str)
return ret; return ret;
} }
string makeHexDump(const vector<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) std::string sbasName(int prn)
{ {
string sbas; string sbas;
@ -411,7 +375,7 @@ time_t parseTime(std::string_view in)
{ {
time_t now=time(0); time_t now=time(0);
vector<string> formats({"%Y-%m-%d %H:%M", "%Y-%m-%d %H:%M:%S", "%Y%m%d %H%M", "%H:%M", "%H%M"}); vector<string> formats({"%Y-%m-%d %H:%M", "%Y%m%d %H%M", "%H:%M", "%H%M"});
for(const auto& f : formats) { for(const auto& f : formats) {
struct tm tm; struct tm tm;
memset(&tm, 0, sizeof(tm)); memset(&tm, 0, sizeof(tm));
@ -428,24 +392,7 @@ time_t parseTime(std::string_view in)
string err="Can only parse on"; string err="Can only parse on";
for(const auto& f : formats) for(const auto& f : formats)
err += " '"+ f+"'"; err += " "+ f;
throw runtime_error(err); 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;
}

View file

@ -5,8 +5,6 @@
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <mutex> #include <mutex>
#include <limits.h>
#include <vector>
extern const char* g_gitHash; extern const char* g_gitHash;
@ -20,8 +18,6 @@ std::string humanTimeNow();
std::string humanTime(time_t t); std::string humanTime(time_t t);
std::string humanTimeShort(time_t t); std::string humanTimeShort(time_t t);
std::string humanTime(time_t t, uint32_t nanoseconds); std::string humanTime(time_t t, uint32_t nanoseconds);
// influx ascii time format, in UTC
std::string influxTime(time_t t);
struct SatID struct SatID
{ {
uint32_t gnss{255}; // these could all be 'int16_t' but leads to howling numbers of warnings with protobuf uint32_t gnss{255}; // these could all be 'int16_t' but leads to howling numbers of warnings with protobuf
@ -82,16 +78,9 @@ double utcFromGPS(int wn, double tow);
void getGPSDateFromUTC(time_t t, int& wn, int& tow); void getGPSDateFromUTC(time_t t, int& wn, int& tow);
void getGalDateFromUTC(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::string& str);
std::string makeHexDump(const std::vector<uint8_t>& str);
size_t writen2(int fd, const void *buf, size_t count); size_t writen2(int fd, const void *buf, size_t count);
void unixDie(const std::string& reason); void unixDie(const std::string& reason);
time_t parseTime(std::string_view in); 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);
inline const std::vector<uint8_t> makeVec(const uint8_t* ptr, size_t len)
{
return std::vector(ptr, ptr+len);
}

View file

@ -18,8 +18,6 @@ message NavMonMessage {
GPSCnavType = 14; GPSCnavType = 14;
RTCMMessageType = 15; RTCMMessageType = 15;
TimeOffsetType = 16; TimeOffsetType = 16;
GalileoFnavType = 17;
GalileoCnavType = 18;
} }
required uint64 sourceID = 1; required uint64 sourceID = 1;
@ -36,31 +34,6 @@ message NavMonMessage {
required uint32 gnssSV =4; required uint32 gnssSV =4;
required bytes contents =5; 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;
optional uint32 ssp = 12;
}
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 { message GPSInav {
@ -244,6 +217,4 @@ message GalileoFnav {
optional GPSCnav gpsc = 18; optional GPSCnav gpsc = 18;
optional RTCMMessage rm = 19; optional RTCMMessage rm = 19;
optional TimeOffsetMessage to = 20; optional TimeOffsetMessage to = 20;
optional GalileoFnav gf=21;
optional GalileoCnav gc=22;
} }

View file

@ -35,7 +35,6 @@ void unixDie(const std::string& str)
throw std::runtime_error(str+string(": ")+string(strerror(errno))); throw std::runtime_error(str+string(": ")+string(strerror(errno)));
} }
// station numbers
vector<uint64_t> getSources() vector<uint64_t> getSources()
{ {
DIR *dir = opendir(g_storage.c_str()); DIR *dir = opendir(g_storage.c_str());
@ -89,7 +88,7 @@ try
close(fd); close(fd);
continue; continue;
} }
// cout <<"Seeked to position "<<fpos[fname]<<" of "<<fname<<endl; cout <<"Seeked to position "<<fpos[fname]<<" of "<<fname<<endl;
NavMonMessage nmm; NavMonMessage nmm;
uint32_t looked=0; uint32_t looked=0;
@ -99,10 +98,6 @@ try
while(getRawNMM(fd, ts, msg, offset)) { while(getRawNMM(fd, ts, msg, offset)) {
// don't drop data that is only 5 seconds too old // 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)) { if(make_pair(ts.tv_sec + 5, ts.tv_nsec) >= make_pair(start.tv_sec, start.tv_nsec)) {
if(ts.tv_sec > time(0)) {
cout << "station "<<src <<" is living in the future, skipping message\n";
}
else
rnmms.push_back({ts, msg}); rnmms.push_back({ts, msg});
} }
++looked; ++looked;
@ -111,15 +106,15 @@ try
fpos[fname]=offset; fpos[fname]=offset;
close(fd); close(fd);
} }
// cout<<"Sorting.. "; cout<<"Sorting.. ";
// cout.flush(); cout.flush();
sort(rnmms.begin(), rnmms.end(), [](const auto& a, const auto& b) sort(rnmms.begin(), rnmms.end(), [](const auto& a, const auto& b)
{ {
return std::tie(a.first.tv_sec, a.first.tv_nsec) return std::tie(a.first.tv_sec, a.first.tv_nsec)
< std::tie(b.first.tv_sec, b.first.tv_nsec); < std::tie(b.first.tv_sec, b.first.tv_nsec);
}); });
// cout<<"Sending.. "; cout<<"Sending.. ";
// cout.flush(); cout.flush();
for(const auto& nmm: rnmms) { for(const auto& nmm: rnmms) {
std::string buf="bert"; std::string buf="bert";
uint16_t len = htons(nmm.second.size()); uint16_t len = htons(nmm.second.size());
@ -127,13 +122,12 @@ try
buf += nmm.second; buf += nmm.second;
SWriten(clientfd, buf); SWriten(clientfd, buf);
} }
// cout<<"Done"<<endl; cout<<"Done"<<endl;
if(3600 + start.tv_sec - (start.tv_sec % 3600) < time(0)) if(3600 + start.tv_sec - (start.tv_sec % 3600) < time(0))
start.tv_sec = 3600 + start.tv_sec - (start.tv_sec % 3600); start.tv_sec = 3600 + start.tv_sec - (start.tv_sec % 3600);
else { else {
if(!rnmms.empty()) // start off where we left it if(!rnmms.empty())
start = {rnmms.rbegin()->first.tv_sec, rnmms.rbegin()->first.tv_nsec}; start = {rnmms.rbegin()->first.tv_sec, rnmms.rbegin()->first.tv_nsec};
// This is a bit iffy as it relies on the station furthest ahead in time
sleep(1); sleep(1);
} }
} }

View file

@ -37,7 +37,7 @@
#include "gpscnav.hh" #include "gpscnav.hh"
#include "rtcm.hh" #include "rtcm.hh"
#include "version.hh" #include "version.hh"
//#include "nequick.hh"
static char program[]="navparse"; static char program[]="navparse";
@ -45,7 +45,7 @@ using namespace std;
extern const char* g_gitHash; extern const char* g_gitHash;
struct ObserverFacts struct ObserverPosition
{ {
Point pos; Point pos;
double groundSpeed{-1}; double groundSpeed{-1};
@ -63,11 +63,9 @@ struct ObserverFacts
string githash; string githash;
string owner; string owner;
string remark; string remark;
bool impinav{false};
time_t impinavTime{0};
time_t lastSeen{0}; time_t lastSeen{0};
}; };
std::map<int, ObserverFacts> g_srcfacts; std::map<int, ObserverPosition> g_srcpos;
struct SBASAndReceiverStatus struct SBASAndReceiverStatus
{ {
@ -265,6 +263,9 @@ void SVStat::reportNewEphemeris(const SatID& id, InfluxPusher& idb)
if(gnss==0) { // GPS if(gnss==0) { // GPS
const auto& eg = ephgpsmsg; const auto& eg = ephgpsmsg;
idb.addValue(id, "ephemeris-actual", {
{"iod", eg.getIOD()}}, satUTCTime(id));
idb.addValue(id, "ephemeris-actual", { idb.addValue(id, "ephemeris-actual", {
{"iod", eg.getIOD()}, {"iod", eg.getIOD()},
@ -295,6 +296,10 @@ void SVStat::reportNewEphemeris(const SatID& id, InfluxPusher& idb)
if(gnss==2) { if(gnss==2) {
const auto& eg = ephgalmsg; const auto& eg = ephgalmsg;
idb.addValue(id, "ephemeris-actual", {
{"iod", eg.getIOD()}}, satUTCTime(id));
idb.addValue(id, "ephemeris-actual", { idb.addValue(id, "ephemeris-actual", {
{"iod", eg.getIOD()}, {"iod", eg.getIOD()},
{"t0e", eg.t0e}, {"t0e", eg.t0e},
@ -440,7 +445,7 @@ std::optional<double> getHzCorrection(time_t now, int src, unsigned int gnssid,
std::string humanBhs(int bhs) std::string humanBhs(int bhs)
{ {
static vector<string> options{"ok", "OUT", "will be out of service", "test"}; static vector<string> options{"ok", "out of service", "will be out of service", "test"};
if(bhs >= (int)options.size()) { if(bhs >= (int)options.size()) {
cerr<<"Asked for humanBHS "<<bhs<<endl; cerr<<"Asked for humanBHS "<<bhs<<endl;
return "??"; return "??";
@ -563,25 +568,6 @@ try {
} }
sats.push_back(sat); 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; // cout<<endl;
auto cov = emitCoverage(sats); auto cov = emitCoverage(sats);
int cells=0; int cells=0;
@ -643,11 +629,10 @@ try
string localAddress("127.0.0.1:29599"); string localAddress("127.0.0.1:29599");
string htmlDir("./html"); string htmlDir("./html");
string influxDBName("null"); string influxDBName("null");
bool doGalileoReportSpeedup{false};
bool doLogRFData{false}; bool doLogRFData{false};
app.add_flag("--version", doVERSION, "show program version and copyright"); 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("--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("--bind,-b", localAddress, "Address to bind to");
app.add_option("--html", htmlDir, "Where to source the HTML & JavaScript"); app.add_option("--html", htmlDir, "Where to source the HTML & JavaScript");
app.add_option("--influxdb", influxDBName, "Name of influxdb database"); app.add_option("--influxdb", influxDBName, "Name of influxdb database");
@ -980,7 +965,7 @@ try
addHeaders(req); addHeaders(req);
nlohmann::json ret = nlohmann::json::array(); nlohmann::json ret = nlohmann::json::array();
for(const auto& src : g_srcfacts) { for(const auto& src : g_srcpos) {
nlohmann::json obj; nlohmann::json obj;
obj["id"] = src.first; obj["id"] = src.first;
auto latlonh = ecefToWGS84(src.second.pos.x, src.second.pos.y, src.second.pos.z); auto latlonh = ecefToWGS84(src.second.pos.x, src.second.pos.y, src.second.pos.z);
@ -1008,10 +993,6 @@ try
obj["owner"]= src.second.owner; obj["owner"]= src.second.owner;
obj["vendor"]= src.second.vendor; obj["vendor"]= src.second.vendor;
obj["remark"]= src.second.remark; obj["remark"]= src.second.remark;
if(src.second.impinavTime >= 0 && (time(0) + 10) - src.second.impinavTime < 86400)
obj["impinav"]=true;
else
obj["impinav"]=false;
obj["acc"] = src.second.accuracy; obj["acc"] = src.second.accuracy;
obj["h"] = get<2>(latlonh); obj["h"] = get<2>(latlonh);
@ -1044,7 +1025,7 @@ try
getCoordinates(latestTow(6, svstats), sv.second.glonassMessage, &sat); getCoordinates(latestTow(6, svstats), sv.second.glonassMessage, &sat);
} }
if(sat.x) { if(sat.x) {
Point our = g_srcfacts[iter->first].pos; Point our = g_srcpos[iter->first].pos;
svo["elev"] = roundf(10.0*getElevationDeg(sat, our))/10.0; svo["elev"] = roundf(10.0*getElevationDeg(sat, our))/10.0;
svo["azi"] = roundf(10.0*getAzimuthDeg(sat, our))/10.0; svo["azi"] = roundf(10.0*getAzimuthDeg(sat, our))/10.0;
} }
@ -1107,6 +1088,10 @@ try
const auto& s= iter->second; const auto& s= iter->second;
// XXX CONVERSION // XXX CONVERSION
/* /*
ret["a0"] = s.a0;
ret["a1"] = s.a1;
ret["a0g"] = s.a0g;
ret["a1g"] = s.a1g;
*/ */
if(id.gnss == 2) { if(id.gnss == 2) {
ret["sf1"] = s.galmsg.sf1; ret["sf1"] = s.galmsg.sf1;
@ -1120,32 +1105,21 @@ try
ret["e1bdvs"]=s.galmsg.e1bdvs; ret["e1bdvs"]=s.galmsg.e1bdvs;
ret["e5bhs"]=s.galmsg.e5bhs; ret["e5bhs"]=s.galmsg.e5bhs;
ret["e1bhs"]=s.galmsg.e1bhs; ret["e1bhs"]=s.galmsg.e1bhs;
ret["ai0"] = s.galmsg.ai0;
ret["ai1"] = s.galmsg.ai1;
ret["ai2"] = s.galmsg.ai2;
ret["dtLS"] = s.galmsg.dtLS;
ret["dtLSF"] = s.galmsg.dtLSF;
ret["wnLSF"] = s.galmsg.wnLSF;
ret["dn"] = s.galmsg.dn;
ret["a0"] = s.galmsg.a0;
ret["a1"] = s.galmsg.a1;
ret["a0g"] = s.galmsg.a0g;
ret["a1g"] = s.galmsg.a1g;
ret["t0c"] = s.galmsg.getT0c();
ret["af0"] = s.galmsg.af0;
ret["af1"] = s.galmsg.af1;
ret["af2"] = s.galmsg.af2;
ret["af0red"] = s.galmsg.af0red;
ret["af1red"] = s.galmsg.af1red;
} }
// XXX CONVERSION // XXX CONVERSION
/* /*
ret["ai0"] = s.ai0;
ret["ai1"] = s.ai1;
ret["ai2"] = s.ai2;
*/ */
ret["wn"] = s.wn(); ret["wn"] = s.wn();
ret["tow"] = s.tow(); ret["tow"] = s.tow();
// XXX CONVERSION // XXX CONVERSION
/* /*
ret["dtLS"] = s.dtLS;
ret["dtLSF"] = s.dtLSF;
ret["wnLSF"] = s.wnLSF;
ret["dn"] = s.dn;
*/ */
if(id.gnss == 3 && svstats[id].ephBeidoumsg.sow >= 0 && svstats[id].ephBeidoumsg.sqrtA != 0) { if(id.gnss == 3 && svstats[id].ephBeidoumsg.sow >= 0 && svstats[id].ephBeidoumsg.sqrtA != 0) {
@ -1550,12 +1524,6 @@ try
} }
} }
if(s.first.gnss == 2) { if(s.first.gnss == 2) {
if(s.second.osnmaTime >= 0 && ephAge(s.second.galmsg.tow, s.second.osnmaTime) < 60)
item["osnma"] = s.second.osnma;
if(s.second.impinavTime >= 0 && ephAge(s.second.galmsg.tow, s.second.impinavTime) < 60)
item["impinav"] = s.second.impinav;
auto galileoalma = g_galileoalmakeeper.get(); auto galileoalma = g_galileoalmakeeper.get();
if(auto iter = galileoalma.find(s.first.sv); iter != galileoalma.end()) { if(auto iter = galileoalma.find(s.first.sv); iter != galileoalma.end()) {
Point almapos; Point almapos;
@ -1668,7 +1636,7 @@ try
s.second.getCoordinates(latestTow(s.first.gnss, svstats), & sat); s.second.getCoordinates(latestTow(s.first.gnss, svstats), & sat);
if(sat.x) { if(sat.x) {
Point our = g_srcfacts[pr.first].pos; Point our = g_srcpos[pr.first].pos;
det["elev"] = roundf(10.0*getElevationDeg(sat, our))/10.0; det["elev"] = roundf(10.0*getElevationDeg(sat, our))/10.0;
det["azi"] = roundf(10.0*getAzimuthDeg(sat, our))/10.0; det["azi"] = roundf(10.0*getAzimuthDeg(sat, our))/10.0;
} }
@ -1697,8 +1665,8 @@ try
item["last-seen-s"] = time(0) - nanoTime(s.first.gnss, s.second.wn(), s.second.tow())/1000000000; item["last-seen-s"] = time(0) - nanoTime(s.first.gnss, s.second.wn(), s.second.tow())/1000000000;
if(s.second.latestDisco >=0) { if(s.second.latestDisco >=0) {
item["orbit-disco"]= truncPrec(s.second.latestDisco, 3); item["latest-disco"]= truncPrec(s.second.latestDisco, 3);
item["orbit-disco-age"]= s.second.latestDiscoAge; item["latest-disco-age"]= s.second.latestDiscoAge;
} }
if(s.second.timeDisco > -100 && s.second.timeDisco < 100) { if(s.second.timeDisco > -100 && s.second.timeDisco < 100) {
item["time-disco"]= truncPrec(s.second.timeDisco, 1); item["time-disco"]= truncPrec(s.second.timeDisco, 1);
@ -1868,7 +1836,7 @@ try
if(!lastCovSyncPoint) if(!lastCovSyncPoint)
holdOffTime = nmm.localutcseconds() + 600; holdOffTime = nmm.localutcseconds() + 600;
if((time_t)nmm.localutcseconds() > holdOffTime) if(nmm.localutcseconds() > holdOffTime)
storeCoverageStats(idb, nmm.localutcseconds()); storeCoverageStats(idb, nmm.localutcseconds());
lastCovSyncPoint = nmm.localutcseconds() / lastCovInterval; lastCovSyncPoint = nmm.localutcseconds() / lastCovInterval;
} }
@ -1880,63 +1848,6 @@ try
lastSelfstatSyncPoint = nmm.localutcseconds() / lastSelfstatInterval; 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_srcfacts.count(pr.first)<<endl;
if(g_srcfacts.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_srcfacts[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) { if(nmm.type() == NavMonMessage::ReceptionDataType) {
int gnssid = nmm.rd().gnssid(); int gnssid = nmm.rd().gnssid();
@ -1966,17 +1877,17 @@ try
Point sat{0,0,0}; Point sat{0,0,0};
//cout<<"Got recdata for "<<id.gnss<<","<<id.sv<<","<<id.sigid<<": count="<<g_svstats.count(id)<<endl; //cout<<"Got recdata for "<<id.gnss<<","<<id.sv<<","<<id.sigid<<": count="<<g_svstats.count(id)<<endl;
if(g_svstats[id].completeIOD() && !(random() % 16)) { if(g_svstats[id].completeIOD() && (id.gnss != 6 || !(random() % 16))) { // glonass is too slow
g_svstats[id].getCoordinates(g_svstats[id].tow(), &sat); g_svstats[id].getCoordinates(g_svstats[id].tow(), &sat);
} }
if(sat.x != 0 && g_srcfacts[nmm.sourceid()].pos.x != 0) { if(sat.x != 0 && g_srcpos[nmm.sourceid()].pos.x != 0) {
if(doLogRFData && !(random() % 16)) if(doLogRFData && !(random() % 4))
idb.addValue(id, "recdata", idb.addValue(id, "recdata",
{ {
{"db", nmm.rd().db()}, {"db", nmm.rd().db()},
{"azi", getAzimuthDeg(sat, g_srcfacts[nmm.sourceid()].pos)}, {"azi", getAzimuthDeg(sat, g_srcpos[nmm.sourceid()].pos)},
{"ele", getElevationDeg(sat, g_srcfacts[nmm.sourceid()].pos)}, {"ele", getElevationDeg(sat, g_srcpos[nmm.sourceid()].pos)},
{"prres", nmm.rd().prres()}, {"prres", nmm.rd().prres()},
{"qi", perrecv.qi}, {"qi", perrecv.qi},
{"used", perrecv.used} {"used", perrecv.used}
@ -1985,7 +1896,7 @@ try
} }
else if(nmm.type() == NavMonMessage::GalileoInavType) { else if(nmm.type() == NavMonMessage::GalileoInavType) {
auto inav = makeVec((uint8_t*)nmm.gi().contents().c_str(), nmm.gi().contents().size()); basic_string<uint8_t> inav((uint8_t*)nmm.gi().contents().c_str(), nmm.gi().contents().size());
int sv = nmm.gi().gnsssv(); int sv = nmm.gi().gnsssv();
int sigid; int sigid;
if(nmm.gi().has_sigid()) if(nmm.gi().has_sigid())
@ -1993,6 +1904,31 @@ try
else else
sigid = 1; // default to E1B sigid = 1; // default to E1B
SatID id={2,(uint32_t)sv,(uint32_t)sigid}; 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]; auto& svstat = g_svstats[id];
svstat.gnss = id.gnss; svstat.gnss = id.gnss;
@ -2001,11 +1937,6 @@ try
auto& gm = svstat.galmsg; auto& gm = svstat.galmsg;
unsigned int wtype = gm.parse(inav); unsigned int wtype = gm.parse(inav);
auto& o = g_srcfacts[nmm.sourceid()];
if(nmm.gi().has_ssp()) {
o.impinav = true;
o.impinavTime = nmm.localutcseconds();
}
if(wtype == 5 && svstat.galmsgTyped.count(5)) { if(wtype == 5 && svstat.galmsgTyped.count(5)) {
const auto& old5gm = svstat.galmsgTyped[5]; const auto& old5gm = svstat.galmsgTyped[5];
@ -2018,15 +1949,6 @@ try
svstat.galmsgTyped[wtype] = gm; svstat.galmsgTyped[wtype] = gm;
if(wtype == 1 || wtype == 2 || wtype == 3 || wtype == 4) { 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}, idb.addValue(id, "ephemeris", {{"iod-live", svstat.galmsg.iodnav},
{"eph-age", ephAge(gm.tow, gm.getT0e())}}, satUTCTime(id)); {"eph-age", ephAge(gm.tow, gm.getT0e())}}, satUTCTime(id));
@ -2037,27 +1959,34 @@ try
if(w > 1 && svstat.galmsgTyped[w-1].iodnav != svstat.galmsgTyped[w].iodnav) if(w > 1 && svstat.galmsgTyped[w-1].iodnav != svstat.galmsgTyped[w].iodnav)
break; break;
} }
if(w==5) { // have complete new ephemeris if(w==5) {
if(svstat.ephgalmsg.iodnav != svstat.galmsgTyped[1].iodnav) { if(svstat.ephgalmsg.iodnav != svstat.galmsgTyped[1].iodnav) {
svstat.oldephgalmsg = svstat.ephgalmsg; svstat.oldephgalmsg = svstat.ephgalmsg;
svstat.ephgalmsg = svstat.galmsgTyped[wtype]; svstat.ephgalmsg = svstat.galmsgTyped[wtype];
svstat.reportNewEphemeris(id, idb); svstat.reportNewEphemeris(id, idb);
} }
} }
} }
svstat.perrecv[nmm.sourceid()].t = nmm.localutcseconds();
// 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();
if(wtype >=1 && wtype <= 4) { // ephemeris if(wtype >=1 && wtype <= 4) { // ephemeris
if(wtype == 3) { if(wtype == 3) {
idb.addValue(id, "sisa", {{"value", svstat.galmsg.sisa}}, satUTCTime(id)); idb.addValue(id, "sisa", {{"value", g_svstats[id].galmsg.sisa}}, satUTCTime(id));
} }
else if(wtype == 4) { else if(wtype == 4) {
idb.addValue(id, "clock", {{"offset_ns", svstat.galmsg.getAtomicOffset(svstat.tow()).first}, idb.addValue(id, "clock", {{"offset_ns", svstat.galmsg.getAtomicOffset(svstat.tow()).first},
{"t0c", svstat.galmsg.t0c*60}, // getT0c()?? {"t0c", g_svstats[id].galmsg.t0c*60}, // getT0c()??
{"af0", svstat.galmsg.af0}, {"af0", g_svstats[id].galmsg.af0},
{"af1", svstat.galmsg.af1}, {"af1", g_svstats[id].galmsg.af1},
{"af2", svstat.galmsg.af2}}, satUTCTime(id)); {"af2", g_svstats[id].galmsg.af2}}, satUTCTime(id));
if(oldgm.af0 && oldgm.t0c != svstat.galmsg.t0c) { if(oldgm.af0 && oldgm.t0c != svstat.galmsg.t0c) {
@ -2065,47 +1994,35 @@ try
auto newOffset = svstat.galmsg.getAtomicOffset(svstat.tow()); auto newOffset = svstat.galmsg.getAtomicOffset(svstat.tow());
svstat.timeDisco = oldOffset.first - newOffset.first; svstat.timeDisco = oldOffset.first - newOffset.first;
if(fabs(svstat.timeDisco) < 10000) if(fabs(svstat.timeDisco) < 10000)
idb.addValue(id, "clock_jump_ns", { idb.addValue(id, "clock_jump_ns", {{"value", svstat.timeDisco}}, satUTCTime(id));
{"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) { else if(wtype == 5) {
idb.addValue(id, "iono", { idb.addValue(id, "iono", {
{"ai0", svstat.galmsg.ai0}, {"ai0", g_svstats[id].galmsg.ai0},
{"ai1", svstat.galmsg.ai1}, {"ai1", g_svstats[id].galmsg.ai1},
{"ai2", svstat.galmsg.ai2}, {"ai2", g_svstats[id].galmsg.ai2},
{"sf1", svstat.galmsg.sf1}, {"sf1", g_svstats[id].galmsg.sf1},
{"sf2", svstat.galmsg.sf2}, {"sf2", g_svstats[id].galmsg.sf2},
{"sf3", svstat.galmsg.sf3}, {"sf3", g_svstats[id].galmsg.sf3},
{"sf4", svstat.galmsg.sf4}, {"sf4", g_svstats[id].galmsg.sf4},
{"sf5", svstat.galmsg.sf5}}, satUTCTime(id)); {"sf5", g_svstats[id].galmsg.sf5}}, satUTCTime(id));
idb.addValue(id, "galbgd", { idb.addValue(id, "galbgd", {
{"BGDE1E5a", svstat.galmsg.BGDE1E5a}, {"BGDE1E5a", g_svstats[id].galmsg.BGDE1E5a},
{"BGDE1E5b", svstat.galmsg.BGDE1E5b}}, satUTCTime(id)); {"BGDE1E5b", g_svstats[id].galmsg.BGDE1E5b}}, satUTCTime(id));
idb.addValue(id, "galhealth", { idb.addValue(id, "galhealth", {
{"e1bhs", svstat.galmsg.e1bhs}, {"e1bhs", g_svstats[id].galmsg.e1bhs},
{"e5bhs", svstat.galmsg.e5bhs}, {"e5bhs", g_svstats[id].galmsg.e5bhs},
{"e5bdvs", svstat.galmsg.e5bdvs}, {"e5bdvs", g_svstats[id].galmsg.e5bdvs},
{"e1bdvs", svstat.galmsg.e1bdvs}}, satUTCTime(id)); {"e1bdvs", g_svstats[id].galmsg.e1bdvs}}, satUTCTime(id));
} }
else if(wtype == 6) { // GST-UTC else if(wtype == 6) { // GST-UTC
const auto& sv = svstat; const auto& sv = g_svstats[id];
g_GSTUTCOffset = sv.galmsg.getUTCOffset(sv.tow(), sv.wn()).first; g_GSTUTCOffset = sv.galmsg.getUTCOffset(sv.tow(), sv.wn()).first;
idb.addValue(id, "utcoffset", { idb.addValue(id, "utcoffset", {
{"a0", sv.galmsg.a0}, {"a0", sv.galmsg.a0},
@ -2144,171 +2061,37 @@ try
g_galileoalma[gm.alma3.svid] = gm.alma3; g_galileoalma[gm.alma3.svid] = gm.alma3;
} }
g_GSTGPSOffset = gm.getGPSOffset(gm.tow, gm.wn).first; g_GSTGPSOffset = gm.getGPSOffset(gm.tow, gm.wn).first;
idb.addValue(id, "gpsoffset", {{"a0g", svstat.galmsg.a0g}, idb.addValue(id, "gpsoffset", {{"a0g", g_svstats[id].galmsg.a0g},
{"a1g", svstat.galmsg.a1g}, {"a1g", g_svstats[id].galmsg.a1g},
{"t0g", svstat.galmsg.t0g}, {"t0g", g_svstats[id].galmsg.t0g},
{"wn0g", svstat.galmsg.wn0g}, {"wn0g", g_svstats[id].galmsg.wn0g},
{"delta", g_GSTGPSOffset} {"delta", g_GSTGPSOffset}
}, satUTCTime(id)); }, satUTCTime(id));
} }
else if(wtype == 16) { // redced
idb.addValue(id, "redced", {{"deltaared", svstat.galmsg.deltaAred}, // CONVERSION XXX
{"exred", svstat.galmsg.a1g}, #if 0
{"eyred", svstat.galmsg.t0g}, for(auto& ent : g_svstats) {
{"deltai0red", svstat.galmsg.deltai0red}, // fmt::printf("%2d\t", ent.first);
{"omega0red", svstat.galmsg.omega0red}, id=ent.first;
{"lambda0red", svstat.galmsg.lambda0red}, if(ent.second.completeIOD() && ent.second.prevIOD.first >= 0) {
{"af0red", svstat.galmsg.af0red},
{"af1red", svstat.galmsg.af1red}, ent.second.clearPrev();
{"t0r", 1 + nmm.gi().gnsstow() - (nmm.gi().gnsstow() % 30)},
}, satUTCTime(id));
svstat.impinav = true;
svstat.impinavTime = nmm.gi().gnsstow();
} }
} }
else if(nmm.type() == NavMonMessage::GalileoCnavType) { #endif
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) {
auto fnav = makeVec((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;
auto& gm = svstat.galmsg;
unsigned int wtype = gm.parseFnav(fnav);
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);
}
}
}
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) { else if(nmm.type() == NavMonMessage::ObserverPositionType) {
g_srcfacts[nmm.sourceid()].lastSeen = nmm.localutcseconds(); g_srcpos[nmm.sourceid()].lastSeen = nmm.localutcseconds();
g_srcfacts[nmm.sourceid()].pos.x = nmm.op().x(); g_srcpos[nmm.sourceid()].pos.x = nmm.op().x();
g_srcfacts[nmm.sourceid()].pos.y = nmm.op().y(); g_srcpos[nmm.sourceid()].pos.y = nmm.op().y();
g_srcfacts[nmm.sourceid()].pos.z = nmm.op().z(); g_srcpos[nmm.sourceid()].pos.z = nmm.op().z();
if(nmm.op().has_groundspeed()) { if(nmm.op().has_groundspeed()) {
g_srcfacts[nmm.sourceid()].groundSpeed = nmm.op().groundspeed(); g_srcpos[nmm.sourceid()].groundSpeed = nmm.op().groundspeed();
} }
g_srcfacts[nmm.sourceid()].accuracy = nmm.op().acc(); g_srcpos[nmm.sourceid()].accuracy = nmm.op().acc();
// idb.addValueObserver(nmm.sourceid(), "accfix", nmm.op().acc(), nmm.localutcseconds()); // idb.addValueObserver(nmm.sourceid(), "accfix", nmm.op().acc(), nmm.localutcseconds());
auto latlonh = ecefToWGS84(nmm.op().x(), nmm.op().y(), nmm.op().z()); auto latlonh = ecefToWGS84(nmm.op().x(), nmm.op().y(), nmm.op().z());
@ -2354,7 +2137,7 @@ try
// the magic 14 is because 'rcvtow()' is in GPS/Galileo TOW // the magic 14 is because 'rcvtow()' is in GPS/Galileo TOW
// but BeiDou operates with 14 leap seconds less than GPS/Galileo // but BeiDou operates with 14 leap seconds less than GPS/Galileo
auto res = doDoppler(nmm.rfd().rcvtow()-14, g_srcfacts[nmm.sourceid()].pos, g_svstats[id].ephBeidoumsg, freq * 1000000); auto res = doDoppler(nmm.rfd().rcvtow()-14, g_srcpos[nmm.sourceid()].pos, g_svstats[id].ephBeidoumsg, freq * 1000000);
if(isnan(res.preddop)) { if(isnan(res.preddop)) {
cerr<<"Problem with doppler calculation for C"<<id.sv<<": "<<endl; cerr<<"Problem with doppler calculation for C"<<id.sv<<": "<<endl;
@ -2378,7 +2161,7 @@ try
idb.addValue(id, "correlator", idb.addValue(id, "correlator",
{{"delta_hz_cor", nmm.rfd().doppler() - res.preddop - (*corr)}, {{"delta_hz_cor", nmm.rfd().doppler() - res.preddop - (*corr)},
{"delta_hz", nmm.rfd().doppler() - res.preddop}, {"delta_hz", nmm.rfd().doppler() - res.preddop},
{"elevation", getElevationDeg(sat, g_srcfacts[nmm.sourceid()].pos)}, {"elevation", getElevationDeg(sat, g_srcpos[nmm.sourceid()].pos)},
{"hz", nmm.rfd().doppler()}, {"hz", nmm.rfd().doppler()},
{"prres", g_svstats[id].perrecv[nmm.sourceid()].prres}, {"prres", g_svstats[id].perrecv[nmm.sourceid()].prres},
{"qi", g_svstats[id].perrecv[nmm.sourceid()].qi}, {"qi", g_svstats[id].perrecv[nmm.sourceid()].qi},
@ -2396,7 +2179,7 @@ try
if(id.gnss == 2 && id.sigid == 5) // this is exactly the beidou b2i freq? if(id.gnss == 2 && id.sigid == 5) // this is exactly the beidou b2i freq?
freqMHZ = 1207.140; freqMHZ = 1207.140;
auto res = g_svstats[id].doDoppler(nmm.rfd().rcvtow(), g_srcfacts[nmm.sourceid()].pos,freqMHZ * 1000000); auto res = g_svstats[id].doDoppler(nmm.rfd().rcvtow(), g_srcpos[nmm.sourceid()].pos,freqMHZ * 1000000);
Point sat; Point sat;
g_svstats[id].getCoordinates(nmm.rfd().rcvtow(), &sat); g_svstats[id].getCoordinates(nmm.rfd().rcvtow(), &sat);
@ -2420,7 +2203,7 @@ try
{{"delta_hz_cor", nmm.rfd().doppler() - res.preddop - (*corr)}, {{"delta_hz_cor", nmm.rfd().doppler() - res.preddop - (*corr)},
{"delta_hz", nmm.rfd().doppler() - res.preddop}, {"delta_hz", nmm.rfd().doppler() - res.preddop},
{"hz", nmm.rfd().doppler()}, {"hz", nmm.rfd().doppler()},
{"elevation", getElevationDeg(sat, g_srcfacts[nmm.sourceid()].pos)}, {"elevation", getElevationDeg(sat, g_srcpos[nmm.sourceid()].pos)},
{"prres", g_svstats[id].perrecv[nmm.sourceid()].prres}, {"prres", g_svstats[id].perrecv[nmm.sourceid()].prres},
{"qi", g_svstats[id].perrecv[nmm.sourceid()].qi}, {"qi", g_svstats[id].perrecv[nmm.sourceid()].qi},
{"used", g_svstats[id].perrecv[nmm.sourceid()].used}, {"used", g_svstats[id].perrecv[nmm.sourceid()].used},
@ -2434,7 +2217,7 @@ try
} }
} }
else if(nmm.type()== NavMonMessage::ObserverDetailsType) { else if(nmm.type()== NavMonMessage::ObserverDetailsType) {
auto& o = g_srcfacts[nmm.sourceid()]; auto& o = g_srcpos[nmm.sourceid()];
o.serialno = nmm.od().serialno(); o.serialno = nmm.od().serialno();
o.swversion = nmm.od().swversion(); o.swversion = nmm.od().swversion();
@ -2505,16 +2288,15 @@ try
} }
else if(nmm.type()== NavMonMessage::DebuggingType) { else if(nmm.type()== NavMonMessage::DebuggingType) {
if(doGalileoReportSpeedup) // continue; // speedup
continue; // speedup
auto ret = parseTrkMeas(makeVec((const uint8_t*)nmm.dm().payload().c_str(), nmm.dm().payload().size())); auto ret = parseTrkMeas(basic_string<uint8_t>((const uint8_t*)nmm.dm().payload().c_str(), nmm.dm().payload().size()));
for(const auto& tss : ret) { for(const auto& tss : ret) {
SatID id{static_cast<uint32_t>(tss.gnss), static_cast<uint32_t>(tss.sv), tss.gnss == 2 ? 1u : 0u}; SatID id{static_cast<uint32_t>(tss.gnss), static_cast<uint32_t>(tss.sv), tss.gnss == 2 ? 1u : 0u};
if(g_svstats[id].completeIOD()) { if(g_svstats[id].completeIOD()) {
double freqMHZ = 1575.42; double freqMHZ = 1575.42;
double tsat = ldexp(1.0* tss.tr, -32) /1000.0; double tsat = ldexp(1.0* tss.tr, -32) /1000.0;
auto res = g_svstats[id].doDoppler(tsat, g_srcfacts[nmm.sourceid()].pos, freqMHZ * 1000000); auto res = g_svstats[id].doDoppler(tsat, g_srcpos[nmm.sourceid()].pos, freqMHZ * 1000000);
// idb.addValueObserver((int)nmm.sourceid(), "orbit", // idb.addValueObserver((int)nmm.sourceid(), "orbit",
@ -2540,7 +2322,7 @@ try
{{"delta_hz_cor", tss.dopplerHz - res.preddop - *corr}, {{"delta_hz_cor", tss.dopplerHz - res.preddop - *corr},
{"delta_hz", tss.dopplerHz - res.preddop}, {"delta_hz", tss.dopplerHz - res.preddop},
{"hz", tss.dopplerHz}, {"hz", tss.dopplerHz},
{"elevation", getElevationDeg(sat, g_srcfacts[nmm.sourceid()].pos)}, {"elevation", getElevationDeg(sat, g_srcpos[nmm.sourceid()].pos)},
{"prres", g_svstats[id].perrecv[nmm.sourceid()].prres}, {"prres", g_svstats[id].perrecv[nmm.sourceid()].prres},
{"qi", g_svstats[id].perrecv[nmm.sourceid()].qi}, {"qi", g_svstats[id].perrecv[nmm.sourceid()].qi},
{"used", g_svstats[id].perrecv[nmm.sourceid()].used}, {"used", g_svstats[id].perrecv[nmm.sourceid()].used},
@ -2561,7 +2343,7 @@ try
cout<<"ignoring sigid "<<nmm.gpsi().sigid()<<" for legacy GPS "<<nmm.gpsi().gnsssv()<<endl; cout<<"ignoring sigid "<<nmm.gpsi().sigid()<<" for legacy GPS "<<nmm.gpsi().gnsssv()<<endl;
continue; continue;
} }
auto cond = getCondensedGPSMessage(makeVec((uint8_t*)nmm.gpsi().contents().c_str(), nmm.gpsi().contents().size())); auto cond = getCondensedGPSMessage(std::basic_string<uint8_t>((uint8_t*)nmm.gpsi().contents().c_str(), nmm.gpsi().contents().size()));
SatID id{nmm.gpsi().gnssid(), nmm.gpsi().gnsssv(), nmm.gpsi().sigid()}; SatID id{nmm.gpsi().gnssid(), nmm.gpsi().gnsssv(), nmm.gpsi().sigid()};
g_svstats[id].perrecv[nmm.sourceid()].t = nmm.localutcseconds(); g_svstats[id].perrecv[nmm.sourceid()].t = nmm.localutcseconds();
@ -2600,22 +2382,8 @@ try
auto oldOffset = getGPSAtomicOffset(gm.tow, oldgm); auto oldOffset = getGPSAtomicOffset(gm.tow, oldgm);
auto newOffset = getGPSAtomicOffset(gm.tow, gm); auto newOffset = getGPSAtomicOffset(gm.tow, gm);
svstat.timeDisco = oldOffset.first - newOffset.first; svstat.timeDisco = oldOffset.first - newOffset.first;
if(fabs(svstat.timeDisco) < 10000) { if(fabs(svstat.timeDisco) < 10000)
idb.addValue(id, "clock_jump_ns", { idb.addValue(id, "clock_jump_ns", {{"value", svstat.timeDisco}}, satUTCTime(id));
{"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) { else if(frame==2) {
@ -2667,8 +2435,7 @@ try
if(rm.type == 1057 || rm.type == 1240) { if(rm.type == 1057 || rm.type == 1240) {
for(const auto& ed : rm.d_ephs) { for(const auto& ed : rm.d_ephs) {
auto iter = g_svstats.find(ed.id); auto iter = g_svstats.find(ed.id);
// XXX NAVCAST ONLY 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; iter->second.rtcmEphDelta = ed;
idb.addValue(ed.id, "rtcm-eph-correction", { idb.addValue(ed.id, "rtcm-eph-correction", {
@ -2694,7 +2461,7 @@ try
for(const auto& cd : rm.d_clocks) { for(const auto& cd : rm.d_clocks) {
auto iter = g_svstats.find(cd.id); auto iter = g_svstats.find(cd.id);
if(iter != g_svstats.end() && nmm.sourceid()==302) /// XXX wrong if(iter != g_svstats.end())
iter->second.rtcmClockDelta = cd; iter->second.rtcmClockDelta = cd;
idb.addValue(cd.id, "rtcm-clock-correction", { idb.addValue(cd.id, "rtcm-clock-correction", {
@ -2711,18 +2478,10 @@ 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) { else if(rm.type == 1060 || rm.type == 1243) {
for(const auto& ed : rm.d_ephs) { for(const auto& ed : rm.d_ephs) {
auto iter = g_svstats.find(ed.id); auto iter = g_svstats.find(ed.id);
if(iter != g_svstats.end() && iter->second.completeIOD() && iter->second.liveIOD().getIOD() == ed.iod && nmm.sourceid()==302) if(iter != g_svstats.end() && iter->second.completeIOD() && iter->second.liveIOD().getIOD() == ed.iod)
iter->second.rtcmEphDelta = ed; iter->second.rtcmEphDelta = ed;
idb.addValue(ed.id, "rtcm-eph-correction", { idb.addValue(ed.id, "rtcm-eph-correction", {
@ -2756,7 +2515,7 @@ try
id.sv = rm.d_sv; id.sv = rm.d_sv;
id.sigid = 6; // seems reasonable for E5a id.sigid = 6; // seems reasonable for E5a
static map<pair<int, int>, unsigned int> lastT0e; static map<pair<int, int>, int> lastT0e;
pair<int, int> key(nmm.sourceid(), rm.d_sv); pair<int, int> key(nmm.sourceid(), rm.d_sv);
if(!lastT0e.count(key) || lastT0e[key] != eg.t0e) { if(!lastT0e.count(key) || lastT0e[key] != eg.t0e) {
@ -2790,7 +2549,7 @@ try
for(const auto& cd : rm.d_clocks) { for(const auto& cd : rm.d_clocks) {
auto iter = g_svstats.find(cd.id); auto iter = g_svstats.find(cd.id);
if(iter != g_svstats.end() && nmm.sourceid()==302) if(iter != g_svstats.end())
iter->second.rtcmClockDelta = cd; iter->second.rtcmClockDelta = cd;
idb.addValue(cd.id, "rtcm-clock-correction", { idb.addValue(cd.id, "rtcm-clock-correction", {
@ -2818,7 +2577,7 @@ try
GPSCNavState gcns; GPSCNavState gcns;
parseGPSCNavMessage( parseGPSCNavMessage(
makeVec((uint8_t*)nmm.gpsc().contents().c_str(), std::basic_string<uint8_t>((uint8_t*)nmm.gpsc().contents().c_str(),
nmm.gpsc().contents().size()), nmm.gpsc().contents().size()),
gcns); gcns);
// cout<<"Got a message from "<<makeSatIDName(id)<<endl; // cout<<"Got a message from "<<makeSatIDName(id)<<endl;
@ -2829,9 +2588,7 @@ try
} }
else if(nmm.type()== NavMonMessage::BeidouInavTypeD1) { else if(nmm.type()== NavMonMessage::BeidouInavTypeD1) {
if(doGalileoReportSpeedup) // continue; // XXX speedup
continue; // speedup
try { try {
SatID id{nmm.bid1().gnssid(), nmm.bid1().gnsssv(), nmm.bid1().sigid()}; SatID id{nmm.bid1().gnssid(), nmm.bid1().gnsssv(), nmm.bid1().sigid()};
@ -2840,7 +2597,7 @@ try
auto& svstat = g_svstats[id]; auto& svstat = g_svstats[id];
svstat.gnss = id.gnss; svstat.gnss = id.gnss;
uint8_t pageno; uint8_t pageno;
auto cond = getCondensedBeidouMessage(makeVec((uint8_t*)nmm.bid1().contents().c_str(), nmm.bid1().contents().size())); auto cond = getCondensedBeidouMessage(std::basic_string<uint8_t>((uint8_t*)nmm.bid1().contents().c_str(), nmm.bid1().contents().size()));
auto& bm = svstat.beidoumsg; auto& bm = svstat.beidoumsg;
auto oldbm = bm; auto oldbm = bm;
int fraid=bm.parse(cond, &pageno); int fraid=bm.parse(cond, &pageno);
@ -2866,7 +2623,7 @@ try
auto newOffset = bm.getAtomicOffset(bm.sow); auto newOffset = bm.getAtomicOffset(bm.sow);
svstat.timeDisco = oldOffset.first - newOffset.first; svstat.timeDisco = oldOffset.first - newOffset.first;
if(fabs(svstat.timeDisco) < 10000) if(fabs(svstat.timeDisco) < 10000)
idb.addValue(id, "clock_jump_ns", {{"jump", svstat.timeDisco}}, satUTCTime(id)); idb.addValue(id, "clock_jump_ns", {{"value", svstat.timeDisco}}, satUTCTime(id));
} }
svstat.lastBeidouMessage1 = bm; svstat.lastBeidouMessage1 = bm;
} }
@ -2916,7 +2673,7 @@ try
} }
} }
else if(nmm.type()== NavMonMessage::BeidouInavTypeD2) { else if(nmm.type()== NavMonMessage::BeidouInavTypeD2) {
auto cond = getCondensedBeidouMessage(makeVec((uint8_t*)nmm.bid2().contents().c_str(), nmm.bid2().contents().size())); auto cond = getCondensedBeidouMessage(std::basic_string<uint8_t>((uint8_t*)nmm.bid2().contents().c_str(), nmm.bid2().contents().size()));
/* /*
int fraid = getbitu(&cond[0], beidouBitconv(16), 3); int fraid = getbitu(&cond[0], beidouBitconv(16), 3);
int sow = getbitu(&cond[0], beidouBitconv(19), 20); int sow = getbitu(&cond[0], beidouBitconv(19), 20);
@ -2930,15 +2687,13 @@ try
*/ */
} }
else if(nmm.type()== NavMonMessage::GlonassInavType) { else if(nmm.type()== NavMonMessage::GlonassInavType) {
if(doGalileoReportSpeedup) // continue; // XXX speedup
continue; // speedup
SatID id{nmm.gloi().gnssid(), nmm.gloi().gnsssv(), nmm.gloi().sigid()}; SatID id{nmm.gloi().gnssid(), nmm.gloi().gnsssv(), nmm.gloi().sigid()};
auto& svstat = g_svstats[id]; auto& svstat = g_svstats[id];
svstat.gnss = id.gnss; svstat.gnss = id.gnss;
auto& gm = svstat.glonassMessage; auto& gm = svstat.glonassMessage;
auto oldgm = gm; auto oldgm = gm;
int strno = gm.parse(makeVec((uint8_t*)nmm.gloi().contents().c_str(), nmm.gloi().contents().size())); int strno = gm.parse(std::basic_string<uint8_t>((uint8_t*)nmm.gloi().contents().c_str(), nmm.gloi().contents().size()));
g_svstats[id].perrecv[nmm.sourceid()].t = nmm.localutcseconds(); g_svstats[id].perrecv[nmm.sourceid()].t = nmm.localutcseconds();
if(strno == 1 && gm.n4 != 0 && gm.NT !=0) { if(strno == 1 && gm.n4 != 0 && gm.NT !=0) {
// uint32_t glotime = gm.getGloTime(); // this starts GLONASS time at 31st of december 1995, 00:00 UTC // uint32_t glotime = gm.getGloTime(); // this starts GLONASS time at 31st of december 1995, 00:00 UTC
@ -2964,7 +2719,7 @@ try
if(oldgm.taun && oldgm.taun != gm.taun) { if(oldgm.taun && oldgm.taun != gm.taun) {
if(gm.getGloTime() - oldgm.getGloTime() < 300) { if(gm.getGloTime() - oldgm.getGloTime() < 300) {
svstat.timeDisco = gm.getTaunNS() - oldgm.getTaunNS(); svstat.timeDisco = gm.getTaunNS() - oldgm.getTaunNS();
idb.addValue(id, "clock_jump_ns", {{"jump", svstat.timeDisco}}, satUTCTime(id)); idb.addValue(id, "clock_jump_ns", {{"value", svstat.timeDisco}}, satUTCTime(id));
} }
} }
} }
@ -2999,14 +2754,12 @@ try
// cout<<"GLONASS R"<<id.second<<" str "<<strno<<endl; // cout<<"GLONASS R"<<id.second<<" str "<<strno<<endl;
} }
else if(nmm.type() == NavMonMessage::SBASMessageType) { else if(nmm.type() == NavMonMessage::SBASMessageType) {
if(doGalileoReportSpeedup) // continue; // XXX speedup
continue; // speedup
auto& sb = g_sbas[nmm.sbm().gnsssv()]; auto& sb = g_sbas[nmm.sbm().gnsssv()];
sb.perrecv[nmm.sourceid()].last_seen = nmm.localutcseconds(); sb.perrecv[nmm.sourceid()].last_seen = nmm.localutcseconds();
auto sbas = makeVec((uint8_t*)nmm.sbm().contents().c_str(), nmm.sbm().contents().length()); basic_string<uint8_t> sbas((uint8_t*)nmm.sbm().contents().c_str(), nmm.sbm().contents().length());
auto delta = sb.status.parse(sbas, nmm.localutcseconds()); auto delta = sb.status.parse(sbas, nmm.localutcseconds());
// fast correction - clogs the database, so dropping that for now // fast correction - clogs the database, so dropping that for now
/* /*
@ -3148,33 +2901,3 @@ catch(std::exception& e)
{ {
cerr<<"Exiting because of fatal error "<<e.what()<<endl; 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

View file

@ -49,12 +49,6 @@ struct SVStat
GalileoMessage ephgalmsg, galmsg, oldephgalmsg; GalileoMessage ephgalmsg, galmsg, oldephgalmsg;
// internal // internal
map<int, GalileoMessage> galmsgTyped; map<int, GalileoMessage> galmsgTyped;
bool osnma{false};
time_t osnmaTime{0};
bool impinav{false};
time_t impinavTime{0};
// Glonass // Glonass
GlonassMessage ephglomsg, glonassMessage, oldephglomsg; GlonassMessage ephglomsg, glonassMessage, oldephglomsg;

View file

@ -16,7 +16,6 @@
#include "version.hh" #include "version.hh"
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include "navmon.hh" #include "navmon.hh"
#include <mutex>
static char program[]="navrecv"; static char program[]="navrecv";
@ -104,6 +103,8 @@ int getfd(const char* path, int mode, int permission)
fds.erase(fds.begin(), end); fds.erase(fds.begin(), end);
} }
FileID fid({path, mode, permission}); FileID fid({path, mode, permission});
// cout<<"Request for "<<path<<endl; // cout<<"Request for "<<path<<endl;
auto iter = fds.find(fid); auto iter = fds.find(fid);
@ -119,7 +120,7 @@ int getfd(const char* path, int mode, int permission)
if(fd < 0) { if(fd < 0) {
throw FatalException("Unable to open file for storage: "+string(strerror(errno))); throw FatalException("Unable to open file for storage: "+string(strerror(errno)));
} }
// cout<<"Opened fd "<<fd<<" for path "<<path<<endl; cout<<"Opened fd "<<fd<<" for path "<<path<<endl;
fds.emplace(fid, FDID(fd)); fds.emplace(fid, FDID(fd));
return fd; return fd;
} }
@ -139,97 +140,11 @@ 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 // note that this moves the socket
void recvSession2(Socket&& uns, ComboAddress client, ClientKeeper::Sentinel& sentinel) void recvSession2(Socket&& uns, ComboAddress client)
{ {
string secret = SRead(uns, 8); // ignored for now string secret = SRead(uns, 8); // ignored for now
cerr << client.toStringWithPort()<< " Entering compressed session"<<endl; cerr << "Entering compressed session for "<<client.toStringWithPort()<<endl;
ZStdReader zsr(uns); ZStdReader zsr(uns);
int s = zsr.getFD(); int s = zsr.getFD();
// time_t start = time(0); // time_t start = time(0);
@ -240,7 +155,7 @@ void recvSession2(Socket&& uns, ComboAddress client, ClientKeeper::Sentinel& sen
// sleep(10); // sleep(10);
string num=SRead(s, 4); string num=SRead(s, 4);
if(num.empty()) { if(num.empty()) {
cerr<<client.toStringWithPort()<<" EOF"<<endl; cerr<<"EOF from "<<client.toStringWithPort()<<endl;
break; break;
} }
string out="bert"; string out="bert";
@ -262,14 +177,14 @@ void recvSession2(Socket&& uns, ComboAddress client, ClientKeeper::Sentinel& sen
memcpy(&denum, num.c_str(), 4); memcpy(&denum, num.c_str(), 4);
denum = htonl(denum); denum = htonl(denum);
// cerr<<"Received message "<<denum<< " "<<nmm.localutcseconds()<<" " << nmm.localutcnanoseconds()/1000000000.0<<endl; // cerr<<"Received message "<<denum<< " "<<nmm.localutcseconds()<<" " << nmm.localutcnanoseconds()/1000000000.0<<endl;
sentinel.update(nmm.sourceid(), false);
writeToDisk(nmm.localutcseconds(), nmm.sourceid(), out); writeToDisk(nmm.localutcseconds(), nmm.sourceid(), out);
if(first) { if(first) {
cerr<<client.toStringWithPort() <<" station: "<<nmm.sourceid() << endl; cerr<<"\tstation: "<<nmm.sourceid() << endl;
first=false; first=false;
} }
#ifdef __linux__ #ifdef __linux__
SSetsockopt(uns, IPPROTO_TCP, TCP_CORK, 1 ); SSetsockopt(uns, IPPROTO_TCP, TCP_CORK, 1 );
#endif #endif
@ -284,21 +199,17 @@ void recvSession(int s, ComboAddress client)
try { try {
Socket sock(s); // this closes on destruction Socket sock(s); // this closes on destruction
SSetsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 1); // saves file descriptors SSetsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 1); // saves file descriptors
cerr<<client.toStringWithPort()<<" New connection\n"; cerr<<"Receiving messages from "<<client.toStringWithPort()<<endl;
cerr.flush();
bool first=true; bool first=true;
ClientKeeper::Sentinel sentinel=g_ckeeper.reportClient(client);
for(int count=0;;++count) { for(int count=0;;++count) {
string part=SRead(sock, 4); string part=SRead(sock, 4);
if(part.empty()) { if(part.empty()) {
cerr<<client.toStringWithPort()<<" EOF"<<endl; cerr<<"EOF from "<<client.toStringWithPort()<<endl;
break; break;
} }
if(part != "bert") { if(part != "bert") {
if(part == "RNIE") if(part == "RNIE")
return recvSession2(std::move(sock), client, sentinel); // protocol v2, socket is moved cuz cleanup is special return recvSession2(std::move(sock), client); // protocol v2, socket is moved cuz cleanup is special
cerr << "Message "<<count<<", wrong magic from "<<client.toStringWithPort()<<": "<<makeHexDump(part)<<endl; cerr << "Message "<<count<<", wrong magic from "<<client.toStringWithPort()<<": "<<makeHexDump(part)<<endl;
break; break;
} }
@ -314,24 +225,22 @@ void recvSession(int s, ComboAddress client)
part = SRead(s, len); part = SRead(s, len);
if(part.size() != len) { if(part.size() != len) {
cerr<<"Mismatch, "<<part.size()<<", len "<<len<<endl; cerr<<"Mismatch, "<<part.size()<<", len "<<len<<endl;
// XX AND THEN WHAT??
} }
out += part; out += part;
NavMonMessage nmm; NavMonMessage nmm;
nmm.ParseFromString(part); nmm.ParseFromString(part);
if(first) { if(first) {
cerr<<client.toStringWithPort()<<" station "<<nmm.sourceid() << endl; cerr<<"\tstation: "<<nmm.sourceid() << endl;
first=false; first=false;
} }
sentinel.update(nmm.sourceid(), true);
writeToDisk(nmm.localutcseconds(), nmm.sourceid(), out); writeToDisk(nmm.localutcseconds(), nmm.sourceid(), out);
} }
} }
catch(std::exception& e) { catch(std::exception& e) {
cout<<client.toStringWithPort()<<" error in receiving thread: "<<e.what()<<endl; cout<<"Error in receiving thread: "<<e.what()<<endl;
} }
cout<<client.toStringWithPort()<< " thread exiting"<<endl; cout<<"Thread for "<<client.toStringWithPort()<< " exiting"<<endl;
} }
void recvListener(Socket&& s, ComboAddress local) void recvListener(Socket&& s, ComboAddress local)
@ -377,9 +286,7 @@ int main(int argc, char** argv)
thread recvThread(recvListener, std::move(receiver), recvaddr); thread recvThread(recvListener, std::move(receiver), recvaddr);
recvThread.detach(); recvThread.detach();
sleep(5);
for(;;) { for(;;) {
g_ckeeper.dump(); sleep(1);
sleep(10);
} }
} }

View file

@ -1,13 +1,60 @@
#include "nmmsender.hh" #include "nmmsender.hh"
#include "comboaddress.hh"
#include "swrappers.hh"
#include "sclasses.hh"
#include <random> #include <random>
#include "navmon.hh" #include "navmon.hh"
#include <algorithm> #include <algorithm>
#include "zstdwrap.hh"
#include <netinet/tcp.h> #include <netinet/tcp.h>
using namespace std; using namespace std;
void NMMSender::sendLoop(Destination* d, SocketCommunicator& sc, std::unique_ptr<ZStdCompressor>& zsc, Socket& s, map<uint32_t, string>& unacked, time_t connStartTime) void NMMSender::sendTCPThread(Destination* d)
{ {
struct NameError{};
for(;;) {
ComboAddress chosen;
map<uint32_t, string> unacked;
try {
vector<ComboAddress> addrs;
for(;;) {
addrs=resolveName(d->dst, true, true);
if(!addrs.empty())
break;
cerr<<humanTimeNow()<<" Unable to resolve "<<d->dst<<", sleeping and trying again later"<<endl;
throw NameError();
}
std::random_device rng;
std::mt19937 urng(rng());
std::shuffle(addrs.begin(), addrs.end(), urng);
for(auto& addr: addrs) {
if(!addr.sin4.sin_port)
addr.sin4.sin_port = ntohs(29603);
chosen=addr;
Socket s(addr.sin4.sin_family, SOCK_STREAM);
SocketCommunicator sc(s);
sc.setTimeout(3);
sc.connect(addr);
#if !defined(TCP_CORK) && defined(TCP_NOPUSH)
/* start off "buffering" */
SSetsockopt(s, IPPROTO_TCP, TCP_NOPUSH, 1 );
#endif
time_t connStartTime = time(0);
if (d_debug) { cerr<<humanTimeNow()<<" Connected to "<<d->dst<<" on "<<addr.toStringWithPort()<<endl; }
auto emit = [&sc](const char*buf, uint32_t len) {
sc.writen(string(buf, len));
};
std::unique_ptr<ZStdCompressor> zsc;
if(d_compress) {
sc.writen("RNIE00000000"); // the other magic value is "bert". hence.
// the 00000000 is a placeholder for a "secret" we might implement later
zsc = std::make_unique<ZStdCompressor>(emit, 9);
}
bool hadMessage=false; bool hadMessage=false;
int msgnum = 0; int msgnum = 0;
@ -41,6 +88,7 @@ void NMMSender::sendLoop(Destination* d, SocketCommunicator& sc, std::unique_ptr
if(!msg.empty()) { if(!msg.empty()) {
hadMessage=true; hadMessage=true;
if(zsc) { if(zsc) {
uint32_t num = htonl(msgnum); uint32_t num = htonl(msgnum);
string encap((const char*)&num, 4); string encap((const char*)&num, 4);
encap += msg; encap += msg;
@ -89,58 +137,6 @@ void NMMSender::sendLoop(Destination* d, SocketCommunicator& sc, std::unique_ptr
} }
} }
}
// this does all kinds of resolving based on a *string* destination
void NMMSender::sendTCPThread(Destination* d)
{
struct NameError{};
for(;;) {
ComboAddress chosen;
map<uint32_t, string> unacked;
try {
vector<ComboAddress> addrs;
for(;;) {
addrs=resolveName(d->dst, true, true);
if(!addrs.empty())
break;
cerr<<humanTimeNow()<<" Unable to resolve "<<d->dst<<", sleeping and trying again later"<<endl;
throw NameError();
}
std::random_device rng;
std::mt19937 urng(rng());
std::shuffle(addrs.begin(), addrs.end(), urng);
for(auto& addr: addrs) {
if(!addr.sin4.sin_port)
addr.sin4.sin_port = ntohs(29603);
chosen=addr;
Socket s(addr.sin4.sin_family, SOCK_STREAM);
SocketCommunicator sc(s);
sc.setTimeout(3);
sc.connect(addr);
#if !defined(TCP_CORK) && defined(TCP_NOPUSH)
/* start off "buffering" */
SSetsockopt(s, IPPROTO_TCP, TCP_NOPUSH, 1 );
#endif
time_t connStartTime = time(0);
if (d_debug) { cerr<<humanTimeNow()<<" Connected to "<<d->dst<<" on "<<addr.toStringWithPort()<<endl; }
auto emit = [&sc](const char*buf, uint32_t len) {
sc.writen(string(buf, len));
};
std::unique_ptr<ZStdCompressor> zsc;
if(d_compress) {
sc.writen("RNIE00000000"); // the other magic value is "bert". hence.
// the 00000000 is a placeholder for a "secret" we might implement later
zsc = std::make_unique<ZStdCompressor>(emit, 9);
}
sendLoop(d, sc, zsc, s, unacked, connStartTime);
} }
} }
catch(NameError&) { catch(NameError&) {
@ -179,142 +175,25 @@ void NMMSender::sendTCPThread(Destination* d)
void NMMSender::emitNMM(const NavMonMessage& nmm) void NMMSender::emitNMM(const NavMonMessage& nmm)
{
for(auto& d : d_dests) {
d->emitNMM(nmm, d_compress);
}
}
void NMMSender::Destination::emitNMM(const NavMonMessage& nmm, bool compressed)
{ {
string out; string out;
nmm.SerializeToString(& out); nmm.SerializeToString(& out);
emitNMM(out);
}
void NMMSender::emitNMM(const std::string& out)
{
std::lock_guard<std::mutex> l(d_destslock);
for(auto& d : d_dests) {
d->emitNMM(out, d_compress);
}
}
/* the listener design. The listener has a thread that waits for connections.
the listener is a normal 'destination'.
It consumes its queue, and forwards messages to any connections made to it.
*/
void NMMSender::acceptorThread(Destination *d)
try
{
cerr<<"Start of acceptor thread"<<endl;
ComboAddress ca(d->dst);
Socket l(ca.sin4.sin_family, SOCK_STREAM);
SSetsockopt(l, SOL_SOCKET, SO_REUSEADDR, 1 );
SBind(l, ca);
SListen(l, 128);
cerr<<"Made a listener on "<<ca.toStringWithPort()<<endl;
NMMSender ns;
std::thread t(&NMMSender::forwarderThread, this, d, &ns);
t.detach();
for(;;) {
ComboAddress remote=ca;
int fd = SAccept(l, remote);
cout<<"Had a new connection from "<<remote.toStringWithPort()<<" on fd "<<fd<<endl;
auto nd = std::make_unique<Destination>();
nd->dst="source";
std::lock_guard<std::mutex> l(ns.d_destslock);
ns.d_dests.push_back(std::move(nd));
std::thread t(&NMMSender::sendTCPListenerThread, &ns, ns.d_dests.rbegin()->get(), fd, remote);
t.detach();
}
}
catch(std::exception& e) {
cerr<<"Acceptor thread dying: "<<e.what()<<endl;
}
void NMMSender::forwarderThread(Destination *d, NMMSender* there)
{
// cout<<"Forwarder thread launched, this " << (void*)this<<" -> "<<(void*)there<<endl;
std::string msg;
for(;;) {
{
std::lock_guard<std::mutex> mut(d->mut);
while(!d->queue.empty()) {
// cerr<<"Forwarded a message to "<< (void*)there<<endl;
msg = d->queue.front();
there->emitNMM(msg);
d->queue.pop_front();
}
}
usleep(100000);
}
}
void NMMSender::sendTCPListenerThread(Destination* d, int fd, ComboAddress addr)
{
cerr<<"sendTCPListenerThread launched on fd "<<fd<<" for "<<addr.toStringWithPort()<<", d_compress "<<d_compress<<endl;
try {
Socket s(fd);
SocketCommunicator sc(s);
time_t connStartTime = time(0);
if (d_debug) { cerr<<humanTimeNow()<<" Connected to "<<d->dst<<" on "<<addr.toStringWithPort()<<endl; }
auto emit = [&sc](const char*buf, uint32_t len) {
sc.writen(string(buf, len));
};
std::unique_ptr<ZStdCompressor> zsc;
if(d_compress) {
sc.writen("RNIE00000000"); // the other magic value is "bert". hence.
// the 00000000 is a placeholder for a "secret" we might implement later
zsc = std::make_unique<ZStdCompressor>(emit, 9);
}
map<uint32_t, string> unacked;
// cerr<<"Entering sendloop"<<endl;
sendLoop(d, sc, zsc, s, unacked, connStartTime);
}
catch(std::exception& e) {
if (d_debug) { cerr<<humanTimeNow()<<" Sending thread for "<<d->dst<<" via "<<addr.toStringWithPort()<<" had error: "<<e.what()<<endl; }
}
catch(...) {
if (d_debug) { cerr<<humanTimeNow()<<" Sending thread for "<<d->dst <<" via "<< addr.toStringWithPort()<<" had error"; }
}
std::lock_guard<std::mutex> l(d_destslock);
d_dests.erase(remove_if(d_dests.begin(), d_dests.end(), [d](const auto& a)
{
// cerr<<(void*) a.get()<< " ==? " <<(void*) d <<endl;
return a.get() == d;
}), d_dests.end());
cerr<<"Done with serving client "<<addr.toStringWithPort()<<": "<<d_dests.size() <<" destinations left"<<endl;
// cerr<<"Size now: "<<d_dests.size()<<endl;
// some kind of cleanup
}
void NMMSender::Destination::emitNMM(const std::string& out, bool compressed)
{
string msg; string msg;
// this bit is exceptionally tricky. We support multiple output formats
// and somehow we do work on that here. This is very stupid.
if(!listener) {
if(dst.empty() || !compressed) if(dst.empty() || !compressed)
msg="bert"; msg="bert";
uint16_t len = htons(out.size()); uint16_t len = htons(out.size());
msg.append((char*)&len, 2); msg.append((char*)&len, 2);
}
msg.append(out); msg.append(out);
if(!dst.empty() || listener) { if(!dst.empty()) {
std::lock_guard<std::mutex> l(mut); std::lock_guard<std::mutex> l(mut);
queue.push_back(msg); queue.push_back(msg);
} }

View file

@ -1,15 +1,10 @@
#pragma once #pragma once
#include <string> #include <string>
#include <deque> #include <deque>
#include <map>
#include <atomic> #include <atomic>
#include "navmon.pb.h" #include "navmon.pb.h"
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include "zstdwrap.hh"
#include "comboaddress.hh"
#include "swrappers.hh"
#include "sclasses.hh"
class NMMSender class NMMSender
{ {
@ -18,11 +13,10 @@ class NMMSender
int fd{-1}; int fd{-1};
std::string dst; std::string dst;
std::string fname; std::string fname;
bool listener{false};
std::deque<std::string> queue; std::deque<std::string> queue;
std::mutex mut; std::mutex mut;
void emitNMM(const std::string& out, bool compress); void emitNMM(const NavMonMessage& nmm, bool compress);
std::vector<Destination> clients;
}; };
public: public:
@ -30,44 +24,27 @@ public:
{ {
auto d = std::make_unique<Destination>(); auto d = std::make_unique<Destination>();
d->fd = fd; d->fd = fd;
std::lock_guard<std::mutex> l(d_destslock);
d_dests.push_back(std::move(d)); d_dests.push_back(std::move(d));
} }
void addDestination(const std::string& dest) void addDestination(const std::string& dest)
{ {
auto d = std::make_unique<Destination>(); auto d = std::make_unique<Destination>();
d->dst = dest; d->dst = dest;
std::lock_guard<std::mutex> l(d_destslock);
d_dests.push_back(std::move(d));
}
void addListener(const std::string& dest)
{
auto d = std::make_unique<Destination>();
d->dst = dest;
d->listener = true;
std::lock_guard<std::mutex> l(d_destslock);
d_dests.push_back(std::move(d)); d_dests.push_back(std::move(d));
} }
void launch() void launch()
{ {
for(auto& d : d_dests) { for(auto& d : d_dests) {
if(d->listener) { if(!d->dst.empty()) {
d_thread.emplace_back(std::move(std::make_unique<std::thread>(&NMMSender::acceptorThread, this, d.get())));
}
else if(!d->dst.empty()) {
d_thread.emplace_back(std::move(std::make_unique<std::thread>(&NMMSender::sendTCPThread, this, d.get()))); d_thread.emplace_back(std::move(std::make_unique<std::thread>(&NMMSender::sendTCPThread, this, d.get())));
} }
} }
} }
void sendTCPThread(Destination* d); void sendTCPThread(Destination* d);
void acceptorThread(Destination* d);
void forwarderThread(Destination* d, NMMSender* there);
void sendTCPListenerThread(Destination* d, int fd, ComboAddress remote);
void sendLoop(Destination* d, SocketCommunicator& sc, std::unique_ptr<ZStdCompressor>& zsc, Socket& s, std::map<uint32_t, std::string>& unacked, time_t connStartTime);
void emitNMM(const NavMonMessage& nmm); void emitNMM(const NavMonMessage& nmm);
void emitNMM(const std::string& out);
bool d_debug{false}; bool d_debug{false};
bool d_compress{false}; // set BEFORE launch bool d_compress{false}; // set BEFORE launch
bool d_pleaseQuit{false}; bool d_pleaseQuit{false};
@ -81,7 +58,6 @@ public:
} }
private: private:
std::mutex d_destslock;
std::vector<std::unique_ptr<Destination>> d_dests; std::vector<std::unique_ptr<Destination>> d_dests;
std::vector<std::unique_ptr<std::thread>> d_thread; std::vector<std::unique_ptr<std::thread>> d_thread;
}; };

View file

@ -84,8 +84,6 @@ private:
struct IntervalStat struct IntervalStat
{ {
std::optional<int> unhealthy; std::optional<int> unhealthy;
std::optional<int> dataunhealthy;
std::optional<int> osnma;
std::optional<int> sisa; std::optional<int> sisa;
bool ripe{false}; bool ripe{false};
bool expired{false}; bool expired{false};
@ -97,7 +95,6 @@ struct IntervalStat
map<SatID, map<time_t,IntervalStat>> g_stats; map<SatID, map<time_t,IntervalStat>> g_stats;
int main(int argc, char **argv) int main(int argc, char **argv)
try
{ {
MiniCurl mc; MiniCurl mc;
MiniCurl::MiniCurlHeaders mch; MiniCurl::MiniCurlHeaders mch;
@ -114,19 +111,15 @@ try
string sp3src("default"); string sp3src("default");
int gnssid=2; int gnssid=2;
int rtcmsrc=300; 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_flag("--version", doVERSION, "show program version and copyright");
app.add_option("--period,-p", periodarg, "period over which to report (1h, 1w)"); app.add_option("--period,-p", periodarg, "period over which to report (1h, 1w)");
app.add_option("--begin,-b", beginarg, "Beginning"); app.add_option("--begin,-b", beginarg, "Beginning");
app.add_option("--end,-e", endarg, "End"); 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("--sp3src", sp3src, "Identifier of SP3 source");
app.add_option("--rtcmsrc", rtcmsrc, "Identifier of RTCM 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("--sigid,-s", sigid, "Signal identifier. 1 or 5 for Galileo.");
app.add_option("--gnssid,-g", gnssid, "gnssid, 0 GPS, 2 Galileo"); app.add_option("--gnssid,-g", gnssid, "gnssid, 0 GPS, 2 Galileo");
app.add_option("--influxdb", influxDBName, "Name of influxdb database"); app.add_option("--influxdb", influxDBName, "Name of influxdb database");
app.add_option("--influxserver", influxserver, "Address of influx server");
try { try {
app.parse(argc, argv); app.parse(argc, argv);
} catch(const CLI::Error &e) { } catch(const CLI::Error &e) {
@ -138,11 +131,7 @@ try
exit(0); exit(0);
} }
if(galwn>= 0) { if(beginarg.empty() && endarg.empty())
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; period = "time > now() - "+periodarg;
else { else {
period = "time > '"+ beginarg +"' and time <= '" + endarg +"'"; period = "time > '"+ beginarg +"' and time <= '" + endarg +"'";
@ -153,16 +142,13 @@ try
// 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)")); // 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)"));
if(influxserver.find("http"))
influxserver="http://"+influxserver;
if(influxserver.empty() || influxserver[influxserver.size()-1]!='/') string url="http://127.0.0.1:8086/query?db="+influxDBName+"&epoch=s&q=";
influxserver+="/";
string url=influxserver+"query?db="+influxDBName+"&epoch=s&q=";
string sisaname = (gnssid==2) ? "sisa" : "gpsura"; 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)"; 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<<"query: "<<query<<endl;
cout<<"url: "<<(url + mc.urlEncode(query))<<endl;
auto res = mc.getURL(url + mc.urlEncode(query)); auto res = mc.getURL(url + mc.urlEncode(query));
auto j = nlohmann::json::parse(res); auto j = nlohmann::json::parse(res);
@ -188,44 +174,13 @@ try
const auto& tags=sv["tags"]; 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"])}; 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"]) { for(const auto& v : sv["values"]) {
auto healthy = (int)v[1]; auto healthy = (int)v[1];
g_stats[id][(int)v[0]].unhealthy = healthy; // hngg 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)")); 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); j = nlohmann::json::parse(res);
for(const auto& sv : j["results"][0]["series"]) { for(const auto& sv : j["results"][0]["series"]) {
@ -429,7 +384,7 @@ try
} }
ofstream csv("sp3.csv"); ofstream csv("sp3.csv");
csv<<"timestamp gnss sv sigid zerror clkoffset"<<endl; csv<<"timestamp gnss sv sigid zerror"<<endl;
InfluxPusher idb(influxDBName); InfluxPusher idb(influxDBName);
@ -484,7 +439,7 @@ try
// cout<<" z-error: "<<dir.inner(v); // cout<<" z-error: "<<dir.inner(v);
csv << e.first << " " << id.gnss <<" " << id.sv << " " << id.sigid <<" " << dir.inner(v) << " " << clkoffset<<endl; csv << e.first << " " << id.gnss <<" " << id.sv << " " << id.sigid <<" " << dir.inner(v) << endl;
idb.addValue({{"gnssid", id.gnss}, {"sv", id.sv}, {"sp3src", sp3src}}, idb.addValue({{"gnssid", id.gnss}, {"sv", id.sv}, {"sp3src", sp3src}},
"sp3delta", "sp3delta",
{{"ecef-dx", v.x}, {"ecef-dy", v.y}, {"ecef-dz", v.z}, {"sv-dz", dir.inner(v)}, {"dclock", clkoffset}, {{"ecef-dx", v.x}, {"ecef-dy", v.y}, {"ecef-dz", v.z}, {"sv-dz", dir.inner(v)}, {"dclock", clkoffset},
@ -534,7 +489,7 @@ try
// cout<<" z-error: "<<dir.inner(v); // cout<<" z-error: "<<dir.inner(v);
csv << e.first << " " << id.gnss <<" " << id.sv << " " << id.sigid <<" " << dir.inner(v) << " " << clkoffset << endl; csv << e.first << " " << id.gnss <<" " << id.sv << " " << id.sigid <<" " << dir.inner(v) << endl;
idb.addValue({{"gnssid", id.gnss}, {"sv", id.sv}, {"sp3src", sp3src}}, idb.addValue({{"gnssid", id.gnss}, {"sv", id.sv}, {"sp3src", sp3src}},
"sp3delta", "sp3delta",
{{"ecef-dx", v.x}, {"ecef-dy", v.y}, {"ecef-dz", v.z}, {"sv-dz", dir.inner(v)}, {"dclock", clkoffset}, {{"ecef-dx", v.x}, {"ecef-dy", v.y}, {"ecef-dz", v.z}, {"sv-dz", dir.inner(v)}, {"dclock", clkoffset},
@ -548,51 +503,13 @@ try
} }
} }
/////
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}]; g_stats[{2,19,1}];
*/ */
@ -651,10 +568,7 @@ try
else if(*i.second.unhealthy==3) else if(*i.second.unhealthy==3)
testing++; testing++;
else { else {
if(i.second.dataunhealthy && *i.second.dataunhealthy) { // this is 'working without guarantee' if(i.second.sisa) {
unhealthy++;
}
else if(i.second.sisa) {
if(*i.second.sisa == 255) if(*i.second.sisa == 255)
napa++; napa++;
else else
@ -723,9 +637,6 @@ try
cout<<endl; cout<<endl;
} }
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", 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(), 100.0*(totunobserved)/maxintervals/g_stats.size(),
@ -736,7 +647,7 @@ try
100.0*totripe/maxintervals/g_stats.size(), 100.0*totripe/maxintervals/g_stats.size(),
100.0*totexpired/maxintervals/g_stats.size()); 100.0*totexpired/maxintervals/g_stats.size());
texstream<<fmt::sprintf("\\hline\nTot & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%%\\\\", texstream<<fmt::sprintf("Tot & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%% & %6.2f\\%%\\\\",
100.0*(totunobserved)/maxintervals/g_stats.size(), 100.0*(totunobserved)/maxintervals/g_stats.size(),
100.0*totunhealthy/maxintervals/g_stats.size(), 100.0*totunhealthy/maxintervals/g_stats.size(),
100.0*tothealthy/maxintervals/g_stats.size(), 100.0*tothealthy/maxintervals/g_stats.size(),
@ -753,9 +664,5 @@ try
cout<<endl; cout<<endl;
} }
catch(exception& e)
{
cerr<<"Fatal error: "<<e.what()<<endl;
return EXIT_FAILURE;
}

View file

@ -156,9 +156,6 @@ G02 2019 12 16 00 00 00-3.670863807201E-04-7.389644451905E-12 0.000000000000E+00
for(int n=1 ; n < 7; ++n) { for(int n=1 ; n < 7; ++n) {
if(!gzgets(d_fp, line, sizeof(line))) if(!gzgets(d_fp, line, sizeof(line)))
return false; return false;
if(n==1) {
entry.iodnav = getRINEXValue(line, 4);
}
if(n==3) { if(n==3) {
double toe = getRINEXValue(line, 4); double toe = getRINEXValue(line, 4);
entry.toe = toe; entry.toe = toe;

View file

@ -18,7 +18,6 @@ struct RINEXEntry
int health; int health;
int toe; int toe;
int tow; int tow;
int iodnav;
double af0, af1, af2; double af0, af1, af2;
double clkflags; double clkflags;
double BGDE1E5a, BGDE1E5b; double BGDE1E5a, BGDE1E5b;

View file

@ -9,7 +9,6 @@ struct Value
optional<int> af0Inav; optional<int> af0Inav;
optional<int> af0Fnav; optional<int> af0Fnav;
int af1; int af1;
int iod;
optional<int> BGDE1E5a; optional<int> BGDE1E5a;
optional<int> BGDE1E5b; optional<int> BGDE1E5b;
}; };
@ -31,7 +30,6 @@ int main(int argc, char** argv)
s.af1 = rint(ldexp(e.af1,46)); s.af1 = rint(ldexp(e.af1,46));
s.BGDE1E5a = rint(ldexp(e.BGDE1E5a,32)); s.BGDE1E5a = rint(ldexp(e.BGDE1E5a,32));
s.BGDE1E5b = rint(ldexp(e.BGDE1E5b,32)); s.BGDE1E5b = rint(ldexp(e.BGDE1E5b,32));
s.iod = e.iodnav;
} }
else { else {
s.af0Fnav = rint(ldexp(e.af0,34)); s.af0Fnav = rint(ldexp(e.af0,34));
@ -43,10 +41,10 @@ int main(int argc, char** argv)
} }
} }
cout<<"timestamp sv iod af0fnav af0inav af1 bgde1e5a bgde1e5b\n"; cout<<"timestamp sv af0fnav af0inav af1 bgde1e5a bgde1e5b\n";
for(const auto& s : satmap) { 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()) 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<<" " << s.second.iod<<" "<< cout << s.first.first<<" " <<s.first.second<<" " <<
*s.second.af0Fnav << " " << *s.second.af0Inav <<" " << s.second.af1<<" " <<*s.second.BGDE1E5a <<" " << *s.second.BGDE1E5b << "\n"; *s.second.af0Fnav << " " << *s.second.af0Inav <<" " << s.second.af1<<" " <<*s.second.BGDE1E5a <<" " << *s.second.BGDE1E5b << "\n";
} }
} }

View file

@ -80,11 +80,13 @@ auto worker(HanderOuter<string>* ho)
cerr<<"Error processing file "<<file<<": "<<e.what()<<endl; cerr<<"Error processing file "<<file<<": "<<e.what()<<endl;
} }
} }
return stat; return std::move(stat);
} }
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
ifstream filefile(argv[1]); ifstream filefile(argv[1]);
string fname; string fname;
deque<string> files; deque<string> files;

153
rs.cc
View file

@ -1,153 +0,0 @@
#include "rs.hh"
#include <stdexcept>
#include <string.h>
#include <iostream>
extern "C" {
#include <fec.h>
}
using namespace std;
RSCodec::RSCodec(const std::vector<unsigned int>& roots, unsigned int fcr, unsigned int prim, unsigned int nroots, unsigned int pad, unsigned int bits)
: d_N((1<< (bits)) - pad -1),
d_K((1<< (bits)) - pad - 1 - nroots),
d_nroots(nroots),
d_bits(bits)
{
if(d_bits > 8)
throw std::runtime_error("This encoder supports 8 bits at most");
for(const auto& r : roots)
d_gfpoly |= (1<<r);
d_rs = init_rs_char(d_bits, d_gfpoly, fcr, prim, nroots, pad);
if(!d_rs)
throw std::runtime_error("Unable to initialize RS codec");
}
void RSCodec::encode(std::string& msg)
{
if(msg.size() > d_K)
throw std::runtime_error("Can't encode message longer than "+std::to_string(d_K)+" bytes");
msg.append(d_K - msg.size(), 0);
// void encode_rs_char(void *rs,unsigned char *data,
// unsigned char *parity);
uint8_t parity[d_nroots];
encode_rs_char(d_rs, (uint8_t*)msg.c_str(), parity);
msg.append((char*)&parity[0], (char*)&parity[d_nroots]);
}
int RSCodec::decode(const std::string& in, std::string& out, vector<unsigned int>* corrs)
{
// int decode_rs_char(void *rs,unsigned char *data,int *eras_pos,
// int no_eras);
unsigned char data[in.length()];
memcpy(data, in.c_str(), in.length());
vector<int> eras_pos;
int eras_no=0;
if(corrs) {
for(const auto& c : *corrs) {
eras_pos.push_back(c);
eras_no++;
}
}
eras_pos.resize(d_nroots);
int ret = decode_rs_char(d_rs, data, &eras_pos[0], eras_no);
/*
The decoder corrects the symbols "in place", returning the number of symbols in error. If the codeword is uncorrectable, -1 is returned and the data block is unchanged. If
eras_pos is non-null, it is used to return a list of corrected symbol positions, in no particular order. This means that the array passed through this parameter must have at
least nroots elements to prevent a possible buffer overflow.
*/
if(ret < 0)
throw std::runtime_error("Could not correct message");
if(corrs)
corrs->clear();
if(ret && corrs) {
for(int n=0; n < ret; ++n)
corrs->push_back(eras_pos.at(n));
}
out.assign((char*) data, (char*)data + d_N);
return ret;
}
RSCodec::~RSCodec()
{
if(d_rs)
free_rs_char(d_rs);
}
////
RSCodecInt::RSCodecInt(const std::vector<unsigned int>& roots, unsigned int fcr, unsigned int prim, unsigned int nroots, unsigned int pad, unsigned int bits)
: d_N((1<< (bits)) - pad -1),
d_K((1<< (bits)) - pad - 1 - nroots),
d_nroots(nroots),
d_bits(bits)
{
if(d_bits > 32)
throw std::runtime_error("This encoder supports 32 bits at most");
for(const auto& r : roots)
d_gfpoly |= (1<<r);
d_rs = init_rs_int(d_bits, d_gfpoly, fcr, prim, nroots, pad);
if(!d_rs)
throw std::runtime_error("Unable to initialize RS codec");
}
void RSCodecInt::encode(vector<unsigned int>& msg)
{
if(msg.size() > d_K)
throw std::runtime_error("Can't encode message longer than "+std::to_string(d_K)+" bytes");
msg.resize(d_K);
vector<unsigned int> parity(d_nroots);
encode_rs_int(d_rs, (int*)&msg[0], (int*)&parity[0]);
for(const auto& i : parity)
msg.push_back(i);
}
int RSCodecInt::decode(const std::vector<unsigned int>& in, std::vector<unsigned int>& out, vector<unsigned int>* corrs)
{
// int decode_rs_char(void *rs,unsigned char *data,int *eras_pos,
// int no_eras);
vector<unsigned int> data = in;
vector<unsigned int> eras_pos;
int eras_no=0;
if(corrs) {
for(const auto& c : *corrs) {
eras_pos.push_back(c);
eras_no++;
}
}
eras_pos.resize(d_nroots);
int ret = decode_rs_int(d_rs, (int*)&data[0], (int*)&eras_pos[0], eras_no);
/*
The decoder corrects the symbols "in place", returning the number of symbols in error. If the codeword is uncorrectable, -1 is returned and the data block is unchanged. If
eras_pos is non-null, it is used to return a list of corrected symbol positions, in no particular order. This means that the array passed through this parameter must have at
least nroots elements to prevent a possible buffer overflow.
*/
if(ret < 0)
throw std::runtime_error("Could not correct message");
if(corrs)
corrs->clear();
if(ret && corrs) {
for(int n=0; n < ret; ++n)
corrs->push_back(eras_pos[n]);
}
out = data;
out.resize(d_N);
return ret;
}
RSCodecInt::~RSCodecInt()
{
if(d_rs)
free_rs_int(d_rs);
}

44
rs.hh
View file

@ -1,44 +0,0 @@
#pragma once
#include <string>
#include <vector>
class RSCodec
{
public:
RSCodec(const std::vector<unsigned int>& roots, unsigned int fcr, unsigned int prim, unsigned int nroots, unsigned int pad=0, unsigned int bits=8);
void encode(std::string& msg);
int decode(const std::string& in, std::string& out, std::vector<unsigned int>* corrections=0);
int getPoly() // the representation as a number
{
return d_gfpoly;
}
~RSCodec();
private:
void* d_rs{0};
unsigned int d_gfpoly{0};
public:
const unsigned int d_N, d_K, d_nroots, d_bits;
};
class RSCodecInt
{
public:
RSCodecInt(const std::vector<unsigned int>& roots, unsigned int fcr, unsigned int prim, unsigned int nroots, unsigned int pad=0, unsigned int bits=8);
void encode(std::vector<unsigned int>& msg);
int decode(const std::vector<unsigned int>& in, std::vector<unsigned int>& out, std::vector<unsigned int>* corrections=0);
int getPoly() // the representation as a number
{
return d_gfpoly;
}
~RSCodecInt();
private:
void* d_rs{0};
unsigned int d_gfpoly{0};
public:
const unsigned int d_N, d_K, d_nroots, d_bits;
};

52
rtcm.cc
View file

@ -6,8 +6,7 @@ using namespace std;
void RTCMMessage::parse(const std::string& str) void RTCMMessage::parse(const std::string& str)
{ {
d_gm={}; memset(&d_gm, 0, sizeof(d_gm));
// memset(&d_gm, 0, sizeof(d_gm));
auto gbu=[&str](int offset, int bits) { auto gbu=[&str](int offset, int bits) {
return getbitu((const unsigned char*)str.c_str(), offset, bits); return getbitu((const unsigned char*)str.c_str(), offset, bits);
}; };
@ -263,56 +262,7 @@ DF 385: Full seconds since the beginning of the GPS week
setbitu(rtcm->buff,i, 1,osdvs2 ); i+= 1; /* E1 DVS */ setbitu(rtcm->buff,i, 1,osdvs2 ); i+= 1; /* E1 DVS */
#endif #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.
*/
}
}
} }
} }

View file

@ -4,7 +4,6 @@
#include "navmon.hh" #include "navmon.hh"
#include <vector> #include <vector>
#include "galileo.hh" #include "galileo.hh"
#include <map>
struct RTCMFrame struct RTCMFrame
{ {
@ -53,7 +52,6 @@ struct RTCMMessage
std::vector<EphemerisDelta> d_ephs; std::vector<EphemerisDelta> d_ephs;
std::vector<ClockDelta> d_clocks; std::vector<ClockDelta> d_clocks;
std::map<SatID, double> d_dcbs;
GalileoMessage d_gm; GalileoMessage d_gm;
int d_sv; int d_sv;
}; };

View file

@ -2,7 +2,6 @@
#include "bits.hh" #include "bits.hh"
#include <vector> #include <vector>
#include <iostream> #include <iostream>
#include <signal.h>
#include "nmmsender.hh" #include "nmmsender.hh"
#include "CLI/CLI.hpp" #include "CLI/CLI.hpp"
#include "swrappers.hh" #include "swrappers.hh"

20
sbas.cc
View file

@ -4,14 +4,14 @@ using namespace std;
#include "bits.hh" #include "bits.hh"
#include <math.h> #include <math.h>
void SBASState::parse0(const vector<uint8_t>& sbas, time_t now) void SBASState::parse0(const basic_string<uint8_t>& sbas, time_t now)
{ {
d_lastDNU = now; d_lastDNU = now;
d_lastSeen = now; d_lastSeen = now;
} }
void SBASState::parse1(const vector<uint8_t>& sbas, time_t now) void SBASState::parse1(const basic_string<uint8_t>& sbas, time_t now)
{ {
d_lastSeen = now; d_lastSeen = now;
int slot=1; int slot=1;
@ -25,7 +25,7 @@ void SBASState::parse1(const vector<uint8_t>& sbas, time_t now)
} }
} }
vector<SBASState::FastCorrection> SBASState::parse2_5(const vector<uint8_t>&sbas, time_t now) vector<SBASState::FastCorrection> SBASState::parse2_5(const basic_string<uint8_t>&sbas, time_t now)
{ {
d_lastSeen = now; d_lastSeen = now;
int type = getbitu(&sbas[0], 8, 6); int type = getbitu(&sbas[0], 8, 6);
@ -47,7 +47,7 @@ vector<SBASState::FastCorrection> SBASState::parse2_5(const vector<uint8_t>&sbas
return ret; return ret;
} }
vector<SBASState::FastCorrection> SBASState::parse6(const vector<uint8_t>&sbas, time_t now) vector<SBASState::FastCorrection> SBASState::parse6(const basic_string<uint8_t>&sbas, time_t now)
{ {
d_lastSeen = now; d_lastSeen = now;
vector<SBASState::FastCorrection> ret; vector<SBASState::FastCorrection> ret;
@ -68,7 +68,7 @@ vector<SBASState::FastCorrection> SBASState::parse6(const vector<uint8_t>&sbas,
return ret; return ret;
} }
void SBASState::parse7(const vector<uint8_t>&sbas, time_t now) void SBASState::parse7(const basic_string<uint8_t>&sbas, time_t now)
{ {
d_lastSeen = now; d_lastSeen = now;
d_latency = getbitu(&sbas[0], 14+4, 4); d_latency = getbitu(&sbas[0], 14+4, 4);
@ -101,7 +101,7 @@ SatID SBASState::getSBASSatID(int slot) const
return ret; return ret;
} }
vector<SBASState::LongTermCorrection> SBASState::parse25(const vector<uint8_t>& sbas, time_t t) vector<SBASState::LongTermCorrection> SBASState::parse25(const basic_string<uint8_t>& sbas, time_t t)
{ {
d_lastSeen = t; d_lastSeen = t;
vector<LongTermCorrection> ret; vector<LongTermCorrection> ret;
@ -111,7 +111,7 @@ vector<SBASState::LongTermCorrection> SBASState::parse25(const vector<uint8_t>&
return ret; return ret;
} }
pair<vector<SBASState::FastCorrection>, vector<SBASState::LongTermCorrection>> SBASState::parse24(const vector<uint8_t>& sbas, time_t t) pair<vector<SBASState::FastCorrection>, vector<SBASState::LongTermCorrection>> SBASState::parse24(const basic_string<uint8_t>& sbas, time_t t)
{ {
d_lastSeen = t; d_lastSeen = t;
pair<vector<FastCorrection>, vector<LongTermCorrection>> ret; pair<vector<FastCorrection>, vector<LongTermCorrection>> ret;
@ -135,7 +135,7 @@ pair<vector<SBASState::FastCorrection>, vector<SBASState::LongTermCorrection>> S
return ret; return ret;
} }
pair<vector<SBASState::FastCorrection>, vector<SBASState::LongTermCorrection>> SBASState::parse(const std::vector<uint8_t>& sbas, time_t now) pair<vector<SBASState::FastCorrection>, vector<SBASState::LongTermCorrection>> SBASState::parse(const std::basic_string<uint8_t>& sbas, time_t now)
{ {
pair<vector<SBASState::FastCorrection>, vector<SBASState::LongTermCorrection>> ret; pair<vector<SBASState::FastCorrection>, vector<SBASState::LongTermCorrection>> ret;
int type = getbitu(&sbas[0], 8, 6); int type = getbitu(&sbas[0], 8, 6);
@ -164,7 +164,7 @@ pair<vector<SBASState::FastCorrection>, vector<SBASState::LongTermCorrection>> S
return ret; return ret;
} }
void SBASState::parse25H(const vector<uint8_t>& sbas, time_t t, int offset, vector<LongTermCorrection>& ret) void SBASState::parse25H(const basic_string<uint8_t>& sbas, time_t t, int offset, vector<LongTermCorrection>& ret)
{ {
LongTermCorrection ltc; LongTermCorrection ltc;
ltc.velocity = getbitu(&sbas[0], offset, 1); ltc.velocity = getbitu(&sbas[0], offset, 1);
@ -217,7 +217,7 @@ void SBASState::parse25H(const vector<uint8_t>& sbas, time_t t, int offset, vect
// old version with ephemeris parsing // old version with ephemeris parsing
#if 0 #if 0
void parseSBAS25H(int sv, const vector<uint8_t>& sbas, time_t t, ofstream& sbascsv, int offset, map<int, GPSState>* gpseph, const Point& src) void parseSBAS25H(int sv, const basic_string<uint8_t>& sbas, time_t t, ofstream& sbascsv, int offset, map<int, GPSState>* gpseph, const Point& src)
{ {
bool velocity = getbitu(&sbas[0], offset, 1); bool velocity = getbitu(&sbas[0], offset, 1);

18
sbas.hh
View file

@ -45,22 +45,22 @@ struct SBASState
time_t lastUpdate{-1}; time_t lastUpdate{-1};
}; };
std::pair<std::vector<SBASState::FastCorrection>, std::vector<SBASState::LongTermCorrection>> parse(const std::vector<uint8_t>& sbas, time_t now); std::pair<std::vector<SBASState::FastCorrection>, std::vector<SBASState::LongTermCorrection>> parse(const std::basic_string<uint8_t>& sbas, time_t now);
void parse0(const std::vector<uint8_t>& message, time_t now); void parse0(const std::basic_string<uint8_t>& message, time_t now);
// updates slot2prn mapping // updates slot2prn mapping
void parse1(const std::vector<uint8_t>& message, time_t now); void parse1(const std::basic_string<uint8_t>& message, time_t now);
std::vector<FastCorrection> parse2_5(const std::vector<uint8_t>& message, time_t now); std::vector<FastCorrection> parse2_5(const std::basic_string<uint8_t>& message, time_t now);
std::vector<FastCorrection> parse6(const std::vector<uint8_t>& message, time_t now); std::vector<FastCorrection> parse6(const std::basic_string<uint8_t>& message, time_t now);
void parse7(const std::vector<uint8_t>& message, time_t now); void parse7(const std::basic_string<uint8_t>& message, time_t now);
std::pair<std::vector<FastCorrection>, std::vector<LongTermCorrection>> parse24(const std::vector<uint8_t>& message, time_t now); std::pair<std::vector<FastCorrection>, std::vector<LongTermCorrection>> parse24(const std::basic_string<uint8_t>& message, time_t now);
std::vector<LongTermCorrection> parse25(const std::vector<uint8_t>& message, time_t now); std::vector<LongTermCorrection> parse25(const std::basic_string<uint8_t>& message, time_t now);
int getSBASNumber(int slot) const; int getSBASNumber(int slot) const;
SatID getSBASSatID(int slot) const; SatID getSBASSatID(int slot) const;
@ -72,7 +72,7 @@ struct SBASState
std::map<int,int> d_slot2prn; std::map<int,int> d_slot2prn;
int d_latency = -1; int d_latency = -1;
time_t d_lastSeen{-1}; time_t d_lastSeen{-1};
void parse25H(const std::vector<uint8_t>& sbas, time_t t, int offset, std::vector<LongTermCorrection>& ret); void parse25H(const std::basic_string<uint8_t>& sbas, time_t t, int offset, std::vector<LongTermCorrection>& ret);
}; };

View file

@ -1,747 +0,0 @@
#include <string>
#include "navmon.hh"
#include <iostream>
#include <string.h>
#include <signal.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"
#include "fmt/format.h"
#include "fmt/os.h"
#include "fmt/printf.h"
#include "gps.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::vector<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::vector<uint8_t> getPayload()
{
return makeVec(&d_store[0], 8);
}
std::vector<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);
vector <uint8_t> msg= makeVec(marker, 2);
uint8_t b[6];
readn2Timeout(fd, b, 6, timeout);
for(int n=0; n < 6; ++n)
msg.push_back(b[n]);
// 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);
for(int n=0; n < len-8; ++n)
msg.push_back(buffer[n]);
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};
string serial, owner, remark;
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("--serial", serial, "Serial number of your Septentrio device");
app.add_option("--quiet", quiet, "Don't emit noise");
app.add_option("--owner,-o", owner, "Name/handle/nick of owner/operator");
app.add_option("--remark", remark, "Remark for this station");
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(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[0], sizeof(si));
// cerr<<"tow "<<si.towMsec /1000<<" wn "<<si.wn <<" sv " << (int) si.sv - 70<<" ";
int sigid = si.src & 31;
int pbsigid=sepsig2ubx(sigid);
if(!si.crcPassed) {
cerr<<fmt::sprintf("E%02d (sigid %d) I/NAV CRC error, skipping\n", si.sv-70, pbsigid);
continue;
}
std::string inav((char*)si.navBits, 32);
// cerr<<makeHexDump(inav)<<endl;
// byte order adjustment
std::vector<uint8_t> payload;
for(unsigned int i = 0 ; i < 8; ++i) {
payload.push_back(si.navBits[4 * i + 3]);
payload.push_back(si.navBits[4 * i + 2]);
payload.push_back(si.navBits[4 * i + 1]);
payload.push_back(si.navBits[4 * i + 0]);
}
/*
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
rst 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
eld is set.
Encoding: NAVBits contains all the bits of the frame, with the ex-
ception of the synchronization eld. The rst received bit is stored as the
MSB of NAVBits[0]. The unused bits in NAVBits[7] must be ignored
by the decoding software.
*/
/* so we find EVEN_PAGE ODD_PAGE */
vector<uint8_t> inav2;
// copy in the payload bits of the even page
for(int n = 0 ; n < 14; ++n)
inav2.push_back(getbitu(&payload[0], 2 + n*8, 8));
// odd page payload bits
for(int n = 0 ; n < 2; ++n)
inav2.push_back(getbitu(&payload[0], 116 + n*8, 8));
// cerr<<makeHexDump(inav2) << endl;
vector<uint8_t> reserved1;
for(int n=0; n < 5 ; ++n)
reserved1.push_back(getbitu(&payload[0], 116 + 16 + n*8, 8));
vector<uint8_t> crc;
for(int n=0; n < 3 ; ++n)
crc.push_back(getbitu(&payload[0], 116 + 16 + 40 +22 + 2 + n*8, 8));
uint8_t ssp = getbitu(&payload[0], 116 + 16 + 40 + 22 + 2 + 24, 8);
// xxx add reserved2
// xxx add sar
// xxx add spare
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((t - floor(t))*1000000000);
nmm.mutable_gi()->set_gnsswn(si.wn - 1024);
// -2 since Septentrio counts from the *end* of the message
nmm.mutable_gi()->set_gnsstow(si.towMsec/1000.0 - 2);
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(pbsigid);
nmm.mutable_gi()->set_reserved1((const char*)&reserved1[0], reserved1.size());
if(pbsigid==1) // on E5b there is no SSP
nmm.mutable_gi()->set_ssp(ssp);
nmm.mutable_gi()->set_crc((const char*)&crc[0], crc.size());
ns.emitNMM( nmm);
}
else if(res.first.getID() == 5914) { // current time
auto str = res.first.getPayload();
struct TimeMsg {
uint32_t tow;
uint16_t wn;
int8_t utcyear;
int8_t utcmonth; // 1 = jan
int8_t utcday;
int8_t utchour;
int8_t utcmin;
int8_t utcsec;
int8_t deltals;
uint8_t synclevel;
} __attribute__((packed));
TimeMsg tmsg;
memcpy(&tmsg, &str[0], sizeof(tmsg));
if(!quiet)
cerr<< fmt::sprintf("UTC Time: %04d%02d%02d %02d:%02d:%02d\n",
2000+tmsg.utcyear,
tmsg.utcmonth,
tmsg.utcday,
tmsg.utchour,
tmsg.utcmin,
tmsg.utcsec);
}
else if(res.first.getID() == 4026) {
// GLONASS
}
else if(res.first.getID() == 4017) { // GPS-CA
auto str = res.first.getPayload();
struct GPSCA
{
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[40];
} __attribute__((packed));
GPSCA ga;
memcpy(&ga, &str[0], sizeof(ga));
int sigid = ga.src & 31;
// cerr<<"tow "<<sf.towMsec /1000<<" wn "<<sf.wn <<" sv " << (int) sf.sv - 70<<" sigid " << sigid <<" ";
if(!ga.crcPassed) {
cerr<<fmt::sprintf("G%02d CA CRC error, skipping\n", ga.sv);
continue;
}
// we get 10 words of 32 bits.
// bits 0-5 are 6 parity bits
// bits 6-29 are contents
// bits 30-31 are padding
// we need to output to protobuf the parity AND padding bits as well
// downstream they get stripped
unsigned char tmp[40]={};
for(int i = 0; i < 10; ++i) {
uint32_t rev= htonl(*(uint32_t*)&ga.navBits[4*i]);
setbitu(tmp, i*32, 30, getbitu((uint8_t*)&rev, 0, 30));
}
auto payload = makeVec(tmp, 40);
auto cond = getCondensedGPSMessage(payload);
// cerr<<makeHexDump(cond)<<" ";
GPSState gs;
gs.parseGPSMessage(cond);
NavMonMessage nmm;
double t = utcFromGST(ga.wn - 1024, ga.towMsec / 1000.0);
// cerr<<t<< " " <<si.wn - 1024 <<" " <<si.towMsec /1000.0 <<" " << g_dtLS<<endl;
nmm.set_sourceid(g_srcid);
nmm.set_type(NavMonMessage::GPSInavType);
nmm.set_localutcseconds(t);
nmm.set_localutcnanoseconds((t - floor(t))*1000000000);
nmm.mutable_gpsi()->set_gnsswn(ga.wn);
int pbsigid=sepsig2ubx(sigid);
nmm.mutable_gpsi()->set_sigid(pbsigid);
nmm.mutable_gpsi()->set_gnsstow(ga.towMsec/1000 - 6); // needs to be adjusted to beginning of message
nmm.mutable_gpsi()->set_gnssid(0);
nmm.mutable_gpsi()->set_gnsssv(ga.sv);
nmm.mutable_gpsi()->set_contents(string((char*)&payload[0], payload.size()));
ns.emitNMM( nmm);
}
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[0], 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<<fmt::sprintf("E%02d F/NAV CRC error, skipping\n", sf.sv-70);
continue;
}
std::string fnav((char*)sf.navBits, 32);
// byte order adjustment
std::vector<uint8_t> payload;
for(unsigned int i = 0 ; i < 8; ++i) {
payload.push_back(sf.navBits[4 * i + 3]);
payload.push_back(sf.navBits[4 * i + 2]);
payload.push_back(sf.navBits[4 * i + 1]);
payload.push_back(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;
float cog;
double rxclkbias;
float rxclkdrift;
uint8_t timesystem;
uint8_t datum;
uint8_t nrsv;
uint8_t wacorrinfo;
uint16_t referenceid;
uint16_t meancorrage;
uint32_t signalinfo;
uint8_t alertflag;
uint8_t nrbases;
uint16_t pppinfo;
uint16_t latency;
uint16_t haccuracy;
uint16_t vaccuracy;
uint8_t misc;
} __attribute__((packed));
PVTCartesian pc;
memcpy(&pc, &str[0], 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);
// cerr << "acc: "<<pc.haccuracy*0.01 <<" "<<pc.vaccuracy*0.01<<" nrsv "<<(int)pc.nrsv<<endl;
nmm.mutable_op()->set_acc( sqrt(pc.haccuracy*0.005*pc.haccuracy*0.005 + pc.vaccuracy*0.005*pc.vaccuracy*0.005)); // septentrio reports twice the error
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(serial);
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(owner);
nmm.mutable_od()->set_remark(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[0], 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<<fmt::sprintf("E%02d C/NAV CRC error, skipping\n", sc.sv-70);
continue;
}
std::string cnav((char*)sc.navBits, 64);
// byte order adjustment
std::vector<uint8_t> payload;
for(unsigned int i = 0 ; i < 16; ++i) {
payload.push_back( sc.navBits[4 * i + 3]);
payload.push_back( sc.navBits[4 * i + 2]);
payload.push_back( sc.navBits[4 * i + 1]);
payload.push_back( 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[0],i, 1));
int calccrc=rtk_crc24q(crc_buff,58);
int realcrc= getbitu(&payload[0], 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[0], 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[0] + 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[0] + 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.
*/

View file

@ -80,16 +80,13 @@ bool getNMM(FILE* fp, NavMonMessage& nmm, uint32_t& offset)
return true; return true;
} }
// if you rely on offset, this function is atomic wrt partial reads,
// the offset will only get updated on a whole message
bool getRawNMM(int fd, timespec& t, string& raw, uint32_t& offset) bool getRawNMM(int fd, timespec& t, string& raw, uint32_t& offset)
{ {
char bert[4]; char bert[4];
int res; int res;
if((res=read(fd, bert, 4)) != 4 || bert[0]!='b' || bert[1]!='e' || bert[2] !='r' || bert[3]!='t') { if((res=read(fd, bert, 4)) != 4 || bert[0]!='b' || bert[1]!='e' || bert[2] !='r' || bert[3]!='t') {
if(res != 4) { if(res != 4)
return false; return false;
}
for(int s=0;; ++s ) { for(int s=0;; ++s ) {
cerr<<"Skipping character hunting for good magic.. "<<s<<endl; cerr<<"Skipping character hunting for good magic.. "<<s<<endl;

View file

@ -45,7 +45,7 @@ extern "C" {
} }
} }
vector<TrkSatStat> parseTrkMeas(const std::vector<uint8_t>& payload) vector<TrkSatStat> parseTrkMeas(std::basic_string_view<uint8_t> payload)
{ {
uint8_t plainchunk[16]; uint8_t plainchunk[16];
std::basic_string<uint8_t> plaintext; std::basic_string<uint8_t> plaintext;

146
ubx.cc
View file

@ -3,10 +3,9 @@
#include "fmt/format.h" #include "fmt/format.h"
#include "fmt/printf.h" #include "fmt/printf.h"
#include "bits.hh" #include "bits.hh"
#include "navmon.hh"
using namespace std; using namespace std;
uint16_t calcUbxChecksum(uint8_t ubxClass, uint8_t ubxType, const std::vector<uint8_t>& str) uint16_t calcUbxChecksum(uint8_t ubxClass, uint8_t ubxType, std::basic_string_view<uint8_t> str)
{ {
uint8_t CK_A = 0, CK_B = 0; uint8_t CK_A = 0, CK_B = 0;
@ -25,33 +24,33 @@ uint16_t calcUbxChecksum(uint8_t ubxClass, uint8_t ubxType, const std::vector<ui
return (CK_B << 8) + CK_A; return (CK_B << 8) + CK_A;
} }
std::vector<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, const std::initializer_list<uint8_t>& lst) std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, const std::initializer_list<uint8_t>& lst)
{ {
std::vector<uint8_t> str; std::basic_string<uint8_t> str;
for(const auto& a : lst) for(const auto& a : lst)
str.push_back(a); str.append(1, a);
return buildUbxMessage(ubxClass, ubxType, str); return buildUbxMessage(ubxClass, ubxType, str);
} }
std::vector<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, const std::vector<uint8_t>& str) std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, std::basic_string_view<uint8_t> str)
{ {
// 0xb5 0x62 class id len1 len2 payload cka ckb // 0xb5 0x62 class id len1 len2 payload cka ckb
std::vector<uint8_t> msg; std::basic_string<uint8_t> msg;
msg.push_back(0xb5); msg.append(1, 0xb5);
msg.push_back(0x62); msg.append(1, 0x62);
msg.push_back(ubxClass); // CFG msg.append(1, ubxClass); // CFG
msg.push_back(ubxType); // MSG msg.append(1, ubxType); // MSG
msg.push_back(str.size()); // len1 msg.append(1, str.size()); // len1
msg.push_back(str.size()/256); // len2 msg.append(1, str.size()/256); // len2
for(unsigned int n= 0 ; n < str.size(); ++n) for(unsigned int n= 0 ; n < str.size(); ++n)
msg.push_back(str[n]); msg.append(1, str[n]);
uint16_t csum = calcUbxChecksum(ubxClass, ubxType, {msg.cbegin() + 6, msg.cend()}); uint16_t csum = calcUbxChecksum(ubxClass, ubxType, msg.substr(6));
msg.push_back(csum&0xff); msg.append(1, csum&0xff);
msg.push_back(csum>>8); msg.append(1, csum>>8);
/* /*
for(const auto& c : msg) { for(const auto& c : msg) {
fmt::fprintf(stderr, "%02x ", (int)c); fmt::fprintf(stderr, "%02x ", (int)c);
@ -61,149 +60,80 @@ std::vector<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, const st
return msg; return msg;
} }
vector<uint8_t> getInavFromSFRBXMsg(const std::vector<uint8_t>& msg, basic_string<uint8_t> getInavFromSFRBXMsg(std::basic_string_view<uint8_t> msg)
vector<uint8_t>& reserved1,
vector<uint8_t>& reserved2,
vector<uint8_t>& sar,
vector<uint8_t>& spare,
vector<uint8_t>& crc, uint8_t* ssp)
{ {
// byte order adjustment // byte order adjustment
std::vector<uint8_t> payload; std::basic_string<uint8_t> payload;
for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i) for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i)
for(int j=1; j <= 4; ++j) for(int j=1; j <= 4; ++j)
payload.push_back(msg[8 + (i+1) * 4 -j]); payload.append(1, msg[8 + (i+1) * 4 -j]);
/* test crc (4(pad) + 114 + 82 bits) */ /* test crc (4(pad) + 114 + 82 bits) */
unsigned char crc_buff[26]={0}; unsigned char crc_buff[26]={0};
unsigned int i,j; unsigned int i,j;
for (i=0,j= 4;i<15;i++,j+=8) setbitu(crc_buff,j,8,getbitu(&*payload.cbegin() ,i*8,8)); 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[0] + 16,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[0] +16,82,24)) { if (rtk_crc24q(crc_buff,25) != getbitu(payload.c_str()+16,82,24)) {
cerr << "CRC mismatch, " << rtk_crc24q(crc_buff, 25) << " != " << getbitu(&payload[0]+16,82,24) <<endl; cout << "CRC mismatch, " << rtk_crc24q(crc_buff, 25) << " != " << getbitu(payload.c_str()+16,82,24) <<endl;
throw CRCMismatch(); throw CRCMismatch();
} }
crc.clear(); std::basic_string<uint8_t> inav;
for(i=0; i < 3; ++i)
crc.push_back(getbitu(&payload[0] +16,82+i*8,8));
if(ssp) {
*ssp=getbitu(&payload[0]+16,82+24,8);
}
std::vector<uint8_t> inav;
for (i=0,j=2; i<14; i++, j+=8) for (i=0,j=2; i<14; i++, j+=8)
inav.push_back((unsigned char)getbitu(&payload[0] ,j,8)); inav.append(1, (unsigned char)getbitu(payload.c_str() ,j,8));
for (i=0,j=2; i< 2; i++, j+=8) for (i=0,j=2; i< 2; i++, j+=8)
inav.push_back((unsigned char)getbitu(&payload[0]+16,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.push_back((unsigned char)getbitu(&payload[0] +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.push_back((unsigned char)getbitu(&payload[0] +16, j, 8));
spare.clear();
spare.push_back((unsigned char)getbitu(&payload[0]+16, 80, 2));
reserved2.clear();
reserved2.push_back((unsigned char)getbitu(&payload[0] +16, 106, 8));
return inav; return inav;
} }
vector<uint8_t> getFnavFromSFRBXMsg(const std::vector<uint8_t>& msg,
vector<uint8_t>& crc)
{
// byte order adjustment
std::vector<uint8_t> payload;
for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i)
for(int j=1; j <= 4; ++j)
payload.push_back(msg[8 + (i+1) * 4 -j]);
//
// 214 bitsof payload
// 2 bits padding, 214 bits payload, 24 bits ctc
// 216 bits -> 27 bytes
unsigned char crc_buff[27]={0};
unsigned int i,j;
for (i=0,j= 2;i<27;i++,j+=8) setbitu(crc_buff,j,8,getbitu(&payload[0] ,i*8,8));
if (rtk_crc24q(crc_buff,27) != getbitu(&payload[0], 214,24)) {
cerr << "CRC mismatch, " << rtk_crc24q(crc_buff, 27) << " != " << getbitu(&payload[0], 214,24) <<endl;
cerr << makeHexDump(payload) << " " << (int) getbitu(&payload[0], 0, 6) << endl;
throw CRCMismatch();
}
// cerr << "F/NAV CRC MATCHED!!"<<endl;
crc.clear();
for(i=0; i < 3; ++i)
crc.push_back(getbitu(&payload[0], 214+i*8,8));
std::vector<uint8_t> fnav;
for (i=0,j=0; i<27; i++, j+=8)
fnav.push_back((unsigned char)getbitu(&payload[0] ,j,8));
return fnav;
}
// XXX this should do the parity check // XXX this should do the parity check
vector<uint8_t> getGPSFromSFRBXMsg(const std::vector<uint8_t>& msg) basic_string<uint8_t> getGPSFromSFRBXMsg(std::basic_string_view<uint8_t> msg)
{ {
// byte order adjustment // byte order adjustment
std::vector<uint8_t> payload; std::basic_string<uint8_t> payload;
for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i) for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i)
for(int j=1; j <= 4; ++j) for(int j=1; j <= 4; ++j)
payload.push_back( msg[8 + (i+1) * 4 -j]); payload.append(1, msg[8 + (i+1) * 4 -j]);
return payload; return payload;
} }
// note, this returns the fourth UBX specific word with derived data, feel free to ignore! // note, this returns the fourth UBX specific word with derived data, feel free to ignore!
vector<uint8_t> getGlonassFromSFRBXMsg(const std::vector<uint8_t>& msg) basic_string<uint8_t> getGlonassFromSFRBXMsg(std::basic_string_view<uint8_t> msg)
{ {
// byte order adjustment // byte order adjustment
std::vector<uint8_t> payload; std::basic_string<uint8_t> payload;
for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i) for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i)
for(int j=1; j <= 4; ++j) for(int j=1; j <= 4; ++j)
payload.push_back( msg[8 + (i+1) * 4 -j]); payload.append(1, msg[8 + (i+1) * 4 -j]);
return payload; return payload;
} }
// note, this returns the fourth UBX specific word with derived data, feel free to ignore! // note, this returns the fourth UBX specific word with derived data, feel free to ignore!
vector<uint8_t> getBeidouFromSFRBXMsg(const std::vector<uint8_t>& msg) basic_string<uint8_t> getBeidouFromSFRBXMsg(std::basic_string_view<uint8_t> msg)
{ {
// byte order adjustment // byte order adjustment
std::vector<uint8_t> payload; std::basic_string<uint8_t> payload;
for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i) for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i)
for(int j=1; j <= 4; ++j) for(int j=1; j <= 4; ++j)
payload.push_back( msg[8 + (i+1) * 4 -j]); payload.append(1, msg[8 + (i+1) * 4 -j]);
return payload; return payload;
} }
vector<uint8_t> getSBASFromSFRBXMsg(const std::vector<uint8_t>& msg) basic_string<uint8_t> getSBASFromSFRBXMsg(std::basic_string_view<uint8_t> msg)
{ {
// byte order adjustment // byte order adjustment
std::vector<uint8_t> payload; std::basic_string<uint8_t> payload;
for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i) for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i)
for(int j=1; j <= 4; ++j) for(int j=1; j <= 4; ++j)
payload.push_back( msg[8 + (i+1) * 4 -j]); payload.append(1, msg[8 + (i+1) * 4 -j]);
return payload; return payload;

29
ubx.hh
View file

@ -1,27 +1,16 @@
#pragma once #pragma once
#include <string> #include <string>
#include <vector> #include <vector>
#include <cstdint> uint16_t calcUbxChecksum(uint8_t ubxClass, uint8_t ubxType, std::basic_string_view<uint8_t> str);
std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, std::basic_string_view<uint8_t> str);
uint16_t calcUbxChecksum(uint8_t ubxClass, uint8_t ubxType, const std::vector<uint8_t>& str); std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, const std::initializer_list<uint8_t>& str);
std::vector<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, const std::vector<uint8_t>& str);
std::vector<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> getGPSFromSFRBXMsg(std::basic_string_view<uint8_t> msg);
std::vector<uint8_t> getInavFromSFRBXMsg(const std::vector<uint8_t>& msg, std::basic_string<uint8_t> getGlonassFromSFRBXMsg(std::basic_string_view<uint8_t> msg);
std::vector<uint8_t>& reserved1, std::basic_string<uint8_t> getBeidouFromSFRBXMsg(std::basic_string_view<uint8_t> msg);
std::vector<uint8_t>& reserved2, std::basic_string<uint8_t> getSBASFromSFRBXMsg(std::basic_string_view<uint8_t> msg);
std::vector<uint8_t>& sar,
std::vector<uint8_t>& spare,
std::vector<uint8_t>& crc, uint8_t* ssp=0);
std::vector<uint8_t> getFnavFromSFRBXMsg(const std::vector<uint8_t>& msg,
std::vector<uint8_t>& crc);
std::vector<uint8_t> getGPSFromSFRBXMsg(const std::vector<uint8_t>& msg);
std::vector<uint8_t> getGlonassFromSFRBXMsg(const std::vector<uint8_t>& msg);
std::vector<uint8_t> getBeidouFromSFRBXMsg(const std::vector<uint8_t>& msg);
std::vector<uint8_t> getSBASFromSFRBXMsg(const std::vector<uint8_t>& msg);
struct CRCMismatch{}; struct CRCMismatch{};
struct TrkSatStat struct TrkSatStat
@ -32,4 +21,4 @@ struct TrkSatStat
uint64_t tr; uint64_t tr;
}; };
std::vector<TrkSatStat> parseTrkMeas(const std::vector<uint8_t>& payload); std::vector<TrkSatStat> parseTrkMeas(std::basic_string_view<uint8_t> payload);

View file

@ -1,7 +1,6 @@
#define _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE
#include <sys/types.h> #include <sys/types.h>
#include <sys/time.h> #include <sys/time.h>
#include <signal.h>
#include <map> #include <map>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
@ -135,13 +134,13 @@ class UBXMessage
{ {
public: public:
struct BadChecksum{}; struct BadChecksum{};
explicit UBXMessage(const std::vector<uint8_t>& src) explicit UBXMessage(basic_string_view<uint8_t> src)
{ {
d_raw = src; d_raw = src;
if(d_raw.size() < 6) if(d_raw.size() < 6)
throw std::runtime_error("Partial UBX message"); throw std::runtime_error("Partial UBX message");
uint16_t csum = calcUbxChecksum(getClass(), getType(), {d_raw.cbegin() + 6, d_raw.cend()-2}); uint16_t csum = calcUbxChecksum(getClass(), getType(), d_raw.substr(6, d_raw.size()-8));
if(csum != d_raw.at(d_raw.size()-2) + 256*d_raw.at(d_raw.size()-1)) if(csum != d_raw.at(d_raw.size()-2) + 256*d_raw.at(d_raw.size()-1))
throw BadChecksum(); throw BadChecksum();
@ -154,11 +153,11 @@ public:
{ {
return d_raw.at(3); return d_raw.at(3);
} }
std::vector<uint8_t> getPayload() const std::basic_string<uint8_t> getPayload() const
{ {
return {d_raw.cbegin() +6, d_raw.cend()-2}; return d_raw.substr(6, d_raw.size()-8);
} }
std::vector<uint8_t> d_raw; std::basic_string<uint8_t> d_raw;
}; };
bool g_fromFile{false}; bool g_fromFile{false};
@ -191,24 +190,21 @@ std::pair<UBXMessage, struct timeval> getUBXMessage(int fd, double* timeout)
if(marker[0]==0xb5 && marker[1]==0x62) { // bingo if(marker[0]==0xb5 && marker[1]==0x62) { // bingo
struct timeval tv; struct timeval tv;
gettimeofday(&tv, 0); gettimeofday(&tv, 0);
vector<uint8_t> msg; basic_string<uint8_t> msg;
msg.push_back(marker[0]); msg.append(marker, 2); // 0,1
msg.push_back(marker[1]);
uint8_t b[4]; uint8_t b[4];
readn2Timeout(fd, b, 4, timeout); readn2Timeout(fd, b, 4, timeout);
for(int n=0; n <4 ;++n) msg.append(b, 4); // class, type, len1, len2
msg.push_back(b[n]);
uint16_t len = b[2] + 256*b[3]; uint16_t len = b[2] + 256*b[3];
// if (doDEBUG) { cerr<<humanTimeNow()<<" Got class "<<(int)msg[2]<<" type "<<(int)msg[3]<<", len = "<<len<<endl; } // if (doDEBUG) { cerr<<humanTimeNow()<<" Got class "<<(int)msg[2]<<" type "<<(int)msg[3]<<", len = "<<len<<endl; }
uint8_t buffer[len+2]; uint8_t buffer[len+2];
res=readn2Timeout(fd, buffer, len+2, timeout); res=readn2Timeout(fd, buffer, len+2, timeout);
for(auto ptr = buffer ; ptr < buffer+len+2; ++ptr) msg.append(buffer, len+2); // checksum
msg.push_back(*ptr); // checksum
if (doLOGFILE) { if (doLOGFILE) {
if(!g_fromFile) if(!g_fromFile)
writen2(logfile, &msg[0], msg.size()); writen2(logfile, msg.c_str(), msg.size());
} }
return make_pair(UBXMessage(msg), tv); return make_pair(UBXMessage(msg), tv);
} }
@ -231,7 +227,7 @@ UBXMessage waitForUBX(int fd, int seconds, uint8_t ubxClass, uint8_t ubxType)
throw std::runtime_error("Did not get response on time"); throw std::runtime_error("Did not get response on time");
} }
UBXMessage sendAndWaitForUBX(int fd, int seconds, const vector<uint8_t>& msg, uint8_t ubxClass, uint8_t ubxType) UBXMessage sendAndWaitForUBX(int fd, int seconds, basic_string_view<uint8_t> msg, uint8_t ubxClass, uint8_t ubxType)
{ {
for(int n=3; n; --n) { for(int n=3; n; --n) {
writen2(fd, &msg[0], msg.size()); writen2(fd, &msg[0], msg.size());
@ -279,7 +275,7 @@ bool waitForUBXAckNack(int fd, int seconds, int ubxClass, int ubxType)
throw std::runtime_error("Did not get ACK/NACK response for class "+to_string(ubxClass)+" type "+to_string(ubxType)+" on time"); throw std::runtime_error("Did not get ACK/NACK response for class "+to_string(ubxClass)+" type "+to_string(ubxType)+" on time");
} }
bool sendAndWaitForUBXAckNack(int fd, int seconds, const vector<uint8_t>& msg, uint8_t ubxClass, uint8_t ubxType) bool sendAndWaitForUBXAckNack(int fd, int seconds, basic_string_view<uint8_t> msg, uint8_t ubxClass, uint8_t ubxType)
{ {
for(int n=3; n; --n) { for(int n=3; n; --n) {
writen2(fd, &msg[0], msg.size()); writen2(fd, &msg[0], msg.size());
@ -298,20 +294,19 @@ bool sendAndWaitForUBXAckNack(int fd, int seconds, const vector<uint8_t>& msg, u
bool version9 = false; bool version9 = false;
bool version10 = false;
void enableUBXMessageOnPort(int fd, uint8_t ubxClass, uint8_t ubxType, uint8_t port, uint8_t rate=1) void enableUBXMessageOnPort(int fd, uint8_t ubxClass, uint8_t ubxType, uint8_t port, uint8_t rate=1)
{ {
for(int n=0 ; n < 5; ++n) { for(int n=0 ; n < 5; ++n) {
try { try {
vector<uint8_t> payload; basic_string<uint8_t> payload;
if(version9) { if(version9) {
payload= vector<uint8_t>({ubxClass, ubxType, rate}); payload= basic_string<uint8_t>({ubxClass, ubxType, rate});
} }
else { else {
if(port > 6) if(port > 6)
throw std::runtime_error("Port number out of range (>6)"); throw std::runtime_error("Port number out of range (>6)");
payload = vector<uint8_t>({ubxClass, ubxType, 0, 0, 0, 0, 0, 0}); payload.assign({ubxClass, ubxType, 0, 0, 0, 0, 0, 0});
payload[2+ port]=rate; payload[2+ port]=rate;
} }
@ -357,7 +352,7 @@ void readSome(int fd)
(void)timestamp; (void)timestamp;
if (doDEBUG) { cerr<<humanTimeNow()<<" Read some init: "<<(int)msg.getClass() << " " <<(int)msg.getType() <<endl; } if (doDEBUG) { cerr<<humanTimeNow()<<" Read some init: "<<(int)msg.getClass() << " " <<(int)msg.getType() <<endl; }
if(msg.getClass() == 0x4) if(msg.getClass() == 0x4)
if (doDEBUG) { cerr<<humanTimeNow()<<" "<<string((char*)&msg.getPayload()[0], msg.getPayload().size()) <<endl; } if (doDEBUG) { cerr<<humanTimeNow()<<" "<<string((char*)msg.getPayload().c_str(), msg.getPayload().size()) <<endl; }
} }
catch(TimeoutError& te) { catch(TimeoutError& te) {
cerr<<"Timeout"<<endl; cerr<<"Timeout"<<endl;
@ -415,10 +410,8 @@ int initFD(const char* fname, bool doRTSCTS)
int fd; int fd;
if (doDEBUG) { cerr<<humanTimeNow()<<" initFD()"<<endl; } if (doDEBUG) { cerr<<humanTimeNow()<<" initFD()"<<endl; }
if (!isPresent(fname)) { if (!isPresent(fname)) {
if (doDEBUG) { cerr<<humanTimeNow()<<" initFD - "<<fname<<" - not present"<<endl; } if (doDEBUG) { cerr<<humanTimeNow()<<" initFD - "<<fname<<" - not present"<<endl; }
throw runtime_error("Opening file "+string(fname));
throw runtime_error("Opening file "+string(fname)+": not present");
} }
if(string(fname) != "stdin" && string(fname) != "/dev/stdin" && isCharDevice(fname)) { if(string(fname) != "stdin" && string(fname) != "/dev/stdin" && isCharDevice(fname)) {
if (doDEBUG) { cerr<<humanTimeNow()<<" initFD - open("<<fname<<")"<<endl; } if (doDEBUG) { cerr<<humanTimeNow()<<" initFD - open("<<fname<<")"<<endl; }
@ -445,7 +438,7 @@ int initFD(const char* fname, bool doRTSCTS)
} }
static string format_serial(const vector<uint8_t>& payload) static string format_serial(basic_string<uint8_t> payload)
{ {
return fmt::sprintf("%02x%02x%02x%02x%02x", return fmt::sprintf("%02x%02x%02x%02x%02x",
payload[4], payload[5], payload[4], payload[5],
@ -503,7 +496,7 @@ struct TIMEGPS
// ubxtool device srcid // ubxtool device srcid
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
auto starttime = std::chrono::steady_clock::now(); time_t starttime=time(0);
GOOGLE_PROTOBUF_VERIFY_VERSION; GOOGLE_PROTOBUF_VERIFY_VERSION;
CLI::App app(program); CLI::App app(program);
@ -608,63 +601,6 @@ int main(int argc, char** argv)
string mods; string mods;
string serialno; string serialno;
auto doRcvrStatPoll = [&]() {
// UBX-MON-RCVRSTAT
auto msg = buildUbxMessage(0x0a, 0x40, {});
auto um1=sendAndWaitForUBX(fd, 1, msg, 0x0a, 0x40);
const auto rstat = um1.getPayload();
uint32_t brate =getbitu(&rstat[0], 40*8, 32);
cerr<< "baud rate " << brate << " " << htonl(brate)<< " bitu " <<getbitu((uint8_t*)&brate, 31-19, 20)<<endl;
auto isOn = [&rstat](int byte, int bit, const char* name, int width=4) {
int comp = 8*width - 1;
cerr<<name<<": ";
uint32_t val = getbitu((const unsigned char*)&rstat[0], byte*8, width*8);
if(getbitu((const unsigned char*)&val, comp - bit, 1))
cerr << "ON ";
else
cerr << "OFF ";
cerr << "src " << (int) getbitu((const unsigned char*)&val, comp - bit - 3, 3) << endl;
};
isOn(1, 0, "SBAS", 1);
isOn(1, 4, "SBAS L1 C/A", 1);
isOn(2, 0, "NAVIC", 1);
isOn(2, 4, "NAVIC L5",1);
isOn(4, 0, "GPS");
isOn(4, 4, "GPS L1 C/A");
isOn(4, 8, "GPS L1C");
isOn(4, 12, "GPS L2C");
isOn(4, 16, "GPS L5");
isOn(8, 0, "Galileo");
isOn(8, 4, "Galileo E1");
isOn(8, 8, "Galileo E5a");
isOn(8, 12, "Galileo E5b");
isOn(8, 16, "Galileo E6");
isOn(12, 0, "QZSS");
isOn(12, 4, "QZSS L1 C/A");
isOn(12, 8, "QZSS L1C");
isOn(12, 12, "QZSS L1S");
isOn(12, 16, "QZSS L2C");
isOn(12, 20, "QZSS L5");
isOn(16, 0, "BeiDou");
isOn(16, 4, "BeiDou B1I");
isOn(16, 8, "BeiDou B1C");
isOn(16, 12, "BeiDou B2");
isOn(16, 16, "BeiDou B2A");
isOn(20, 0, "GLONASS");
isOn(20, 4, "GLONASS L1");
isOn(20, 8, "GLONASS L2");
isOn(20, 12, "GLONASS L3");
};
if(!g_fromFile) { if(!g_fromFile) {
bool doInit = true; bool doInit = true;
if(doInit) { if(doInit) {
@ -673,11 +609,11 @@ int main(int argc, char** argv)
else else
readSome(fd); readSome(fd);
std::vector<uint8_t> msg; std::basic_string<uint8_t> msg;
if(doReset) { if(doReset) {
if (doDEBUG) { cerr<<humanTimeNow()<<" Sending a soft reset"<<endl; } if (doDEBUG) { cerr<<humanTimeNow()<<" Sending a soft reset"<<endl; }
msg = buildUbxMessage(0x06, 0x04, {0x00, 0x00, 0x01, 0x00}); // soft reset msg = buildUbxMessage(0x06, 0x04, {0x00, 0x00, 0x01, 0x00}); // soft reset
writen2(fd, &msg[0], msg.size()); writen2(fd, msg.c_str(), msg.size());
usleep(100000); usleep(100000);
close(fd); close(fd);
for(int n=0 ; n< 20; ++n) { for(int n=0 ; n< 20; ++n) {
@ -701,23 +637,18 @@ int main(int argc, char** argv)
if (doDEBUG) { cerr<<humanTimeNow()<<" Sending version query"<<endl; } if (doDEBUG) { cerr<<humanTimeNow()<<" Sending version query"<<endl; }
UBXMessage um1=sendAndWaitForUBX(fd, 1, msg, 0x0a, 0x04); // ask for version UBXMessage um1=sendAndWaitForUBX(fd, 1, msg, 0x0a, 0x04); // ask for version
swversion = (char*)&um1.getPayload()[0]; swversion = (const char*)um1.getPayload().c_str();
hwversion = (char*)&um1.getPayload().at(30); hwversion = (const char*)um1.getPayload().c_str()+30;
cerr<<humanTimeNow()<<" swVersion: "<<swversion<<endl; cerr<<humanTimeNow()<<" swVersion: "<<swversion<<endl;
cerr<<humanTimeNow()<<" hwVersion: "<<hwversion<<endl; cerr<<humanTimeNow()<<" hwVersion: "<<hwversion<<endl;
for(unsigned int n=0; 40+30*n < um1.getPayload().size(); ++n) { for(unsigned int n=0; 40+30*n < um1.getPayload().size(); ++n) {
string line = (const char*)&um1.getPayload()[0] + 40 +30*n; string line = (const char*)um1.getPayload().c_str() + 40 +30*n;
cerr<<humanTimeNow()<<" Extended info: "<<line <<endl; cerr<<humanTimeNow()<<" Extended info: "<<line <<endl;
if(line.find("F9") != string::npos) if(line.find("F9") != string::npos)
version9=true; version9=true;
// https://content.u-blox.com/sites/default/files/documents/u-blox-F10-SPG-6.00_InterfaceDescription_UBX-23002975.pdf?utm_content=UBX-23002975
if(line.find("F10") != string::npos)
version10=true;
if(line.find("M8T") != string::npos) { if(line.find("M8T") != string::npos) {
m8t=true; m8t=true;
} }
@ -739,11 +670,7 @@ int main(int argc, char** argv)
if(version9) if(version9)
cerr<<humanTimeNow()<<" Detected version U-Blox 9"<<endl; cerr<<humanTimeNow()<<" Detected version U-Blox 9"<<endl;
usleep(50000); usleep(50000);
if(!version10) {
if (doDEBUG) { cerr<<humanTimeNow()<<" Sending GNSS query"<<endl; } if (doDEBUG) { cerr<<humanTimeNow()<<" Sending GNSS query"<<endl; }
msg = buildUbxMessage(0x06, 0x3e, {}); msg = buildUbxMessage(0x06, 0x3e, {});
um1=sendAndWaitForUBX(fd, 1, msg, 0x06, 0x3e); // query GNSS um1=sendAndWaitForUBX(fd, 1, msg, 0x06, 0x3e); // query GNSS
@ -762,9 +689,7 @@ int main(int argc, char** argv)
}catch(...) { }catch(...) {
cerr<<"Got timeout waiting for ack of poll, no problem"<<endl; cerr<<"Got timeout waiting for ack of poll, no problem"<<endl;
} }
} if(!version9) {
if(!version9 && !version10) {
// ver RO maxch cfgs // ver RO maxch cfgs
msg = buildUbxMessage(0x06, 0x3e, {0x00, 0x00, 0xff, 0x06, msg = buildUbxMessage(0x06, 0x3e, {0x00, 0x00, 0xff, 0x06,
// GPS min max res x1 x2 x3, x4 // GPS min max res x1 x2 x3, x4
@ -793,7 +718,7 @@ int main(int argc, char** argv)
exit(-1); exit(-1);
} }
} }
else if(version9) { // UBX-CFG-VALSET else { // UBX-CFG-VALSET
// vers ram res res // vers ram res res
msg = buildUbxMessage(0x06, 0x8a, {0x00, 0x01, 0x00, 0x00, msg = buildUbxMessage(0x06, 0x8a, {0x00, 0x01, 0x00, 0x00,
0x1f,0x00,0x31,0x10, doGPS, // 0x1f,0x00,0x31,0x10, doGPS, //
@ -806,8 +731,7 @@ int main(int argc, char** argv)
0x22,0x00,0x31,0x10, doBeidou, 0x22,0x00,0x31,0x10, doBeidou,
0x0d,0x00,0x31,0x10, doBeidou, 0x0d,0x00,0x31,0x10, doBeidou,
0x0f,0x00,0x31,0x10, doBeidou, 0x0e,0x00,0x31,0x10, doBeidou,
0x28,0x00,0x31,0x10, doBeidou,
0x25,0x00,0x31,0x10, doGlonass, 0x25,0x00,0x31,0x10, doGlonass,
0x18,0x00,0x31,0x10, doGlonass, 0x18,0x00,0x31,0x10, doGlonass,
@ -838,57 +762,6 @@ int main(int argc, char** argv)
exit(-1); exit(-1);
} }
} }
else if(version10) {
// vers lay tra res
msg = buildUbxMessage(0x06, 0x8a, {0x00, 0x01, 0x00, 0x00,
0x1f,0x00,0x31,0x10, doGPS, // 0x1031001f
0x01,0x00,0x31,0x10, doGPS,
0x04,0x00,0x31,0x10, doGPS, // 0x10310004
0x21,0x00,0x31,0x10, doGalileo,
0x07,0x00,0x31,0x10, doGalileo,
0x09,0x00,0x31,0x10, doGalileo, // e5a
0x20,0x00,0x31,0x10, 0, //SBAS
0x22,0x00,0x31,0x10, doBeidou, // main
0x0d,0x00,0x31,0x10, 0, // B1I
0x0f,0x00,0x31,0x10, doBeidou, // B1C <- F10
0x28,0x00,0x31,0x10, doBeidou // B2A <- F10
});
if (doDEBUG) { cerr<<humanTimeNow()<<" Sending F10 GNSS setting, GPS: "<<doGPS<<", Galileo: "<<doGalileo<<", BeiDou: "<<doBeidou<< ", GLONASS: "<<doGlonass<<", SBAS: "<<doSBAS<<endl; }
if(sendAndWaitForUBXAckNack(fd, 2, msg, 0x06, 0x8a)) { // GNSS setting, F10 stylee
if (doDEBUG) { cerr<<humanTimeNow()<<" Got ack on F10 GNSS setting"<<endl; }
}
else {
cerr<<humanTimeNow()<<" Got nack on F10 GNSS setting"<<endl;
exit(-1);
}
/* VALSET
0x20 91 02 32 =
*/
// why not 0x10740001 - Flag to indicate if UBX should be an output protocol on UART1
usleep(750000); // required according to the doc
doRcvrStatPoll();
msg = buildUbxMessage(0x06, 0x8a, {0x00, 0x01, 0x00, 0x00,
0x07, 0x00, 0x91, 0x20, 1, // Output rate of the UBX-NAV-PVT message on port UART1
0x32, 0x02, 0x91, 0x20, 10} // Output rate of the UBX-RXM-SFRBX message on port UART1
);
if(sendAndWaitForUBXAckNack(fd, 2, msg, 0x06, 0x8a)) { // msg cfg F10
if (doDEBUG) { cerr<<humanTimeNow()<<" Got ack on F10 UART1 setting"<<endl; }
}
else {
cerr<<humanTimeNow()<<" Got nack on F10 UART1 setting"<<endl;
exit(-1);
}
}
if(m8t) { if(m8t) {
cerr<<humanTimeNow()<<" Sending TMODE2 status query"<<endl; cerr<<humanTimeNow()<<" Sending TMODE2 status query"<<endl;
msg = buildUbxMessage(0x06, 0x3d, {}); msg = buildUbxMessage(0x06, 0x3d, {});
@ -935,7 +808,7 @@ int main(int argc, char** argv)
minCentimetersVal = surveyMinCM * 100; minCentimetersVal = surveyMinCM * 100;
uint8_t* ptrSeconds = (uint8_t*)&minSecondsVal, *ptrCent= (uint8_t*)&minCentimetersVal; uint8_t* ptrSeconds = (uint8_t*)&minSecondsVal, *ptrCent= (uint8_t*)&minCentimetersVal;
uint8_t cmd; uint8_t cmd;
std::vector<uint8_t> msg; std::basic_string<uint8_t> msg;
if(version9) { if(version9) {
cmd = 0x8a; cmd = 0x8a;
msg = buildUbxMessage(0x06, cmd, {0x00, 0x01, 0x00, 0x00, msg = buildUbxMessage(0x06, cmd, {0x00, 0x01, 0x00, 0x00,
@ -1030,7 +903,6 @@ int main(int argc, char** argv)
} }
} }
} }
if(!version10) {
if (doDEBUG) { cerr<<humanTimeNow()<<" Polling port settings"<<endl; } // UBX-CFG-PRT, 0x03 == USB if (doDEBUG) { cerr<<humanTimeNow()<<" Polling port settings"<<endl; } // UBX-CFG-PRT, 0x03 == USB
msg = buildUbxMessage(0x06, 0x00, {(unsigned char)(ubxport)}); msg = buildUbxMessage(0x06, 0x00, {(unsigned char)(ubxport)});
@ -1048,14 +920,14 @@ int main(int argc, char** argv)
}catch(...) { }catch(...) {
cerr<<"Got timeout waiting for ack of port protocol poll, no problem"<<endl; cerr<<"Got timeout waiting for ack of port protocol poll, no problem"<<endl;
} }
}
if(mods.find("NEO-M8P") ==string::npos) { if(mods.find("NEO-M8P") ==string::npos) {
if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-RXM-RLM"<<endl; } // SAR if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-RXM-RLM"<<endl; } // SAR
enableUBXMessageOnPort(fd, 0x02, 0x59, ubxport); // UBX-RXM-RLM enableUBXMessageOnPort(fd, 0x02, 0x59, ubxport); // UBX-RXM-RLM
} }
if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-MON-HW"<<endl; } if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-MON-HW"<<endl; } // SAR
enableUBXMessageOnPort(fd, 0x0A, 0x09, ubxport, 16); // UBX-MON-HW enableUBXMessageOnPort(fd, 0x0A, 0x09, ubxport, 16); // UBX-MON-HW
@ -1068,7 +940,7 @@ int main(int argc, char** argv)
enableUBXMessageOnPort(fd, 0x0d, 0x04, ubxport, 2); enableUBXMessageOnPort(fd, 0x0d, 0x04, ubxport, 2);
} }
if(mods.find("NEO-M9N") == string::npos && !version10) { if(mods.find("NEO-M9N") == string::npos) {
if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-RXM-RAWX"<<endl; } // RF doppler if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-RXM-RAWX"<<endl; } // RF doppler
enableUBXMessageOnPort(fd, 0x02, 0x15, ubxport, 8); // RXM-RAWX enableUBXMessageOnPort(fd, 0x02, 0x15, ubxport, 8); // RXM-RAWX
} }
@ -1080,10 +952,8 @@ int main(int argc, char** argv)
if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling/disabling UBX-NAV-TIMEGPS"<<endl; } // GPS time solution if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling/disabling UBX-NAV-TIMEGPS"<<endl; } // GPS time solution
enableUBXMessageOnPort(fd, 0x01, 0x20, ubxport, doGPS ? 16 : 0); // UBX-NAV-TIMEGPS enableUBXMessageOnPort(fd, 0x01, 0x20, ubxport, doGPS ? 16 : 0); // UBX-NAV-TIMEGPS
if(!version10) {
if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling/disabling "<< doGlonass<< " UBX-NAV-TIMEGLO"<<endl; } // GLONASS time solution if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling/disabling "<< doGlonass<< " UBX-NAV-TIMEGLO"<<endl; } // GLONASS time solution
enableUBXMessageOnPort(fd, 0x01, 0x23, ubxport, doGlonass ? 16 : 0); // UBX-NAV-TIMEGLO enableUBXMessageOnPort(fd, 0x01, 0x23, ubxport, doGlonass ? 16 : 0); // UBX-NAV-TIMEGLO
}
if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling/disabling UBX-NAV-TIMEBDS"<<endl; } // Beidou time solution if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling/disabling UBX-NAV-TIMEBDS"<<endl; } // Beidou time solution
enableUBXMessageOnPort(fd, 0x01, 0x24, ubxport, doBeidou ? 16 : 0); // UBX-NAV-TIMEBDS enableUBXMessageOnPort(fd, 0x01, 0x24, ubxport, doBeidou ? 16 : 0); // UBX-NAV-TIMEBDS
@ -1107,7 +977,7 @@ int main(int argc, char** argv)
if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-NAV-POSECEF"<<endl; } // position if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-NAV-POSECEF"<<endl; } // position
enableUBXMessageOnPort(fd, 0x01, 0x01, ubxport, 8); // POSECEF enableUBXMessageOnPort(fd, 0x01, 0x01, ubxport, 8); // POSECEF
if(version9 || version10) { if(version9) {
if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-NAV-SIG"<<endl; } // satellite reception details if (doDEBUG) { cerr<<humanTimeNow()<<" Enabling UBX-NAV-SIG"<<endl; } // satellite reception details
enableUBXMessageOnPort(fd, 0x01, 0x43, ubxport, 8); // NAV-SIG enableUBXMessageOnPort(fd, 0x01, 0x43, ubxport, 8); // NAV-SIG
/* /*
@ -1138,7 +1008,6 @@ int main(int argc, char** argv)
*/ */
int curCycleTOW{-1}; // means invalid int curCycleTOW{-1}; // means invalid
int lastSatTOW{-1}; // last TOW decoded from an actual message
ns.d_compress = doCompress; ns.d_compress = doCompress;
ns.launch(); ns.launch();
@ -1238,7 +1107,6 @@ int main(int argc, char** argv)
for(;;) { for(;;) {
try { try {
auto [msg, timestamp] = getUBXMessage(fd, nullptr); auto [msg, timestamp] = getUBXMessage(fd, nullptr);
// cerr<<"Got something "<< (unsigned int) msg.getClass() <<" " << (unsigned int)msg.getType() << endl;
(void)timestamp; (void)timestamp;
auto payload = msg.getPayload(); auto payload = msg.getPayload();
@ -1408,7 +1276,7 @@ int main(int argc, char** argv)
uint32_t pAcc; uint32_t pAcc;
}; };
pos p; pos p;
memcpy(&p, &payload[0], sizeof(pos)); memcpy(&p, payload.c_str(), sizeof(pos));
if(fuzzPositionMeters) { if(fuzzPositionMeters) {
p.ecefX -= (p.ecefX % (fuzzPositionMeters*100)); p.ecefX -= (p.ecefX % (fuzzPositionMeters*100));
p.ecefY -= (p.ecefY % (fuzzPositionMeters*100)); p.ecefY -= (p.ecefY % (fuzzPositionMeters*100));
@ -1449,10 +1317,9 @@ int main(int argc, char** argv)
try { try {
pair<int,int> id = make_pair(payload[0], payload[1]); pair<int,int> id = make_pair(payload[0], payload[1]);
int sigid = payload[2]; int sigid = payload[2];
static set<tuple<int,int,int>> svseen; static set<tuple<int,int,int>> svseen;
static time_t lastStat; static time_t lastStat;
svseen.insert({id.first, id.second, sigid}); svseen.insert({id.first, id.second, payload[2]});
if(time(0)- lastStat > 30) { if(time(0)- lastStat > 30) {
cerr<<humanTimeNow()<<" src "<<g_srcid<< " (fix: "<<g_fixtype<<") currently receiving: "; cerr<<humanTimeNow()<<" src "<<g_srcid<< " (fix: "<<g_fixtype<<") currently receiving: ";
@ -1486,11 +1353,11 @@ int main(int argc, char** argv)
nmm.mutable_gpsi()->set_gnsstow(tow); // "with 6 second increments" -- needs to be adjusted nmm.mutable_gpsi()->set_gnsstow(tow); // "with 6 second increments" -- needs to be adjusted
nmm.mutable_gpsi()->set_gnssid(id.first); nmm.mutable_gpsi()->set_gnssid(id.first);
nmm.mutable_gpsi()->set_gnsssv(id.second); nmm.mutable_gpsi()->set_gnsssv(id.second);
nmm.mutable_gpsi()->set_contents(string((char*)&gpsframe[0], gpsframe.size())); nmm.mutable_gpsi()->set_contents(string((char*)gpsframe.c_str(), gpsframe.size()));
ns.emitNMM( nmm); ns.emitNMM( nmm);
continue; continue;
} }
else if(id.first == 0 && sigid) { // new GPS if(id.first == 0 && sigid) { // new GPS
auto cnav = getGPSFromSFRBXMsg(payload); auto cnav = getGPSFromSFRBXMsg(payload);
static int wn, tow; static int wn, tow;
@ -1514,59 +1381,11 @@ int main(int argc, char** argv)
nmm.mutable_gpsc()->set_gnsstow(tow); // "with 6 second increments" -- needs to be adjusted nmm.mutable_gpsc()->set_gnsstow(tow); // "with 6 second increments" -- needs to be adjusted
nmm.mutable_gpsc()->set_gnssid(id.first); nmm.mutable_gpsc()->set_gnssid(id.first);
nmm.mutable_gpsc()->set_gnsssv(id.second); nmm.mutable_gpsc()->set_gnsssv(id.second);
nmm.mutable_gpsc()->set_contents(string((char*)&cnav[0], cnav.size())); nmm.mutable_gpsc()->set_contents(string((char*)cnav.c_str(), cnav.size()));
ns.emitNMM( nmm); ns.emitNMM( nmm);
} }
else if(id.first ==2) { // GALILEO else if(id.first ==2) { // GALILEO
vector<uint8_t> reserved1, reserved2, sar, spare, crc; auto inav = getInavFromSFRBXMsg(payload);
uint8_t ssp;
if(sigid == 3) { // F/NAV
auto fnav = getFnavFromSFRBXMsg(payload, crc);
GalileoMessage gm;
struct Entry {
time_t when{-1};
GalileoMessage gm;
};
static map<int,Entry> last4;
// sequence is 1, 2, 3, 4, 5/6
// 5 and 6 have no WN or TOW, but will be +10 from 4 or +20 from last 3
gm.parseFnav(fnav);
if(gm.wtype == 4)
last4[id.second]={g_gnssutc.tv_sec, gm};
else if(gm.wtype == 5 || gm.wtype==6) {
if(g_gnssutc.tv_sec - last4[id.second].when < 15) {
gm.tow = last4[id.second].when + 10;
// cerr<<"Fixed up tow for wtype "<<(int)gm.wtype<<" based on last wtype 4 from " << g_gnssutc.tv_sec - last4[id.second].when << " seconds ago\n";
}
else {
// cerr<<"Could NOT fix up tow for wtype "<<(int)gm.wtype<<" based on last wtype 4 from " << g_gnssutc.tv_sec - last4[id.second].when << " seconds ago\n";
continue;
}
}
NavMonMessage nmm;
nmm.set_sourceid(g_srcid);
nmm.set_type(NavMonMessage::GalileoFnavType);
nmm.set_localutcseconds(g_gnssutc.tv_sec);
nmm.set_localutcnanoseconds(g_gnssutc.tv_nsec);
nmm.mutable_gf()->set_gnsswn(g_galwn);
nmm.mutable_gf()->set_gnsstow(gm.tow);
nmm.mutable_gf()->set_gnssid(id.first);
nmm.mutable_gf()->set_gnsssv(id.second);
nmm.mutable_gf()->set_sigid(6); // ubx calls it 3, but we previously picked 6 for E5a
nmm.mutable_gf()->set_contents((const char*)&fnav[0], fnav.size());
ns.emitNMM( nmm);
continue;
}
// I/NAV from hereon
auto inav = getInavFromSFRBXMsg(payload, reserved1, reserved2, sar, spare, crc, &ssp);
unsigned int wtype = getbitu(&inav[0], 0, 6); unsigned int wtype = getbitu(&inav[0], 0, 6);
uint32_t satTOW; uint32_t satTOW;
@ -1575,7 +1394,6 @@ int main(int argc, char** argv)
// if (doDEBUG) { cerr<<humanTimeNow()<<" "<<wtype<<" sv "<<id.second<<" tow "<<satTOW << " % 30 = "<< satTOW % 30<<", implied start of cycle: "<<(satTOW - (satTOW %30)) <<endl; } // if (doDEBUG) { cerr<<humanTimeNow()<<" "<<wtype<<" sv "<<id.second<<" tow "<<satTOW << " % 30 = "<< satTOW % 30<<", implied start of cycle: "<<(satTOW - (satTOW %30)) <<endl; }
msgTOW = satTOW; msgTOW = satTOW;
curCycleTOW = satTOW - (satTOW %30); curCycleTOW = satTOW - (satTOW %30);
lastSatTOW = satTOW;
} }
else { else {
if(curCycleTOW < 0) // did not yet have a start of cycle if(curCycleTOW < 0) // did not yet have a start of cycle
@ -1621,20 +1439,6 @@ int main(int argc, char** argv)
else if(wtype==8 || wtype == 10) { else if(wtype==8 || wtype == 10) {
msgTOW = curCycleTOW + 9; msgTOW = curCycleTOW + 9;
} }
else if(wtype==17 || wtype == 18) {
msgTOW = curCycleTOW + 11;
}
else if(wtype==19 || wtype == 20) {
msgTOW = curCycleTOW + 13;
}
else if(wtype==16) {
if(lastSatTOW <= curCycleTOW + 15){ // "<=" rather than "<" since sometimes we get at TOW at this index (WT0 or WT5)
msgTOW = curCycleTOW + 15;
}
else {
msgTOW = curCycleTOW + 29;
}
}
else if(wtype==1) { else if(wtype==1) {
msgTOW = curCycleTOW + 21; msgTOW = curCycleTOW + 21;
} }
@ -1662,20 +1466,10 @@ int main(int argc, char** argv)
nmm.mutable_gi()->set_gnsssv(id.second); nmm.mutable_gi()->set_gnsssv(id.second);
nmm.mutable_gi()->set_sigid(sigid); nmm.mutable_gi()->set_sigid(sigid);
nmm.mutable_gi()->set_contents((const char*)&inav[0], inav.size()); 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());
nmm.mutable_gi()->set_ssp((unsigned int)ssp);
ns.emitNMM( nmm); ns.emitNMM( nmm);
} }
else if(id.first==3) { else if(id.first==3) {
try {
if(version10 && sigid==8)
continue;
auto gstr = getGlonassFromSFRBXMsg(payload); auto gstr = getGlonassFromSFRBXMsg(payload);
auto cond = getCondensedBeidouMessage(gstr); auto cond = getCondensedBeidouMessage(gstr);
static map<int, BeidouMessage> bms; static map<int, BeidouMessage> bms;
@ -1700,7 +1494,7 @@ int main(int argc, char** argv)
nmm.mutable_bid1()->set_gnssid(id.first); nmm.mutable_bid1()->set_gnssid(id.first);
nmm.mutable_bid1()->set_gnsssv(id.second); nmm.mutable_bid1()->set_gnsssv(id.second);
nmm.mutable_bid1()->set_sigid(sigid); nmm.mutable_bid1()->set_sigid(sigid);
nmm.mutable_bid1()->set_contents(string((char*)&gstr[0], gstr.size())); nmm.mutable_bid1()->set_contents(string((char*)gstr.c_str(), gstr.size()));
ns.emitNMM( nmm); ns.emitNMM( nmm);
} }
else { else {
@ -1716,10 +1510,7 @@ int main(int argc, char** argv)
*/ */
} }
}
catch(std::exception& e) {
cerr<<"Failure to parse BeiDou message (sigid="<<sigid<<"): "<<e.what() << endl;
}
continue; continue;
} }
else if(id.first==6) { else if(id.first==6) {
@ -1740,7 +1531,7 @@ int main(int argc, char** argv)
nmm.mutable_gloi()->set_gnssid(id.first); nmm.mutable_gloi()->set_gnssid(id.first);
nmm.mutable_gloi()->set_gnsssv(id.second); nmm.mutable_gloi()->set_gnsssv(id.second);
nmm.mutable_gloi()->set_sigid(sigid); nmm.mutable_gloi()->set_sigid(sigid);
nmm.mutable_gloi()->set_contents(string((char*)&gstr[0], gstr.size())); nmm.mutable_gloi()->set_contents(string((char*)gstr.c_str(), gstr.size()));
ns.emitNMM( nmm); ns.emitNMM( nmm);
} }
@ -1760,7 +1551,7 @@ int main(int argc, char** argv)
nmm.set_type(NavMonMessage::SBASMessageType); nmm.set_type(NavMonMessage::SBASMessageType);
nmm.mutable_sbm()->set_gnssid(id.first); nmm.mutable_sbm()->set_gnssid(id.first);
nmm.mutable_sbm()->set_gnsssv(id.second); nmm.mutable_sbm()->set_gnsssv(id.second);
nmm.mutable_sbm()->set_contents(string((char*)&sbas[0], sbas.size())); nmm.mutable_sbm()->set_contents(string((char*)sbas.c_str(), sbas.size()));
ns.emitNMM( nmm); ns.emitNMM( nmm);
} }
@ -1796,7 +1587,7 @@ int main(int argc, char** argv)
nmm.mutable_rd()->set_db(db); nmm.mutable_rd()->set_db(db);
nmm.mutable_rd()->set_el(el); nmm.mutable_rd()->set_el(el);
nmm.mutable_rd()->set_azi(azi); nmm.mutable_rd()->set_azi(azi);
nmm.mutable_rd()->set_prres(*((int16_t*)(&payload[0] + 14 +12*n)) *0.1); nmm.mutable_rd()->set_prres(*((int16_t*)(payload.c_str()+ 14 +12*n)) *0.1);
uint32_t status; uint32_t status;
memcpy(&status, &payload[16+12*n], 4); memcpy(&status, &payload[16+12*n], 4);
@ -1830,15 +1621,12 @@ int main(int argc, char** argv)
memcpy(&sigflags, &payload[18+16*n], 2); memcpy(&sigflags, &payload[18+16*n], 2);
int sigid = 0; int sigid = 0;
if(version9 || version10) { // we only use this on versions 9 and 10 right now tho if(version9) { // we only use this on version9 right now tho
sigid = payload[10+16*n]; sigid = payload[10+16*n];
if(gnssid == 2 && sigid ==6) // they separate out I and Q, but the rest of UBX doesn't if(gnssid == 2 && sigid ==6) // they separate out I and Q, but the rest of UBX doesn't
sigid = 5; // so map it back sigid = 5; // so map it back
if(gnssid == 2 && sigid ==0) if(gnssid == 2 && sigid ==0)
sigid = 1; sigid = 1;
if(gnssid == 2 && sigid == 4) // map E5a
sigid = 6;
if(gnssid ==0) { if(gnssid ==0) {
if(sigid==3) // L2C is sent as '4' and '3', but the '4' numbers here are bogus if(sigid==3) // L2C is sent as '4' and '3', but the '4' numbers here are bogus
sigid=4; // if we see 3, use it, and change number to 4 to be consistent sigid=4; // if we see 3, use it, and change number to 4 to be consistent
@ -1861,7 +1649,7 @@ int main(int argc, char** argv)
nmm.mutable_rd()->set_gnssid(gnssid); nmm.mutable_rd()->set_gnssid(gnssid);
nmm.mutable_rd()->set_gnsssv(sv); nmm.mutable_rd()->set_gnsssv(sv);
nmm.mutable_rd()->set_db(db); nmm.mutable_rd()->set_db(db);
nmm.mutable_rd()->set_prres(*((int16_t*)(&payload[0] + 12 +16*n)) *0.1); // ENDIANISM nmm.mutable_rd()->set_prres(*((int16_t*)(payload.c_str()+ 12 +16*n)) *0.1); // ENDIANISM
nmm.mutable_rd()->set_sigid(sigid); nmm.mutable_rd()->set_sigid(sigid);
nmm.mutable_rd()->set_el(0); nmm.mutable_rd()->set_el(0);
nmm.mutable_rd()->set_azi(0); nmm.mutable_rd()->set_azi(0);
@ -1909,7 +1697,7 @@ int main(int argc, char** argv)
nmm.mutable_od()->set_owner(owner); nmm.mutable_od()->set_owner(owner);
nmm.mutable_od()->set_remark(remark); nmm.mutable_od()->set_remark(remark);
nmm.mutable_od()->set_recvgithash(g_gitHash); nmm.mutable_od()->set_recvgithash(g_gitHash);
nmm.mutable_od()->set_uptime(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now()-starttime).count()); nmm.mutable_od()->set_uptime(time(0) - starttime);
ns.emitNMM( nmm); ns.emitNMM( nmm);
@ -1951,14 +1739,14 @@ int main(int argc, char** argv)
nmm.mutable_sr()->set_gnsssv(payload[2]); nmm.mutable_sr()->set_gnsssv(payload[2]);
nmm.mutable_sr()->set_sigid(1); // nmm.mutable_sr()->set_sigid(1); //
nmm.mutable_sr()->set_type(payload[1]); nmm.mutable_sr()->set_type(payload[1]);
nmm.mutable_sr()->set_identifier(string((char*)&payload[0] +4, 8)); nmm.mutable_sr()->set_identifier(string((char*)payload.c_str()+4, 8));
nmm.mutable_sr()->set_code(payload[12]); nmm.mutable_sr()->set_code(payload[12]);
nmm.mutable_sr()->set_params(string((char*)&payload[0] + 13, payload.size()-14)); nmm.mutable_sr()->set_params(string((char*)payload.c_str()+13, payload.size()-14));
string hexstring; string hexstring;
for(int n = 0; n < 15; ++n) for(int n = 0; n < 15; ++n)
hexstring+=fmt::sprintf("%x", (int)getbitu(&payload[0], 36 + 4*n, 4)); hexstring+=fmt::sprintf("%x", (int)getbitu(payload.c_str(), 36 + 4*n, 4));
ns.emitNMM(nmm); ns.emitNMM(nmm);
} }
@ -1996,7 +1784,7 @@ int main(int argc, char** argv)
cerr<<"Wrong NAV-SVIN message size, skipping"<<endl; cerr<<"Wrong NAV-SVIN message size, skipping"<<endl;
continue; continue;
} }
memcpy(&NS, &payload[0], sizeof(NS)); memcpy(&NS, payload.c_str(), sizeof(NS));
NS.res[0] = NS.res[1] = NS.res[2] = 0; NS.res[0] = NS.res[1] = NS.res[2] = 0;
NS.res2=0; NS.res2=0;
NS.res3[0] = NS.res3[1] = 0; NS.res3[0] = NS.res3[1] = 0;
@ -2030,7 +1818,7 @@ int main(int argc, char** argv)
cerr<<"Wrong NAV-SVIN message size, skipping"<<endl; cerr<<"Wrong NAV-SVIN message size, skipping"<<endl;
continue; continue;
} }
memcpy(&TS, &payload[0], sizeof(TS)); memcpy(&TS, payload.c_str(), sizeof(TS));
TS.res3[0] = TS.res3[1] = 0; TS.res3[0] = TS.res3[1] = 0;
if(memcmp(&TS, &lastTS, sizeof(TS))) { if(memcmp(&TS, &lastTS, sizeof(TS))) {
cerr<<humanTimeNow()<<" TIM-SVIN valid "<< (int)TS.valid<<" active " << (int)TS.active<<" duration "<<TS.dur<<"s meanAcc " <<sqrt(TS.meanVar)/10<< "cm obs "<<TS.obs<<" "; cerr<<humanTimeNow()<<" TIM-SVIN valid "<< (int)TS.valid<<" active " << (int)TS.active<<" duration "<<TS.dur<<"s meanAcc " <<sqrt(TS.meanVar)/10<< "cm obs "<<TS.obs<<" ";
@ -2058,7 +1846,7 @@ int main(int argc, char** argv)
uint32_t pinIrq, pullH, pullL; uint32_t pinIrq, pullH, pullL;
} __attribute__((packed)); } __attribute__((packed));
MonHW mhw; MonHW mhw;
memcpy(&mhw, &payload[0], sizeof(MonHW)); memcpy(&mhw, payload.c_str(), sizeof(MonHW));
// cerr << "agcCnt "<< mhw.agcCnt <<" jamind " << (unsigned int) mhw.jamInd <<" flags "<< (unsigned int)mhw.flags << endl; // cerr << "agcCnt "<< mhw.agcCnt <<" jamind " << (unsigned int) mhw.jamInd <<" flags "<< (unsigned int)mhw.flags << endl;
NavMonMessage nmm; NavMonMessage nmm;
nmm.set_sourceid(g_srcid); nmm.set_sourceid(g_srcid);

View file

@ -149,21 +149,17 @@ void ZStdReader::worker()
for(;;) { for(;;) {
input.pos=0; input.pos=0;
int ret = read(d_sourcefd, (char*)input.src, inputcapacity); input.size=read(d_sourcefd, (char*)input.src, inputcapacity);
if(ret <= 0) { if(input.size <= 0) {
// cerr<<"Got EOF on input fd "<<d_sourcefd<<", terminating thread"<<endl; cerr<<"Got EOF on input fd "<<d_sourcefd<<", terminating thread"<<endl;
break; break;
} }
input.size = ret; // this is unsigned, so we need 'ret' to see the error
while(input.pos != input.size) { while(input.pos != input.size) {
output.pos=0; output.pos=0;
output.size=outputcapacity; output.size=outputcapacity;
int res = ZSTD_decompressStream(z, &output, &input); 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); res = writen(d_writepipe, output.dst, output.pos);
if(!res) // we are history if(!res) // we are history
break; break;
@ -179,6 +175,10 @@ void ZStdReader::worker()
ZStdReader::~ZStdReader() ZStdReader::~ZStdReader()
{ {
cerr<<"ZStdReader destructor called"<<endl;
int rc = close(d_readpipe); int rc = close(d_readpipe);
cerr<<"Close rc = "<<rc<<endl;
cerr<<"Waiting on join"<<endl;
d_thread.join(); d_thread.join();
cerr<<"Done waiting on join"<<endl;
} }