mirror of
https://github.com/berthubert/galmon.git
synced 2026-05-16 14:13:17 -04:00
Compare commits
No commits in common. "master" and "eph-store" have entirely different histories.
85 changed files with 13146 additions and 6021 deletions
12
.github/workflows/ccpp.yml
vendored
12
.github/workflows/ccpp.yml
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
|
|
@ -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 \
|
||||||
|
|
|
||||||
47
Dockerfile
47
Dockerfile
|
|
@ -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
18
Dockerfile-pi
Normal 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}
|
||||||
86
Makefile
86
Makefile
|
|
@ -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
136
README.md
|
|
@ -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
|
||||||
----
|
----
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
23
beidou.hh
23
beidou.hh
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
2929
ext/doctest.h
2929
ext/doctest.h
File diff suppressed because it is too large
Load diff
27
ext/fmt-6.1.2/LICENSE.rst
Normal file
27
ext/fmt-6.1.2/LICENSE.rst
Normal 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
501
ext/fmt-6.1.2/README.rst
Normal 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.
|
||||||
1106
ext/fmt-6.1.2/include/fmt/chrono.h
Normal file
1106
ext/fmt-6.1.2/include/fmt/chrono.h
Normal file
File diff suppressed because it is too large
Load diff
570
ext/fmt-6.1.2/include/fmt/color.h
Normal file
570
ext/fmt-6.1.2/include/fmt/color.h
Normal 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_
|
||||||
585
ext/fmt-6.1.2/include/fmt/compile.h
Normal file
585
ext/fmt-6.1.2/include/fmt/compile.h
Normal 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_
|
||||||
1519
ext/fmt-6.1.2/include/fmt/core.h
Normal file
1519
ext/fmt-6.1.2/include/fmt/core.h
Normal file
File diff suppressed because it is too large
Load diff
1396
ext/fmt-6.1.2/include/fmt/format-inl.h
Normal file
1396
ext/fmt-6.1.2/include/fmt/format-inl.h
Normal file
File diff suppressed because it is too large
Load diff
3541
ext/fmt-6.1.2/include/fmt/format.h
Normal file
3541
ext/fmt-6.1.2/include/fmt/format.h
Normal file
File diff suppressed because it is too large
Load diff
77
ext/fmt-6.1.2/include/fmt/locale.h
Normal file
77
ext/fmt-6.1.2/include/fmt/locale.h
Normal 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_
|
||||||
141
ext/fmt-6.1.2/include/fmt/ostream.h
Normal file
141
ext/fmt-6.1.2/include/fmt/ostream.h
Normal 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_
|
||||||
321
ext/fmt-6.1.2/include/fmt/posix.h
Normal file
321
ext/fmt-6.1.2/include/fmt/posix.h
Normal 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_
|
||||||
711
ext/fmt-6.1.2/include/fmt/printf.h
Normal file
711
ext/fmt-6.1.2/include/fmt/printf.h
Normal 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_
|
||||||
365
ext/fmt-6.1.2/include/fmt/ranges.h
Normal file
365
ext/fmt-6.1.2/include/fmt/ranges.h
Normal 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
176
ext/fmt-6.1.2/src/format.cc
Normal 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
237
ext/fmt-6.1.2/src/posix.cc
Normal 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
|
||||||
218
fixhunter.cc
218
fixhunter.cc
|
|
@ -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;
|
|
||||||
}
|
|
||||||
15
fixhunter.hh
15
fixhunter.hh
|
|
@ -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;
|
|
||||||
};
|
|
||||||
138
galileo.cc
138
galileo.cc
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
168
galileo.hh
168
galileo.hh
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
47
galmonmon.cc
47
galmonmon.cc
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
glonass.cc
13
glonass.cc
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
glonass.hh
21
glonass.hh
|
|
@ -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);
|
||||||
|
|
|
||||||
28
gndate.cc
28
gndate.cc
|
|
@ -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
14
gps.cc
|
|
@ -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
7
gps.hh
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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 += " <a href='sv.html?gnssid="+row.gnssid+"&sv="+row.svid+"&sigid="+row.sigid+"'>"+row.sv+"</a>";
|
ret.value += " <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)
|
||||||
|
|
|
||||||
BIN
html/favicon.ico
BIN
html/favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 198 B |
|
|
@ -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>
|
<input type="checkbox" id="GalE1" onclick="updateSats();"> <label for="GalE1">Galileo E1</label>
|
||||||
<input type="checkbox" id="GalE5a" onclick="updateSats();"> <label for="GalE5a">Galileo E5a</label>
|
|
||||||
<input type="checkbox" id="GalE5b" onclick="updateSats();"> <label for="GalE5b">Galileo E5b</label>
|
<input type="checkbox" id="GalE5b" onclick="updateSats();"> <label for="GalE5b">Galileo E5b</label>
|
||||||
<input type="checkbox" id="BeiDouB1I" onclick="updateSats();"> <label for="BeiDouB1I">BeiDou B1I</label>
|
<input type="checkbox" id="BeiDouB1I" onclick="updateSats();"> <label for="BeiDouB1I">BeiDou B1I</label>
|
||||||
<input type="checkbox" id="BeiDouB2I" onclick="updateSats();"> <label for="BeiDouB2I">BeiDou B2I</label>
|
<input type="checkbox" id="BeiDouB2I" onclick="updateSats();"> <label for="BeiDouB2I">BeiDou B2I</label>
|
||||||
|
|
@ -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>last‑seen‑s</td><td>Time since we've last received from this SV.</td></tr>
|
<tr><td>last‑seen‑s</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&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&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>
|
||||||
|
|
|
||||||
|
|
@ -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>.
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>.
|
||||||
|
|
|
||||||
|
|
@ -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"]);
|
||||||
|
|
|
||||||
|
|
@ -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>.
|
||||||
|
|
|
||||||
|
|
@ -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>.
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ function maketable(str, arr)
|
||||||
|
|
||||||
ret.value = sbas + " ";
|
ret.value = sbas + " ";
|
||||||
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="";
|
||||||
|
|
|
||||||
|
|
@ -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>.
|
||||||
|
|
|
||||||
|
|
@ -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>.
|
||||||
|
|
|
||||||
28
influxdb.md
28
influxdb.md
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
|
||||||
78
minicurl.cc
78
minicurl.cc
|
|
@ -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 || http_code != 200) {
|
||||||
|
throw std::runtime_error("Unable to retrieve URL ("+std::to_string(http_code)+"): "+string(curl_easy_strerror(res)));
|
||||||
}
|
}
|
||||||
if(res != CURLE_OK) {
|
|
||||||
throw std::runtime_error("Unable to retrieve URL "+str+ " - "+string(curl_easy_strerror(res)));
|
|
||||||
}
|
|
||||||
|
|
||||||
d_filetime=-1;
|
|
||||||
curl_easy_getinfo(d_curl, CURLINFO_FILETIME, &d_filetime);
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
||||||
13
minicurl.hh
13
minicurl.hh
|
|
@ -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);
|
|
||||||
|
|
|
||||||
13
minivec.hh
13
minivec.hh
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
134
navcat.cc
134
navcat.cc
|
|
@ -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(;;) {
|
||||||
|
cerr<<"Gathering data"<<endl;
|
||||||
|
vector<uint64_t> srcs = stations.empty() ? getSources(dir) : stations;
|
||||||
rnmms.clear();
|
rnmms.clear();
|
||||||
for(const auto& dir : dirs) {
|
for(const auto& src: srcs) {
|
||||||
cerr<<"Gathering data from "<<humanTime(start.tv_sec)<<" from "<<dir<<".. ";
|
string fname = getPath(dir, start.tv_sec, src);
|
||||||
|
FILE* fp = fopen(fname.c_str(), "r");
|
||||||
vector<uint64_t> srcs = stations.empty() ? getSources(dir) : stations;
|
if(!fp)
|
||||||
int count=0;
|
continue;
|
||||||
for(const auto& src: srcs) {
|
uint32_t offset= fpos[fname];
|
||||||
string fname = getPath(dir, start.tv_sec, src);
|
if(fseek(fp, offset, SEEK_SET) < 0) {
|
||||||
FILE* ptr = fopen(fname.c_str(), "r");
|
cerr<<"Error seeking: "<<strerror(errno) <<endl;
|
||||||
shared_ptr<FILE> fp;
|
fclose(fp);
|
||||||
if(ptr) {
|
continue;
|
||||||
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;
|
|
||||||
fp = shared_ptr<FILE>(ptr, pclose);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string msg;
|
|
||||||
struct timespec ts;
|
|
||||||
unsigned int offset=0;
|
|
||||||
while(getRawNMM(fp.get(), ts, msg, offset)) {
|
|
||||||
// don't drop data that is only 5 seconds too old
|
|
||||||
if(make_pair(ts.tv_sec + 5, ts.tv_nsec) >= make_pair(start.tv_sec, start.tv_nsec)) {
|
|
||||||
rnmms.push_back({ts, msg});
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// cerr<<"Harvested "<<rnmms.size()<<" events out of "<<looked<<endl;
|
|
||||||
// fp will close itself
|
|
||||||
}
|
}
|
||||||
cerr<<" added "<<count<<endl;
|
// cerr <<"Seeked to position "<<fpos[fname]<<" of "<<fname<<endl;
|
||||||
|
|
||||||
|
uint32_t looked=0;
|
||||||
|
string msg;
|
||||||
|
struct timespec ts;
|
||||||
|
while(getRawNMM(fp, ts, msg, offset)) {
|
||||||
|
// don't drop data that is only 5 seconds too old
|
||||||
|
if(make_pair(ts.tv_sec + 5, ts.tv_nsec) >= make_pair(start.tv_sec, start.tv_nsec)) {
|
||||||
|
rnmms.push_back({ts, msg});
|
||||||
|
}
|
||||||
|
++looked;
|
||||||
|
}
|
||||||
|
// cerr<<"Harvested "<<rnmms.size()<<" events out of "<<looked<<endl;
|
||||||
|
fpos[fname]=offset;
|
||||||
|
fclose(fp);
|
||||||
}
|
}
|
||||||
// cerr<<"Sorting data"<<endl;
|
|
||||||
// sort(execution::par, rnmms.begin(), rnmms.end(), [](const auto& a, const auto& b)
|
cerr<<"Sorting data"<<endl;
|
||||||
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) );
|
||||||
|
|
|
||||||
322
navdump.cc
322
navdump.cc
|
|
@ -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,34 +1014,30 @@ 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();
|
if(nmm.od().has_owner())
|
||||||
if(nmm.od().has_owner())
|
cout<<" owner "<<nmm.od().owner();
|
||||||
cout<<" owner "<<nmm.od().owner();
|
if(nmm.od().has_clockoffsetdriftns())
|
||||||
if(nmm.od().has_clockoffsetdriftns())
|
cout<<" drift "<<nmm.od().clockoffsetdriftns();
|
||||||
cout<<" drift "<<nmm.od().clockoffsetdriftns();
|
if(nmm.od().has_clockaccuracyns())
|
||||||
if(nmm.od().has_clockaccuracyns())
|
cout<<" clock-accuracy "<<nmm.od().clockaccuracyns();
|
||||||
cout<<" clock-accuracy "<<nmm.od().clockaccuracyns();
|
|
||||||
|
|
||||||
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 {
|
||||||
|
|
|
||||||
286
navmerge.cc
286
navmerge.cc
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
63
navmon.cc
63
navmon.cc
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
13
navmon.hh
13
navmon.hh
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
29
navmon.proto
29
navmon.proto
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
navnexus.cc
20
navnexus.cc
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
549
navparse.cc
549
navparse.cc
|
|
@ -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");
|
||||||
|
|
@ -666,7 +651,7 @@ try
|
||||||
// feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW );
|
// feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW );
|
||||||
|
|
||||||
|
|
||||||
// g_tles.parseFile("active.txt");
|
// g_tles.parseFile("active.txt");
|
||||||
|
|
||||||
g_tles.parseFile("galileo.txt");
|
g_tles.parseFile("galileo.txt");
|
||||||
g_tles.parseFile("glo-ops.txt");
|
g_tles.parseFile("glo-ops.txt");
|
||||||
|
|
@ -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},
|
|
||||||
{"exred", svstat.galmsg.a1g},
|
|
||||||
{"eyred", svstat.galmsg.t0g},
|
|
||||||
{"deltai0red", svstat.galmsg.deltai0red},
|
|
||||||
{"omega0red", svstat.galmsg.omega0red},
|
|
||||||
{"lambda0red", svstat.galmsg.lambda0red},
|
|
||||||
{"af0red", svstat.galmsg.af0red},
|
|
||||||
{"af1red", svstat.galmsg.af1red},
|
|
||||||
{"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) {
|
|
||||||
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];
|
// CONVERSION XXX
|
||||||
svstat.gnss = id.gnss;
|
#if 0
|
||||||
auto oldgm = svstat.galmsg;
|
for(auto& ent : g_svstats) {
|
||||||
|
// fmt::printf("%2d\t", ent.first);
|
||||||
|
id=ent.first;
|
||||||
|
if(ent.second.completeIOD() && ent.second.prevIOD.first >= 0) {
|
||||||
|
|
||||||
auto& gm = svstat.galmsg;
|
ent.second.clearPrev();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
|
|
||||||
svstat.perrecv[nmm.sourceid()].t = nmm.localutcseconds();
|
|
||||||
|
|
||||||
if(wtype >=1 && wtype <= 4) { // ephemeris
|
|
||||||
if(wtype == 1) {
|
|
||||||
idb.addValue(id, "sisa", {{"value", svstat.galmsg.sisa}}, satUTCTime(id));
|
|
||||||
|
|
||||||
idb.addValue(id, "galbgd", {
|
|
||||||
{"BGDE1E5a", svstat.galmsg.BGDE1E5a},
|
|
||||||
}, satUTCTime(id));
|
|
||||||
|
|
||||||
|
|
||||||
idb.addValue(id, "galhealth", {
|
|
||||||
{"e5ahs", svstat.galmsg.e5bhs},
|
|
||||||
{"e5advs", svstat.galmsg.e5bdvs}
|
|
||||||
}, satUTCTime(id));
|
|
||||||
|
|
||||||
|
|
||||||
idb.addValue(id, "clock", {{"offset_ns", svstat.galmsg.getAtomicOffset(svstat.tow()).first},
|
|
||||||
{"t0c", svstat.galmsg.t0c*60}, // getT0c()??
|
|
||||||
{"af0", svstat.galmsg.af0},
|
|
||||||
{"af1", svstat.galmsg.af1},
|
|
||||||
{"af2", svstat.galmsg.af2}}, satUTCTime(id));
|
|
||||||
|
|
||||||
if(oldgm.af0 && oldgm.t0c != svstat.galmsg.t0c) {
|
|
||||||
auto oldOffset = oldgm.getAtomicOffset(svstat.tow());
|
|
||||||
auto newOffset = svstat.galmsg.getAtomicOffset(svstat.tow());
|
|
||||||
svstat.timeDisco = oldOffset.first - newOffset.first;
|
|
||||||
if(fabs(svstat.timeDisco) < 10000)
|
|
||||||
idb.addValue(id, "clock_jump_ns", {
|
|
||||||
{"jump", svstat.timeDisco},
|
|
||||||
{"duration", ephAge(svstat.galmsg.t0c * 60, oldgm.t0c * 60)},
|
|
||||||
{"old-af0", oldgm.af0},
|
|
||||||
{"old-af1", oldgm.af1},
|
|
||||||
{"old-af2", oldgm.af2},
|
|
||||||
{"old-t0c", oldgm.t0c * 60},
|
|
||||||
{"new-af0", svstat.galmsg.af0},
|
|
||||||
{"new-af1", svstat.galmsg.af1},
|
|
||||||
{"new-af2", svstat.galmsg.af2},
|
|
||||||
{"new-t0c", svstat.galmsg.t0c * 60}
|
|
||||||
|
|
||||||
}, satUTCTime(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
idb.addValue(id, "iono", {
|
|
||||||
{"ai0", svstat.galmsg.ai0},
|
|
||||||
{"ai1", svstat.galmsg.ai1},
|
|
||||||
{"ai2", svstat.galmsg.ai2},
|
|
||||||
{"sf1", svstat.galmsg.sf1},
|
|
||||||
{"sf2", svstat.galmsg.sf2},
|
|
||||||
{"sf3", svstat.galmsg.sf3},
|
|
||||||
{"sf4", svstat.galmsg.sf4},
|
|
||||||
{"sf5", svstat.galmsg.sf5}}, satUTCTime(id));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(wtype == 4) {
|
|
||||||
const auto& sv = g_svstats[id];
|
|
||||||
g_GSTUTCOffset = sv.galmsg.getUTCOffset(sv.tow(), sv.wn()).first;
|
|
||||||
idb.addValue(id, "utcoffset", {
|
|
||||||
{"a0", sv.galmsg.a0},
|
|
||||||
{"a1", sv.galmsg.a1},
|
|
||||||
{"t0t", sv.galmsg.t0t},
|
|
||||||
{"delta", g_GSTUTCOffset}
|
|
||||||
},
|
|
||||||
satUTCTime(id));
|
|
||||||
|
|
||||||
g_dtLS = sv.galmsg.dtLS;
|
|
||||||
g_GSTGPSOffset = gm.getGPSOffset(gm.tow, gm.wn).first;
|
|
||||||
idb.addValue(id, "gpsoffset", {{"a0g", svstat.galmsg.a0g},
|
|
||||||
{"a1g", svstat.galmsg.a1g},
|
|
||||||
{"t0g", svstat.galmsg.t0g},
|
|
||||||
{"wn0g", svstat.galmsg.wn0g},
|
|
||||||
{"delta", g_GSTGPSOffset}
|
|
||||||
}, satUTCTime(id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(nmm.type() == NavMonMessage::ObserverPositionType) {
|
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
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
123
navrecv.cc
123
navrecv.cc
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
315
nmmsender.cc
315
nmmsender.cc
|
|
@ -1,98 +1,14 @@
|
||||||
#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)
|
|
||||||
{
|
|
||||||
bool hadMessage=false;
|
|
||||||
int msgnum = 0;
|
|
||||||
|
|
||||||
for(;;) {
|
|
||||||
uint32_t num;
|
|
||||||
// read acks
|
|
||||||
for(; zsc ;) { // only do this for compressed protocol
|
|
||||||
try {
|
|
||||||
readn2(s, &num, 4); // this will give us 4, or throw
|
|
||||||
num = ntohl(num);
|
|
||||||
unacked.erase(num);
|
|
||||||
}
|
|
||||||
catch(EofException& ee) {
|
|
||||||
throw std::runtime_error("EOF while reading acks");
|
|
||||||
}
|
|
||||||
catch(std::exception& e) {
|
|
||||||
if(errno != EAGAIN)
|
|
||||||
unixDie("Reading acknowledgements in nmmsender");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::string msg;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> mut(d->mut);
|
|
||||||
if(!d->queue.empty()) {
|
|
||||||
msg = d->queue.front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!msg.empty()) {
|
|
||||||
hadMessage=true;
|
|
||||||
if(zsc) {
|
|
||||||
uint32_t num = htonl(msgnum);
|
|
||||||
string encap((const char*)&num, 4);
|
|
||||||
encap += msg;
|
|
||||||
zsc->give(encap.c_str(), encap.size());
|
|
||||||
unacked[msgnum] = msg;
|
|
||||||
msgnum++;
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
sc.writen(msg);
|
|
||||||
std::lock_guard<std::mutex> mut(d->mut);
|
|
||||||
d->queue.pop_front();
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(zsc && hadMessage) {
|
|
||||||
// cerr << "Compressed to: "<< 100.0*zsc->d_outputBytes/zsc->d_inputBytes<<"%, buffered compressed: "<<zsc->outputBufferBytes()<<" out of " <<zsc->outputBufferCapacity()<<" bytes. Unacked: "<<unacked.size()<<endl;
|
|
||||||
|
|
||||||
zsc->flush();
|
|
||||||
|
|
||||||
if(time(0) - connStartTime > 10 && unacked.size() > 1000)
|
|
||||||
throw std::runtime_error("Too many messages unacked ("+to_string(unacked.size())+"), recycling connection");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
hadMessage = false;
|
|
||||||
if(d_pleaseQuit)
|
|
||||||
return;
|
|
||||||
usleep(100000);
|
|
||||||
#if defined(TCP_CORK)
|
|
||||||
/* linux-only: has an implied 200ms timeout */
|
|
||||||
SSetsockopt(s, IPPROTO_TCP, TCP_CORK, 1 );
|
|
||||||
#elif defined(TCP_NOPUSH)
|
|
||||||
/*
|
|
||||||
* freebsd/osx: buffers until buffer full/connection closed, so
|
|
||||||
* we toggle it every other loop through
|
|
||||||
*/
|
|
||||||
static bool push_toggle;
|
|
||||||
if (push_toggle) {
|
|
||||||
SSetsockopt(s, IPPROTO_TCP, TCP_NOPUSH, 0 );
|
|
||||||
SSetsockopt(s, IPPROTO_TCP, TCP_NOPUSH, 1 );
|
|
||||||
}
|
|
||||||
push_toggle = !push_toggle;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// this does all kinds of resolving based on a *string* destination
|
|
||||||
void NMMSender::sendTCPThread(Destination* d)
|
void NMMSender::sendTCPThread(Destination* d)
|
||||||
{
|
{
|
||||||
struct NameError{};
|
struct NameError{};
|
||||||
|
|
@ -139,8 +55,88 @@ void NMMSender::sendTCPThread(Destination* d)
|
||||||
// the 00000000 is a placeholder for a "secret" we might implement later
|
// the 00000000 is a placeholder for a "secret" we might implement later
|
||||||
zsc = std::make_unique<ZStdCompressor>(emit, 9);
|
zsc = std::make_unique<ZStdCompressor>(emit, 9);
|
||||||
}
|
}
|
||||||
|
bool hadMessage=false;
|
||||||
|
int msgnum = 0;
|
||||||
|
|
||||||
sendLoop(d, sc, zsc, s, unacked, connStartTime);
|
for(;;) {
|
||||||
|
uint32_t num;
|
||||||
|
// read acks
|
||||||
|
for(; zsc ;) { // only do this for compressed protocol
|
||||||
|
try {
|
||||||
|
readn2(s, &num, 4); // this will give us 4, or throw
|
||||||
|
num = ntohl(num);
|
||||||
|
unacked.erase(num);
|
||||||
|
}
|
||||||
|
catch(EofException& ee) {
|
||||||
|
throw std::runtime_error("EOF while reading acks");
|
||||||
|
}
|
||||||
|
catch(std::exception& e) {
|
||||||
|
if(errno != EAGAIN)
|
||||||
|
unixDie("Reading acknowledgements in nmmsender");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string msg;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> mut(d->mut);
|
||||||
|
if(!d->queue.empty()) {
|
||||||
|
msg = d->queue.front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!msg.empty()) {
|
||||||
|
hadMessage=true;
|
||||||
|
if(zsc) {
|
||||||
|
|
||||||
|
uint32_t num = htonl(msgnum);
|
||||||
|
string encap((const char*)&num, 4);
|
||||||
|
encap += msg;
|
||||||
|
zsc->give(encap.c_str(), encap.size());
|
||||||
|
unacked[msgnum] = msg;
|
||||||
|
msgnum++;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sc.writen(msg);
|
||||||
|
std::lock_guard<std::mutex> mut(d->mut);
|
||||||
|
d->queue.pop_front();
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(zsc && hadMessage) {
|
||||||
|
// cerr << "Compressed to: "<< 100.0*zsc->d_outputBytes/zsc->d_inputBytes<<"%, buffered compressed: "<<zsc->outputBufferBytes()<<" out of " <<zsc->outputBufferCapacity()<<" bytes. Unacked: "<<unacked.size()<<endl;
|
||||||
|
|
||||||
|
zsc->flush();
|
||||||
|
|
||||||
|
if(time(0) - connStartTime > 10 && unacked.size() > 1000)
|
||||||
|
throw std::runtime_error("Too many messages unacked ("+to_string(unacked.size())+"), recycling connection");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
hadMessage = false;
|
||||||
|
if(d_pleaseQuit)
|
||||||
|
return;
|
||||||
|
usleep(100000);
|
||||||
|
#if defined(TCP_CORK)
|
||||||
|
/* linux-only: has an implied 200ms timeout */
|
||||||
|
SSetsockopt(s, IPPROTO_TCP, TCP_CORK, 1 );
|
||||||
|
#elif defined(TCP_NOPUSH)
|
||||||
|
/*
|
||||||
|
* freebsd/osx: buffers until buffer full/connection closed, so
|
||||||
|
* we toggle it every other loop through
|
||||||
|
*/
|
||||||
|
static bool push_toggle;
|
||||||
|
if (push_toggle) {
|
||||||
|
SSetsockopt(s, IPPROTO_TCP, TCP_NOPUSH, 0 );
|
||||||
|
SSetsockopt(s, IPPROTO_TCP, TCP_NOPUSH, 1 );
|
||||||
|
}
|
||||||
|
push_toggle = !push_toggle;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
if(dst.empty() || !compressed)
|
||||||
// and somehow we do work on that here. This is very stupid.
|
msg="bert";
|
||||||
if(!listener) {
|
|
||||||
if(dst.empty() || !compressed)
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
32
nmmsender.hh
32
nmmsender.hh
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
123
reporter.cc
123
reporter.cc
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
3
rinex.cc
3
rinex.cc
|
|
@ -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;
|
||||||
|
|
|
||||||
1
rinex.hh
1
rinex.hh
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
153
rs.cc
|
|
@ -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
44
rs.hh
|
|
@ -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
52
rtcm.cc
|
|
@ -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.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
rtcm.hh
2
rtcm.hh
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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
20
sbas.cc
|
|
@ -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
18
sbas.hh
|
|
@ -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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
747
septool.cc
747
septool.cc
|
|
@ -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
|
|
||||||
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.
|
|
||||||
Encoding: NAVBits contains all the bits of the frame, with the ex-
|
|
||||||
ception of the synchronization field. The first 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
146
ubx.cc
|
|
@ -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
29
ubx.hh
|
|
@ -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);
|
||||||
|
|
|
||||||
420
ubxtool.cc
420
ubxtool.cc
|
|
@ -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,32 +670,26 @@ 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 (doDEBUG) { cerr<<humanTimeNow()<<" Sending GNSS query"<<endl; }
|
||||||
|
msg = buildUbxMessage(0x06, 0x3e, {});
|
||||||
|
|
||||||
|
um1=sendAndWaitForUBX(fd, 1, msg, 0x06, 0x3e); // query GNSS
|
||||||
if(!version10) {
|
auto payload = um1.getPayload();
|
||||||
if (doDEBUG) { cerr<<humanTimeNow()<<" Sending GNSS query"<<endl; }
|
if (doDEBUG) {
|
||||||
|
cerr<<humanTimeNow()<<" GNSS status, got " << (int)payload[3]<<" rows:"<<endl;
|
||||||
msg = buildUbxMessage(0x06, 0x3e, {});
|
for(uint8_t n = 0 ; n < payload[3]; ++n) {
|
||||||
|
cerr<<humanTimeNow()<<" GNSSID "<<(int)payload[4+8*n]<<" enabled "<<(int)payload[8+8*n]<<" minTrk "<< (int)payload[5+8*n] <<" maxTrk "<<(int)payload[6+8*n]<<" " << (int)payload[8+8*n]<<" " << (int)payload[9+8*n] << " " <<" " << (int)payload[10+8*n]<<" " << (int)payload[11+8*n]<<endl;
|
||||||
um1=sendAndWaitForUBX(fd, 1, msg, 0x06, 0x3e); // query GNSS
|
}
|
||||||
auto payload = um1.getPayload();
|
|
||||||
if (doDEBUG) {
|
|
||||||
cerr<<humanTimeNow()<<" GNSS status, got " << (int)payload[3]<<" rows:"<<endl;
|
|
||||||
for(uint8_t n = 0 ; n < payload[3]; ++n) {
|
|
||||||
cerr<<humanTimeNow()<<" GNSSID "<<(int)payload[4+8*n]<<" enabled "<<(int)payload[8+8*n]<<" minTrk "<< (int)payload[5+8*n] <<" maxTrk "<<(int)payload[6+8*n]<<" " << (int)payload[8+8*n]<<" " << (int)payload[9+8*n] << " " <<" " << (int)payload[10+8*n]<<" " << (int)payload[11+8*n]<<endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if(waitForUBXAckNack(fd, 2, 0x06, 0x3e)) {
|
|
||||||
if (doDEBUG) { cerr<<humanTimeNow()<<" Got ACK for our poll of GNSS settings"<<endl; }
|
|
||||||
}
|
|
||||||
}catch(...) {
|
|
||||||
cerr<<"Got timeout waiting for ack of poll, no problem"<<endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!version9 && !version10) {
|
try {
|
||||||
|
if(waitForUBXAckNack(fd, 2, 0x06, 0x3e)) {
|
||||||
|
if (doDEBUG) { cerr<<humanTimeNow()<<" Got ACK for our poll of GNSS settings"<<endl; }
|
||||||
|
}
|
||||||
|
}catch(...) {
|
||||||
|
cerr<<"Got timeout waiting for ack of poll, no problem"<<endl;
|
||||||
|
}
|
||||||
|
if(!version9) {
|
||||||
// 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,65 +1466,52 @@ 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 {
|
auto gstr = getGlonassFromSFRBXMsg(payload);
|
||||||
if(version10 && sigid==8)
|
auto cond = getCondensedBeidouMessage(gstr);
|
||||||
continue;
|
static map<int, BeidouMessage> bms;
|
||||||
|
auto& bm = bms[id.second];
|
||||||
|
|
||||||
auto gstr = getGlonassFromSFRBXMsg(payload);
|
uint8_t pageno;
|
||||||
auto cond = getCondensedBeidouMessage(gstr);
|
bm.parse(cond, &pageno);
|
||||||
static map<int, BeidouMessage> bms;
|
|
||||||
auto& bm = bms[id.second];
|
|
||||||
|
|
||||||
uint8_t pageno;
|
if(bm.wn < 0) {
|
||||||
bm.parse(cond, &pageno);
|
if (doDEBUG) { cerr<<humanTimeNow()<<" BeiDou C"<<id.second<<" WN not yet known, not yet emitting message"<<endl; }
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
NavMonMessage nmm;
|
||||||
|
nmm.set_localutcseconds(g_gnssutc.tv_sec);
|
||||||
|
nmm.set_localutcnanoseconds(g_gnssutc.tv_nsec);
|
||||||
|
nmm.set_sourceid(g_srcid);
|
||||||
|
if(id.second > 5) {
|
||||||
|
// this **HARDCODES** that C01,02,03,04,05 emit D2 messages!
|
||||||
|
nmm.set_type(NavMonMessage::BeidouInavTypeD1);
|
||||||
|
nmm.mutable_bid1()->set_gnsswn(bm.wn); // only sent in word 1!!
|
||||||
|
nmm.mutable_bid1()->set_gnsstow(bm.sow);
|
||||||
|
nmm.mutable_bid1()->set_gnssid(id.first);
|
||||||
|
nmm.mutable_bid1()->set_gnsssv(id.second);
|
||||||
|
nmm.mutable_bid1()->set_sigid(sigid);
|
||||||
|
nmm.mutable_bid1()->set_contents(string((char*)gstr.c_str(), gstr.size()));
|
||||||
|
ns.emitNMM( nmm);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// not sending this: we can't even get the week number right!
|
||||||
|
/*
|
||||||
|
nmm.set_type(NavMonMessage::BeidouInavTypeD2);
|
||||||
|
nmm.mutable_bid2()->set_gnsswn(bm.wn);
|
||||||
|
nmm.mutable_bid2()->set_gnsstow(bm.sow);
|
||||||
|
nmm.mutable_bid2()->set_gnssid(id.first);
|
||||||
|
nmm.mutable_bid2()->set_gnsssv(id.second);
|
||||||
|
nmm.mutable_bid2()->set_sigid(sigid);
|
||||||
|
nmm.mutable_bid2()->set_contents(string((char*)gstr.c_str(), gstr.size()));
|
||||||
|
*/
|
||||||
|
|
||||||
if(bm.wn < 0) {
|
}
|
||||||
if (doDEBUG) { cerr<<humanTimeNow()<<" BeiDou C"<<id.second<<" WN not yet known, not yet emitting message"<<endl; }
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
NavMonMessage nmm;
|
|
||||||
nmm.set_localutcseconds(g_gnssutc.tv_sec);
|
|
||||||
nmm.set_localutcnanoseconds(g_gnssutc.tv_nsec);
|
|
||||||
nmm.set_sourceid(g_srcid);
|
|
||||||
if(id.second > 5) {
|
|
||||||
// this **HARDCODES** that C01,02,03,04,05 emit D2 messages!
|
|
||||||
nmm.set_type(NavMonMessage::BeidouInavTypeD1);
|
|
||||||
nmm.mutable_bid1()->set_gnsswn(bm.wn); // only sent in word 1!!
|
|
||||||
nmm.mutable_bid1()->set_gnsstow(bm.sow);
|
|
||||||
nmm.mutable_bid1()->set_gnssid(id.first);
|
|
||||||
nmm.mutable_bid1()->set_gnsssv(id.second);
|
|
||||||
nmm.mutable_bid1()->set_sigid(sigid);
|
|
||||||
nmm.mutable_bid1()->set_contents(string((char*)&gstr[0], gstr.size()));
|
|
||||||
ns.emitNMM( nmm);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// not sending this: we can't even get the week number right!
|
|
||||||
/*
|
|
||||||
nmm.set_type(NavMonMessage::BeidouInavTypeD2);
|
|
||||||
nmm.mutable_bid2()->set_gnsswn(bm.wn);
|
|
||||||
nmm.mutable_bid2()->set_gnsstow(bm.sow);
|
|
||||||
nmm.mutable_bid2()->set_gnssid(id.first);
|
|
||||||
nmm.mutable_bid2()->set_gnsssv(id.second);
|
|
||||||
nmm.mutable_bid2()->set_sigid(sigid);
|
|
||||||
nmm.mutable_bid2()->set_contents(string((char*)gstr.c_str(), gstr.size()));
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
continue;
|
||||||
}
|
|
||||||
catch(std::exception& e) {
|
|
||||||
cerr<<"Failure to parse BeiDou message (sigid="<<sigid<<"): "<<e.what() << endl;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
else if(id.first==6) {
|
else if(id.first==6) {
|
||||||
// if (doDEBUG) { cerr<<humanTimeNow()<<" SFRBX from GLONASS "<<id.second<<" @ frequency "<<(int)payload[3]<<", msg of "<<(int)payload[4]<< " words"<<endl; }
|
// if (doDEBUG) { cerr<<humanTimeNow()<<" SFRBX from GLONASS "<<id.second<<" @ frequency "<<(int)payload[3]<<", msg of "<<(int)payload[4]<< " words"<<endl; }
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
18
zstdwrap.cc
18
zstdwrap.cc
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue