the whole thingus

pull/1/head
bert hubert 2019-08-26 15:58:01 +02:00
parent 8ed9401cab
commit 0962be6b8b
18 changed files with 3177 additions and 233 deletions

View File

@ -1,4 +1,4 @@
CXXFLAGS:= -std=gnu++17 -Wall -O3 -MMD -MP -ggdb -fno-omit-frame-pointer -Iext/fmt-5.2.1/include/ -Iext/powerblog/ext/simplesocket -Iext/powerblog/ext/
CXXFLAGS:= -std=gnu++17 -Wall -O3 -MMD -MP -ggdb -fno-omit-frame-pointer -Iext/CLI11 -Iext/fmt-5.2.1/include/ -Iext/powerblog/ext/simplesocket -Iext/powerblog/ext/ -I/usr/local/opt/openssl/include/ -Wno-delete-non-virtual-dtor
PROGRAMS = navparse ubxtool navnexus navrecv navdump
@ -12,10 +12,10 @@ clean:
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
navparse: navparse.o ext/fmt-5.2.1/src/format.o $(H2OPP) $(SIMPLESOCKETS) minicurl.o ubx.o bits.o navmon.pb.o gps.o ephemeris.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lh2o-evloop -lssl -lcrypto -lz -lcurl -lprotobuf # -lwslay
navparse: navparse.o ext/fmt-5.2.1/src/format.o $(H2OPP) $(SIMPLESOCKETS) minicurl.o ubx.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -L/usr/local/opt/openssl/lib/ -lh2o-evloop -lssl -lcrypto -lz -lcurl -lprotobuf # -lwslay
navdump: navdump.o ext/fmt-5.2.1/src/format.o bits.o navmon.pb.o gps.o ephemeris.o
navdump: navdump.o ext/fmt-5.2.1/src/format.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -lprotobuf
@ -29,6 +29,6 @@ navrecv: navrecv.o ext/fmt-5.2.1/src/format.o $(SIMPLESOCKETS) navmon.pb.o stora
navmon.pb.cc: navmon.proto
protoc --cpp_out=./ navmon.proto
ubxtool: navmon.pb.o ubxtool.o ubx.o bits.o ext/fmt-5.2.1/src/format.o galileo.o gps.o
ubxtool: navmon.pb.o ubxtool.o ubx.o bits.o ext/fmt-5.2.1/src/format.o galileo.o gps.o beidou.o
$(CXX) -std=gnu++17 $^ -o $@ -lprotobuf

View File

@ -22,7 +22,7 @@ struct BeidouMessage
};
int fraid, sow; // part of every message (thanks!)
int fraid{-1}, sow{-1}; // part of every message (thanks!)
int parse(std::basic_string_view<uint8_t> cond, uint8_t* pageno)
{
@ -99,6 +99,7 @@ struct BeidouMessage
e = bbitu(133,32);
cus = bbits(181, 18);
crc = bbits(199, 18);
crs = bbits(225, 18);
sqrtA = bbitu(251, 32);
t0eMSB = bbitu(291, 2);
@ -126,7 +127,7 @@ struct BeidouMessage
i0 = bbits(66, 32);
cic = bbits(106, 18);
omegadot = bbits(132, 24);
cis = bbits(160, 18);
cis = bbits(164, 18);
idot = bbits(190, 14);
Omega0 = bbits(212, 32);
omega = bbits(252, 32);

View File

@ -10,30 +10,30 @@ void getCoordinates(int wn, double tow, const T& iod, Point* p, bool quiet=true)
using namespace std;
// here goes
const double mu = 3.986004418 * pow(10.0, 14.0);
const double omegaE = 7.2921151467 * pow(10.0, -5);
const double mu = iod.getMu();
const double omegaE = iod.getOmegaE();
double sqrtA = 1.0*iod.sqrtA / (1ULL<<19);
double deltan = M_PI * 1.0*iod.deltan / (1LL<<43);
double t0e = iod.t0e; // t0e is PRE-SCALED
double m0 = M_PI * 1.0*iod.m0 / (1LL<<31);
double e = 1.0*iod.e / (1ULL<<33);
double omega = M_PI * 1.0*iod.omega / (1LL<<31);
const double sqrtA = iod.getSqrtA();
const double deltan = iod.getDeltan();
const double t0e = iod.getT0e();
const double m0 = iod.getM0();
const double e = iod.getE();
const double omega = iod.getOmega();
double cuc = 1.0*iod.cuc / (1LL<<29);
double cus = 1.0*iod.cus / (1LL<<29);
const double cuc = iod.getCuc();
const double cus = iod.getCus();
double crc = 1.0*iod.crc / (1LL<<5);
double crs = 1.0*iod.crs / (1LL<<5);
const double crc = iod.getCrc();
const double crs = iod.getCrs();
double cic = 1.0*iod.cic / (1LL<<29);
double cis = 1.0*iod.cis / (1LL<<29);
const double cic = iod.getCic();
const double cis = iod.getCis();
double idot = M_PI * 1.0*iod.idot / (1LL<<43);
double i0 = M_PI * 1.0*iod.i0 / (1LL << 31);
const double idot = iod.getIdot();
const double i0 = iod.getI0();
double Omegadot = M_PI * 1.0*iod.omegadot / (1LL << 43);
double Omega0 = M_PI * 1.0*iod.omega0 / (1LL << 31);
const double Omegadot = iod.getOmegadot();
const double Omega0 = iod.getOmega0();
// NO IOD BEYOND THIS POINT!
if(!quiet) {

1276
ext/CLI11/CLI/App.hpp 100644

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
// CLI Library includes
// Order is important for combiner script
#include "CLI/Error.hpp"
#include "CLI/TypeTools.hpp"
#include "CLI/StringTools.hpp"
#include "CLI/Split.hpp"
#include "CLI/Ini.hpp"
#include "CLI/Validators.hpp"
#include "CLI/Option.hpp"
#include "CLI/App.hpp"

View File

@ -0,0 +1,165 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <exception>
#include <stdexcept>
#include <string>
namespace CLI {
/// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut,
/// int values from e.get_error_code().
enum class ExitCodes {
Success = 0,
IncorrectConstruction = 100,
BadNameString,
OptionAlreadyAdded,
File,
Conversion,
Validation,
Required,
Requires,
Excludes,
Extras,
ExtrasINI,
Invalid,
Horrible,
OptionNotFound,
BaseClass = 255
};
// Error definitions
/// @defgroup error_group Errors
/// @brief Errors thrown by CLI11
///
/// These are the errors that can be thrown. Some of them, like CLI::Success, are not really errors.
/// @{
/// All errors derive from this one
struct Error : public std::runtime_error {
int exit_code;
bool print_help;
int get_exit_code() const { return exit_code; }
Error(std::string parent, std::string name, ExitCodes exit_code = ExitCodes::BaseClass, bool print_help = true)
: runtime_error(parent + ": " + name), exit_code(static_cast<int>(exit_code)), print_help(print_help) {}
Error(std::string parent,
std::string name,
int exit_code = static_cast<int>(ExitCodes::BaseClass),
bool print_help = true)
: runtime_error(parent + ": " + name), exit_code(exit_code), print_help(print_help) {}
};
/// Construction errors (not in parsing)
struct ConstructionError : public Error {
// Using Error::Error constructors seem to not work on GCC 4.7
ConstructionError(std::string parent,
std::string name,
ExitCodes exit_code = ExitCodes::BaseClass,
bool print_help = true)
: Error(parent, name, exit_code, print_help) {}
};
/// Thrown when an option is set to conflicting values (non-vector and multi args, for example)
struct IncorrectConstruction : public ConstructionError {
IncorrectConstruction(std::string name)
: ConstructionError("IncorrectConstruction", name, ExitCodes::IncorrectConstruction) {}
};
/// Thrown on construction of a bad name
struct BadNameString : public ConstructionError {
BadNameString(std::string name) : ConstructionError("BadNameString", name, ExitCodes::BadNameString) {}
};
/// Thrown when an option already exists
struct OptionAlreadyAdded : public ConstructionError {
OptionAlreadyAdded(std::string name)
: ConstructionError("OptionAlreadyAdded", name, ExitCodes::OptionAlreadyAdded) {}
};
// Parsing errors
/// Anything that can error in Parse
struct ParseError : public Error {
ParseError(std::string parent, std::string name, ExitCodes exit_code = ExitCodes::BaseClass, bool print_help = true)
: Error(parent, name, exit_code, print_help) {}
};
// Not really "errors"
/// This is a successful completion on parsing, supposed to exit
struct Success : public ParseError {
Success() : ParseError("Success", "Successfully completed, should be caught and quit", ExitCodes::Success, false) {}
};
/// -h or --help on command line
struct CallForHelp : public ParseError {
CallForHelp()
: ParseError("CallForHelp", "This should be caught in your main function, see examples", ExitCodes::Success) {}
};
/// Thrown when parsing an INI file and it is missing
struct FileError : public ParseError {
FileError(std::string name) : ParseError("FileError", name, ExitCodes::File) {}
};
/// Thrown when conversion call back fails, such as when an int fails to coerse to a string
struct ConversionError : public ParseError {
ConversionError(std::string name) : ParseError("ConversionError", name, ExitCodes::Conversion) {}
};
/// Thrown when validation of results fails
struct ValidationError : public ParseError {
ValidationError(std::string name) : ParseError("ValidationError", name, ExitCodes::Validation) {}
};
/// Thrown when a required option is missing
struct RequiredError : public ParseError {
RequiredError(std::string name) : ParseError("RequiredError", name, ExitCodes::Required) {}
};
/// Thrown when a requires option is missing
struct RequiresError : public ParseError {
RequiresError(std::string name, std::string subname)
: ParseError("RequiresError", name + " requires " + subname, ExitCodes::Requires) {}
};
/// Thrown when a exludes option is present
struct ExcludesError : public ParseError {
ExcludesError(std::string name, std::string subname)
: ParseError("ExcludesError", name + " excludes " + subname, ExitCodes::Excludes) {}
};
/// Thrown when too many positionals or options are found
struct ExtrasError : public ParseError {
ExtrasError(std::string name) : ParseError("ExtrasError", name, ExitCodes::Extras) {}
};
/// Thrown when extra values are found in an INI file
struct ExtrasINIError : public ParseError {
ExtrasINIError(std::string name) : ParseError("ExtrasINIError", name, ExitCodes::ExtrasINI) {}
};
/// Thrown when validation fails before parsing
struct InvalidError : public ParseError {
InvalidError(std::string name) : ParseError("InvalidError", name, ExitCodes::Invalid) {}
};
/// This is just a safety check to verify selection and parsing match
struct HorribleError : public ParseError {
HorribleError(std::string name)
: ParseError("HorribleError", "(You should never see this error) " + name, ExitCodes::Horrible) {}
};
// After parsing
/// Thrown when counting a non-existent option
struct OptionNotFound : public Error {
OptionNotFound(std::string name) : Error("OptionNotFound", name, ExitCodes::OptionNotFound) {}
};
/// @}
} // namespace CLI

View File

@ -0,0 +1,115 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include "CLI/StringTools.hpp"
namespace CLI {
namespace detail {
inline std::string inijoin(std::vector<std::string> args) {
std::ostringstream s;
size_t start = 0;
for(const auto &arg : args) {
if(start++ > 0)
s << " ";
auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace<char>(ch, std::locale()); });
if(it == arg.end())
s << arg;
else if(arg.find(R"(")") == std::string::npos)
s << R"(")" << arg << R"(")";
else
s << R"(')" << arg << R"(')";
}
return s.str();
}
struct ini_ret_t {
/// This is the full name with dots
std::string fullname;
/// Listing of inputs
std::vector<std::string> inputs;
/// Current parent level
size_t level = 0;
/// Return parent or empty string, based on level
///
/// Level 0, a.b.c would return a
/// Level 1, a.b.c could return b
std::string parent() const {
std::vector<std::string> plist = detail::split(fullname, '.');
if(plist.size() > (level + 1))
return plist[level];
else
return "";
}
/// Return name
std::string name() const {
std::vector<std::string> plist = detail::split(fullname, '.');
return plist.at(plist.size() - 1);
}
};
/// Internal parsing function
inline std::vector<ini_ret_t> parse_ini(std::istream &input) {
std::string name, line;
std::string section = "default";
std::vector<ini_ret_t> output;
while(getline(input, line)) {
std::vector<std::string> items;
detail::trim(line);
size_t len = line.length();
if(len > 1 && line[0] == '[' && line[len - 1] == ']') {
section = line.substr(1, len - 2);
} else if(len > 0 && line[0] != ';') {
output.emplace_back();
ini_ret_t &out = output.back();
// Find = in string, split and recombine
auto pos = line.find("=");
if(pos != std::string::npos) {
name = detail::trim_copy(line.substr(0, pos));
std::string item = detail::trim_copy(line.substr(pos + 1));
items = detail::split_up(item);
} else {
name = detail::trim_copy(line);
items = {"ON"};
}
if(detail::to_lower(section) == "default")
out.fullname = name;
else
out.fullname = section + "." + name;
out.inputs.insert(std::end(out.inputs), std::begin(items), std::end(items));
}
}
return output;
}
/// Parse an INI file, throw an error (ParseError:INIParseError or FileError) on failure
inline std::vector<ini_ret_t> parse_ini(const std::string &name) {
std::ifstream input{name};
if(!input.good())
throw FileError(name);
return parse_ini(input);
}
} // namespace detail
} // namespace CLI

View File

@ -0,0 +1,460 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <algorithm>
#include <functional>
#include <memory>
#include <set>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "CLI/Error.hpp"
#include "CLI/Split.hpp"
#include "CLI/StringTools.hpp"
namespace CLI {
using results_t = std::vector<std::string>;
using callback_t = std::function<bool(results_t)>;
class Option;
class App;
using Option_p = std::unique_ptr<Option>;
class Option {
friend App;
protected:
/// @name Names
///@{
/// A list of the short names (`-a`) without the leading dashes
std::vector<std::string> snames_;
/// A list of the long names (`--a`) without the leading dashes
std::vector<std::string> lnames_;
/// A positional name
std::string pname_;
/// If given, check the environment for this option
std::string envname_;
///@}
/// @name Help
///@{
/// The description for help strings
std::string description_;
/// A human readable default value, usually only set if default is true in creation
std::string defaultval_;
/// A human readable type value, set when App creates this
std::string typeval_;
/// The group membership
std::string group_{"Options"};
/// True if this option has a default
bool default_{false};
///@}
/// @name Configuration
///@{
/// True if this is a required option
bool required_{false};
/// The number of expected values, 0 for flag, -1 for unlimited vector
int expected_{1};
/// A private setting to allow args to not be able to accept incorrect expected values
bool changeable_{false};
/// Ignore the case when matching (option, not value)
bool ignore_case_{false};
/// A list of validators to run on each value parsed
std::vector<std::function<bool(std::string)>> validators_;
/// A list of options that are required with this option
std::set<Option *> requires_;
/// A list of options that are excluded with this option
std::set<Option *> excludes_;
///@}
/// @name Other
///@{
/// Remember the parent app
App *parent_;
/// Options store a callback to do all the work
callback_t callback_;
///@}
/// @name Parsing results
///@{
/// Results of parsing
results_t results_;
/// Whether the callback has run (needed for INI parsing)
bool callback_run_{false};
///@}
/// Making an option by hand is not defined, it must be made by the App class
Option(std::string name,
std::string description = "",
std::function<bool(results_t)> callback = [](results_t) { return true; },
bool default_ = true,
App *parent = nullptr)
: description_(std::move(description)), default_(default_), parent_(parent), callback_(std::move(callback)) {
std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(name));
}
public:
/// @name Basic
///@{
/// Count the total number of times an option was passed
size_t count() const { return results_.size(); }
/// This class is true if option is passed.
operator bool() const { return count() > 0; }
/// Clear the parsed results (mostly for testing)
void clear() { results_.clear(); }
///@}
/// @name Setting options
///@{
/// Set the option as required
Option *required(bool value = true) {
required_ = value;
return this;
}
/// Support Plubmum term
Option *mandatory(bool value = true) { return required(value); }
/// Set the number of expected arguments (Flags bypass this)
Option *expected(int value) {
if(value == 0)
throw IncorrectConstruction("Cannot set 0 expected, use a flag instead");
else if(expected_ == 0)
throw IncorrectConstruction("Cannot make a flag take arguments!");
else if(!changeable_)
throw IncorrectConstruction("You can only change the expected arguments for vectors");
expected_ = value;
return this;
}
/// Adds a validator
Option *check(std::function<bool(std::string)> validator) {
validators_.push_back(validator);
return this;
}
/// Changes the group membership
Option *group(std::string name) {
group_ = name;
return this;
}
/// Sets required options
Option *requires(Option *opt) {
auto tup = requires_.insert(opt);
if(!tup.second)
throw OptionAlreadyAdded(get_name() + " requires " + opt->get_name());
return this;
}
/// Can find a string if needed
template <typename T = App> Option *requires(std::string opt_name) {
for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
if(opt.get() != this && opt->check_name(opt_name))
return requires(opt.get());
throw IncorrectConstruction("Option " + opt_name + " is not defined");
}
/// Any number supported, any mix of string and Opt
template <typename A, typename B, typename... ARG> Option *requires(A opt, B opt1, ARG... args) {
requires(opt);
return requires(opt1, args...);
}
/// Sets excluded options
Option *excludes(Option *opt) {
auto tup = excludes_.insert(opt);
if(!tup.second)
throw OptionAlreadyAdded(get_name() + " excludes " + opt->get_name());
return this;
}
/// Can find a string if needed
template <typename T = App> Option *excludes(std::string opt_name) {
for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
if(opt.get() != this && opt->check_name(opt_name))
return excludes(opt.get());
throw IncorrectConstruction("Option " + opt_name + " is not defined");
}
/// Any number supported, any mix of string and Opt
template <typename A, typename B, typename... ARG> Option *excludes(A opt, B opt1, ARG... args) {
excludes(opt);
return excludes(opt1, args...);
}
/// Sets environment variable to read if no option given
Option *envname(std::string name) {
envname_ = name;
return this;
}
/// Ignore case
///
/// The template hides the fact that we don't have the definition of App yet.
/// You are never expected to add an argument to the template here.
template <typename T = App> Option *ignore_case(bool value = true) {
ignore_case_ = value;
for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
if(opt.get() != this && *opt == *this)
throw OptionAlreadyAdded(opt->get_name());
return this;
}
///@}
/// @name Accessors
///@{
/// True if this is a required option
bool get_required() const { return required_; }
/// The number of arguments the option expects
int get_expected() const { return expected_; }
/// True if this has a default value
int get_default() const { return default_; }
/// True if the argument can be given directly
bool get_positional() const { return pname_.length() > 0; }
/// True if option has at least one non-positional name
bool nonpositional() const { return (snames_.size() + lnames_.size()) > 0; }
/// True if option has description
bool has_description() const { return description_.length() > 0; }
/// Get the group of this option
const std::string &get_group() const { return group_; }
/// Get the description
const std::string &get_description() const { return description_; }
// Just the pname
std::string get_pname() const { return pname_; }
///@}
/// @name Help tools
///@{
/// Gets a , sep list of names. Does not include the positional name if opt_only=true.
std::string get_name(bool opt_only = false) const {
std::vector<std::string> name_list;
if(!opt_only && pname_.length() > 0)
name_list.push_back(pname_);
for(const std::string &sname : snames_)
name_list.push_back("-" + sname);
for(const std::string &lname : lnames_)
name_list.push_back("--" + lname);
return detail::join(name_list);
}
/// The name and any extras needed for positionals
std::string help_positional() const {
std::string out = pname_;
if(get_expected() > 1)
out = out + "(" + std::to_string(get_expected()) + "x)";
else if(get_expected() == -1)
out = out + "...";
out = get_required() ? out : "[" + out + "]";
return out;
}
/// The first half of the help print, name plus default, etc
std::string help_name() const {
std::stringstream out;
out << get_name(true) << help_aftername();
return out.str();
}
/// pname with type info
std::string help_pname() const {
std::stringstream out;
out << get_pname() << help_aftername();
return out.str();
}
/// This is the part after the name is printed but before the description
std::string help_aftername() const {
std::stringstream out;
if(get_expected() != 0) {
if(typeval_ != "")
out << " " << typeval_;
if(defaultval_ != "")
out << "=" << defaultval_;
if(get_expected() > 1)
out << " x " << get_expected();
if(get_expected() == -1)
out << " ...";
}
if(envname_ != "")
out << " (env:" << envname_ << ")";
if(!requires_.empty()) {
out << " Requires:";
for(const Option *opt : requires_)
out << " " << opt->get_name();
}
if(!excludes_.empty()) {
out << " Excludes:";
for(const Option *opt : excludes_)
out << " " << opt->get_name();
}
return out.str();
}
///@}
/// @name Parser tools
///@{
/// Process the callback
void run_callback() const {
if(!callback_(results_))
throw ConversionError(get_name() + "=" + detail::join(results_));
if(!validators_.empty()) {
for(const std::string &result : results_)
for(const std::function<bool(std::string)> &vali : validators_)
if(!vali(result))
throw ValidationError(get_name() + "=" + result);
}
}
/// If options share any of the same names, they are equal (not counting positional)
bool operator==(const Option &other) const {
for(const std::string &sname : snames_)
if(other.check_sname(sname))
return true;
for(const std::string &lname : lnames_)
if(other.check_lname(lname))
return true;
// We need to do the inverse, just in case we are ignore_case
for(const std::string &sname : other.snames_)
if(check_sname(sname))
return true;
for(const std::string &lname : other.lnames_)
if(check_lname(lname))
return true;
return false;
}
/// Check a name. Requires "-" or "--" for short / long, supports positional name
bool check_name(std::string name) const {
if(name.length() > 2 && name.substr(0, 2) == "--")
return check_lname(name.substr(2));
else if(name.length() > 1 && name.substr(0, 1) == "-")
return check_sname(name.substr(1));
else {
std::string local_pname = pname_;
if(ignore_case_) {
local_pname = detail::to_lower(local_pname);
name = detail::to_lower(name);
}
return name == local_pname;
}
}
/// Requires "-" to be removed from string
bool check_sname(std::string name) const {
if(ignore_case_) {
name = detail::to_lower(name);
return std::find_if(std::begin(snames_), std::end(snames_), [&name](std::string local_sname) {
return detail::to_lower(local_sname) == name;
}) != std::end(snames_);
} else
return std::find(std::begin(snames_), std::end(snames_), name) != std::end(snames_);
}
/// Requires "--" to be removed from string
bool check_lname(std::string name) const {
if(ignore_case_) {
name = detail::to_lower(name);
return std::find_if(std::begin(lnames_), std::end(lnames_), [&name](std::string local_sname) {
return detail::to_lower(local_sname) == name;
}) != std::end(lnames_);
} else
return std::find(std::begin(lnames_), std::end(lnames_), name) != std::end(lnames_);
}
/// Puts a result at position r
void add_result(std::string s) {
results_.push_back(s);
callback_run_ = false;
}
/// Get a copy of the results
std::vector<std::string> results() const { return results_; }
/// See if the callback has been run already
bool get_callback_run() const { return callback_run_; }
///@}
/// @name Custom options
///@{
/// Set a custom option, typestring, expected, and changeable
void set_custom_option(std::string typeval, int expected = 1, bool changeable = false) {
typeval_ = typeval;
expected_ = expected;
changeable_ = changeable;
}
/// Set the default value string representation
void set_default_str(std::string val) { defaultval_ = val; }
/// Set the default value string representation and evaluate
void set_default_val(std::string val) {
set_default_str(val);
auto old_results = results_;
results_ = {val};
run_callback();
results_ = std::move(old_results);
}
/// Set the type name displayed on this option
void set_type_name(std::string val) { typeval_ = val; }
///@}
protected:
/// @name App Helpers
///@{
/// Can print positional name detailed option if true
bool _has_help_positional() const {
return get_positional() && (has_description() || !requires_.empty() || !excludes_.empty());
}
///@}
};
} // namespace CLI

View File

@ -0,0 +1,90 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <string>
#include <tuple>
#include <vector>
#include "CLI/Error.hpp"
#include "CLI/StringTools.hpp"
namespace CLI {
namespace detail {
// Returns false if not a short option. Otherwise, sets opt name and rest and returns true
inline bool split_short(const std::string &current, std::string &name, std::string &rest) {
if(current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) {
name = current.substr(1, 1);
rest = current.substr(2);
return true;
} else
return false;
}
// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true
inline bool split_long(const std::string &current, std::string &name, std::string &value) {
if(current.size() > 2 && current.substr(0, 2) == "--" && valid_first_char(current[2])) {
auto loc = current.find("=");
if(loc != std::string::npos) {
name = current.substr(2, loc - 2);
value = current.substr(loc + 1);
} else {
name = current.substr(2);
value = "";
}
return true;
} else
return false;
}
// Splits a string into multiple long and short names
inline std::vector<std::string> split_names(std::string current) {
std::vector<std::string> output;
size_t val;
while((val = current.find(",")) != std::string::npos) {
output.push_back(current.substr(0, val));
current = current.substr(val + 1);
}
output.push_back(current);
return output;
}
/// Get a vector of short names, one of long names, and a single name
inline std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>
get_names(const std::vector<std::string> &input) {
std::vector<std::string> short_names;
std::vector<std::string> long_names;
std::string pos_name;
for(std::string name : input) {
if(name.length() == 0)
continue;
else if(name.length() > 1 && name[0] == '-' && name[1] != '-') {
if(name.length() == 2 && valid_first_char(name[1]))
short_names.emplace_back(1, name[1]);
else
throw BadNameString("Invalid one char name: " + name);
} else if(name.length() > 2 && name.substr(0, 2) == "--") {
name = name.substr(2);
if(valid_name_string(name))
long_names.push_back(name);
else
throw BadNameString("Bad long name: " + name);
} else if(name == "-" || name == "--") {
throw BadNameString("Must have a name, not just dashes");
} else {
if(pos_name.length() > 0)
throw BadNameString("Only one positional name allowed, remove: " + name);
pos_name = name;
}
}
return std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>(
short_names, long_names, pos_name);
}
} // namespace detail
} // namespace CLI

View File

@ -0,0 +1,190 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <algorithm>
#include <iomanip>
#include <locale>
#include <sstream>
#include <string>
#include <type_traits>
namespace CLI {
namespace detail {
// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c
/// Split a string by a delim
inline std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
// Check to see if emtpy string, give consistent result
if(s == "")
elems.emplace_back("");
else {
std::stringstream ss;
ss.str(s);
std::string item;
while(std::getline(ss, item, delim)) {
elems.push_back(item);
}
}
return elems;
}
/// Simple function to join a string
template <typename T> std::string join(const T &v, std::string delim = ",") {
std::ostringstream s;
size_t start = 0;
for(const auto &i : v) {
if(start++ > 0)
s << delim;
s << i;
}
return s.str();
}
/// Join a string in reverse order
template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
std::ostringstream s;
for(size_t start = 0; start < v.size(); start++) {
if(start > 0)
s << delim;
s << v[v.size() - start - 1];
}
return s.str();
}
// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string
/// Trim whitespace from left of string
inline std::string &ltrim(std::string &str) {
auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
str.erase(str.begin(), it);
return str;
}
/// Trim anything from left of string
inline std::string &ltrim(std::string &str, const std::string &filter) {
auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
str.erase(str.begin(), it);
return str;
}
/// Trim whitespace from right of string
inline std::string &rtrim(std::string &str) {
auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
str.erase(it.base(), str.end());
return str;
}
/// Trim anything from right of string
inline std::string &rtrim(std::string &str, const std::string &filter) {
auto it =
std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
str.erase(it.base(), str.end());
return str;
}
/// Trim whitespace from string
inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); }
/// Trim anything from string
inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); }
/// Make a copy of the string and then trim it
inline std::string trim_copy(const std::string &str) {
std::string s = str;
return trim(s);
}
/// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered)
inline std::string trim_copy(const std::string &str, const std::string &filter) {
std::string s = str;
return trim(s, filter);
}
/// Print a two part "help" string
inline void format_help(std::stringstream &out, std::string name, std::string description, size_t wid) {
name = " " + name;
out << std::setw(static_cast<int>(wid)) << std::left << name;
if(description != "") {
if(name.length() >= wid)
out << std::endl << std::setw(static_cast<int>(wid)) << "";
out << description;
}
out << std::endl;
}
/// Verify the first character of an option
template <typename T> bool valid_first_char(T c) { return std::isalpha(c, std::locale()) || c == '_'; }
/// Verify following characters of an option
template <typename T> bool valid_later_char(T c) {
return std::isalnum(c, std::locale()) || c == '_' || c == '.' || c == '-';
}
/// Verify an option name
inline bool valid_name_string(const std::string &str) {
if(str.empty() || !valid_first_char(str[0]))
return false;
for(auto c : str.substr(1))
if(!valid_later_char(c))
return false;
return true;
}
/// Return a lower case version of a string
inline std::string to_lower(std::string str) {
std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) {
return std::tolower(x, std::locale());
});
return str;
}
/// Split a string '"one two" "three"' into 'one two', 'three'
inline std::vector<std::string> split_up(std::string str) {
std::vector<char> delims = {'\'', '\"'};
auto find_ws = [](char ch) { return std::isspace<char>(ch, std::locale()); };
trim(str);
std::vector<std::string> output;
while(!str.empty()) {
if(str[0] == '\'') {
auto end = str.find('\'', 1);
if(end != std::string::npos) {
output.push_back(str.substr(1, end - 1));
str = str.substr(end + 1);
} else {
output.push_back(str.substr(1));
str = "";
}
} else if(str[0] == '\"') {
auto end = str.find('\"', 1);
if(end != std::string::npos) {
output.push_back(str.substr(1, end - 1));
str = str.substr(end + 1);
} else {
output.push_back(str.substr(1));
str = "";
}
} else {
auto it = std::find_if(std::begin(str), std::end(str), find_ws);
if(it != std::end(str)) {
std::string value = std::string(str.begin(), it);
output.push_back(value);
str = std::string(it, str.end());
} else {
output.push_back(str);
str = "";
}
}
trim(str);
}
return output;
}
} // namespace detail
} // namespace CLI

View File

@ -0,0 +1,121 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <chrono>
#include <functional>
#include <iostream>
#include <string>
#include <utility>
namespace CLI {
class Timer {
protected:
/// This is a typedef to make clocks easier to use
using clock = std::chrono::steady_clock;
/// This typedef is for points in time
using time_point = std::chrono::time_point<clock>;
/// This is the type of a printing function, you can make your own
using time_print_t = std::function<std::string(std::string, std::string)>;
/// This is the title of the timer
std::string title_;
/// This is the function that is used to format most of the timing message
time_print_t time_print_;
/// This is the starting point (when the timer was created)
time_point start_;
/// This is the number of times cycles (print divides by this number)
size_t cycles{1};
public:
/// Standard print function, this one is set by default
static std::string Simple(std::string title, std::string time) { return title + ": " + time; }
/// This is a fancy print function with --- headers
static std::string Big(std::string title, std::string time) {
return std::string("-----------------------------------------\n") + "| " + title + " | Time = " + time + "\n" +
"-----------------------------------------";
}
public:
/// Standard constructor, can set title and print function
Timer(std::string title = "Timer", time_print_t time_print = Simple)
: title_(std::move(title)), time_print_(std::move(time_print)), start_(clock::now()) {}
/// Time a function by running it multiple times. Target time is the len to target.
std::string time_it(std::function<void()> f, double target_time = 1) {
time_point start = start_;
double total_time;
start_ = clock::now();
size_t n = 0;
do {
f();
std::chrono::duration<double> elapsed = clock::now() - start_;
total_time = elapsed.count();
} while(n++ < 100 && total_time < target_time);
std::string out = make_time_str(total_time / n) + " for " + std::to_string(n) + " tries";
start_ = start;
return out;
}
/// This formats the numerical value for the time string
std::string make_time_str() const {
time_point stop = clock::now();
std::chrono::duration<double> elapsed = stop - start_;
double time = elapsed.count() / cycles;
return make_time_str(time);
}
// LCOV_EXCL_START
std::string make_time_str(double time) const {
auto print_it = [](double x, std::string unit) {
char buffer[50];
std::snprintf(buffer, 50, "%.5g", x);
return buffer + std::string(" ") + unit;
};
if(time < .000001)
return print_it(time * 1000000000, "ns");
else if(time < .001)
return print_it(time * 1000000, "us");
else if(time < 1)
return print_it(time * 1000, "ms");
else
return print_it(time, "s");
}
// LCOV_EXCL_END
/// This is the main function, it creates a string
std::string to_string() const { return time_print_(title_, make_time_str()); }
/// Division sets the number of cycles to divide by (no graphical change)
Timer &operator/(size_t val) {
cycles = val;
return *this;
}
};
/// This class prints out the time upon destruction
class AutoTimer : public Timer {
public:
/// Reimplementing the constructor is required in GCC 4.7
AutoTimer(std::string title = "Timer", time_print_t time_print = Simple) : Timer(title, time_print) {}
// GCC 4.7 does not support using inheriting constructors.
/// This desctructor prints the string
~AutoTimer() { std::cout << to_string() << std::endl; }
};
} // namespace CLI
/// This prints out the time if shifted into a std::cout like stream.
inline std::ostream &operator<<(std::ostream &in, const CLI::Timer &timer) { return in << timer.to_string(); }

View File

@ -0,0 +1,114 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include <exception>
#include <string>
#include <type_traits>
#include <vector>
namespace CLI {
// Type tools
// Copied from C++14
#if __cplusplus < 201402L
template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
#else
// If your compiler supports C++14, you can use that definition instead
using std::enable_if_t;
#endif
template <typename T> struct is_vector { static const bool value = false; };
template <class T, class A> struct is_vector<std::vector<T, A>> { static bool const value = true; };
template <typename T> struct is_bool { static const bool value = false; };
template <> struct is_bool<bool> { static bool const value = true; };
namespace detail {
// Based generally on https://rmf.io/cxx11/almost-static-if
/// Simple empty scoped class
enum class enabler {};
/// An instance to use in EnableIf
constexpr enabler dummy = {};
// Type name print
/// Was going to be based on
/// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template
/// But this is cleaner and works better in this case
template <typename T,
enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
return "INT";
}
template <typename T,
enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
return "UINT";
}
template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
return "FLOAT";
}
/// This one should not be used, since vector types print the internal type
template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
constexpr const char *type_name() {
return "VECTOR";
}
template <typename T,
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value,
detail::enabler> = detail::dummy>
constexpr const char *type_name() {
return "TEXT";
}
// Lexical cast
/// Integers / enums
template <typename T,
enable_if_t<std::is_integral<T>::value || std::is_enum<T>::value, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
try {
output = static_cast<T>(std::stoll(input));
return true;
} catch(const std::invalid_argument &) {
return false;
} catch(const std::out_of_range &) {
return false;
}
}
/// Floats
template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
try {
output = static_cast<T>(std::stold(input));
return true;
} catch(const std::invalid_argument &) {
return false;
} catch(const std::out_of_range &) {
return false;
}
}
/// String and similar
template <typename T,
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !std::is_enum<T>::value,
detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
output = input;
return true;
}
} // namespace detail
} // namespace CLI

View File

@ -0,0 +1,83 @@
#pragma once
// Distributed under the 3-Clause BSD License. See accompanying
// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include "CLI/TypeTools.hpp"
#include <functional>
#include <iostream>
#include <string>
// C standard library
// Only needed for existence checking
// Could be swapped for filesystem in C++17
#include <sys/stat.h>
#include <sys/types.h>
namespace CLI {
/// @defgroup validator_group Validators
/// @brief Some validators that are provided
///
/// These are simple `bool(std::string)` validators that are useful.
/// @{
/// Check for an existing file
inline bool ExistingFile(std::string filename) {
struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0;
bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
if(!exist) {
std::cerr << "File does not exist: " << filename << std::endl;
return false;
} else if(is_dir) {
std::cerr << "File is actually a directory: " << filename << std::endl;
return false;
} else {
return true;
}
}
/// Check for an existing directory
inline bool ExistingDirectory(std::string filename) {
struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0;
bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
if(!exist) {
std::cerr << "Directory does not exist: " << filename << std::endl;
return false;
} else if(is_dir) {
return true;
} else {
std::cerr << "Directory is actually a file: " << filename << std::endl;
return false;
}
}
/// Check for a non-existing path
inline bool NonexistentPath(std::string filename) {
struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0;
if(!exist) {
return true;
} else {
std::cerr << "Path exists: " << filename << std::endl;
return false;
}
}
/// Produce a range validator function
template <typename T> std::function<bool(std::string)> Range(T min, T max) {
return [min, max](std::string input) {
T val;
detail::lexical_cast(input, val);
return val >= min && val <= max;
};
}
/// Range of one value is 0 to value
template <typename T> std::function<bool(std::string)> Range(T max) { return Range(static_cast<T>(0), max); }
/// @}
} // namespace CLI

View File

@ -14,7 +14,7 @@ function maketable(str, arr)
enter().
append("tr");
var columns = ["sv", "iod", "eph-age-m", "latest-disco", "sisa", "delta_hz_corr", "e1bhs", "e1bdvs", "e5bhs", "e5bdvs", "gpshealth", "a0", "a1","a0g", "a1g", "sources", "db", "elev", "last-seen-s"];
var columns = ["sv", "iod", "aode", "eph-age-m", "latest-disco", "sisa", "delta_hz_corr", "health", "a0", "a1","a0g", "a1g", "sources", "db", "elev", "last-seen-s"];
// append the header row
thead.append("tr")
@ -61,28 +61,20 @@ function maketable(str, arr)
}
else if(column == "latest-disco" && row[column] != null)
ret.value = ((100*row[column]).toFixed(2))+" cm";
else if(column == "e1bdvs" || column =="e5bdvs") {
if(row[column] == 0)
ret.value = "valid";
else if(row[column] == 1)
ret.value = "working no guarantee"
}
else if(column == "e1bhs" || column =="e5bhs") {
if(row[column] == 0)
ret.value = "ok";
else if(row[column] == 1)
ret.value = "out of service"
else if(row[column] == 2)
ret.value = "will be out of service"
else if(row[column] == 3) {
else if(column == "health") {
ret.value = row[column];
console.log(row["healthissue"]);
if(row["healthissue"] == 1) {
ret.color="orange";
ret.value = "test"
}
if(row["healthissue"] == 2) {
ret.color="red";
}
}
else {
ret.value= row[column];
}
if(column == "eph-age-m" && row[column] > 30 && row["last-seen-s"] < 120)
if(column == "eph-age-m" && row[column] > 60 && row["last-seen-s"] < 120)
ret.color="orange";
if(column == "sisa" && parseInt(row[column]) > 312)
ret.color="#ffaaaa";

View File

@ -17,6 +17,9 @@
#include "navmon.pb.h"
#include "ephemeris.hh"
#include "gps.hh"
#include "glonass.hh"
#include "beidou.hh"
#include <unistd.h>
using namespace std;
@ -75,7 +78,15 @@ int main(int argc, char** argv)
}
cout<<"\n";
}
else if(nmm.type() == NavMonMessage::BeidouInavType) {
int sv = nmm.bi().gnsssv();
auto cond = getCondensedBeidouMessage(std::basic_string<uint8_t>((uint8_t*)nmm.bi().contents().c_str(), nmm.bi().contents().size()));
BeidouMessage bm;
uint8_t pageno;
int fraid = bm.parse(cond, &pageno);
cout<<"BeiDou "<<sv<<": "<<bm.sow<<", FraID "<<fraid<<endl;
}
else if(nmm.type() == NavMonMessage::ObserverPositionType) {
cout<<"ECEF"<<endl;
// XXX!! this has to deal with source id!

View File

@ -45,7 +45,7 @@ vector<uint64_t> getSources()
}
if(result->d_name[0] != '.') {
uint64_t src;
if(sscanf(result->d_name, "%08lx", &src)==1)
if(sscanf(result->d_name, "%08llx", &src)==1)
ret.push_back(src);
}
}

View File

@ -19,9 +19,11 @@
#include "navmon.pb.h"
#include "ephemeris.hh"
#include "gps.hh"
#include "glonass.hh"
#include "beidou.hh"
#include <optional>
using namespace std;
struct EofException{};
Point g_ourpos(3922.505 * 1000, 290.116 * 1000, 5004.189 * 1000);
@ -89,6 +91,34 @@ struct SVIOD
return words[2] && words[3];
}
void addGalileoWord(std::basic_string_view<uint8_t> page);
double getMu() const
{
if(gnssid == 2) return 3.986004418 * pow(10.0, 14.0);
if(gnssid == 0) return 3.986005 * pow(10.0, 14.0);
throw std::runtime_error("getMu() called for unsupported gnssid "+to_string(gnssid));
} // 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 t0e; }
double getSqrtA() const { return ldexp(sqrtA, -19); }
double getE() const { return ldexp(e, -33); }
double getCuc() const { return ldexp(cuc, -29); } // radians
double getCus() const { return ldexp(cus, -29); } // radians
double getCrc() const { return ldexp(crc, -5); } // meters
double getCrs() const { return ldexp(crs, -5); } // meters
double getM0() const { return ldexp(m0 * M_PI, -31); } // radians
double getDeltan()const { return ldexp(deltan *M_PI, -43); } //radians/s
double getI0() const { return ldexp(i0 * M_PI, -31); } // radians
double getCic() const { return ldexp(cic, -29); } // radians
double getCis() const { return ldexp(cis, -29); } // radians
double getOmegadot() const { return ldexp(omegadot * M_PI, -43); } // radians/s
double getOmega0() const { return ldexp(omega0 * M_PI, -31); } // radians
double getIdot() const { return ldexp(idot * M_PI, -43); } // radians/s
double getOmega() const { return ldexp(omega * M_PI, -31); } // radians
};
void SVIOD::addGalileoWord(std::basic_string_view<uint8_t> page)
@ -165,6 +195,12 @@ struct SVStat
uint8_t dn; // leap second day number
// 1 2^-31 2^-43 2^-55 16 second
int ura, af0, af1, af2, t0c; // GPS parameters that should not be here XXX
// beidou:
int t0eMSB{-1}, t0eLSB{-1}, aode{-1};
BeidouMessage beidouMessage, oldBeidouMessage;
BeidouMessage lastBeidouMessage2;
map<uint64_t, SVPerRecv> perrecv;
pair<uint32_t, double> deltaHz;
double latestDisco{-1};
@ -349,15 +385,19 @@ struct InfluxPusher
{
explicit InfluxPusher(std::string_view dbname) : d_dbname(dbname)
{
if(dbname=="null")
cout<<"Not sending data to influxdb"<<endl;
}
void addValue( const pair<pair<int,int>,SVStat>& ent, string_view name, auto value)
template<typename T>
void addValue( const pair<pair<int,int>,SVStat>& ent, string_view name, const T& value)
{
d_buffer+= string(name)+",gnssid="+to_string(ent.first.first)+ +",sv=" +to_string(ent.first.second)+" value="+to_string(value)+
" "+to_string(nanoTime(ent.first.first, ent.second.wn, ent.second.tow))+"\n";
checkSend();
}
void addValue(pair<int,int> id, string_view name, auto value)
template<typename T>
void addValue(pair<int,int> id, string_view name, const T& value)
{
if(g_svstats[id].wn ==0 && g_svstats[id].tow == 0)
return;
@ -392,7 +432,8 @@ struct InfluxPusher
~InfluxPusher()
{
doSend(d_buffer);
if(d_dbname != "null")
doSend(d_buffer);
}
std::string d_buffer;
@ -446,6 +487,36 @@ std::optional<double> getHzCorrection(time_t now)
return allHzCorr;
}
char getGNSSChar(int id)
{
if(id==0)
return 'G';
if(id==2)
return 'E';
if(id==3)
return 'C';
if(id==6)
return 'R';
else
return '0'+id;
}
double getElevation(const Point& p)
{
Point our = g_ourpos;
Point core{0,0,0};
Vector core2us(core, our);
Vector dx(our, p); // = x-ourx, dy = y-oury, dz = z-ourz;
// https://ds9a.nl/articles/
double elev = acos ( core2us.inner(dx) / (core2us.length() * dx.length()));
double deg = 180.0* (elev/M_PI);
return 90.0 - deg;
}
int main(int argc, char** argv)
try
{
@ -478,8 +549,6 @@ try
int dw = (uint8_t)g_svstats[{0,sv}].wn - g_svstats[{0,sv}].wn0t;
int age = dw * 7 * 86400 + g_svstats[{0,sv}].tow - g_svstats[{0,sv}].t0t; // t0t is PRESCALED
gpsutcstats[age]=s.first.second;
continue;
}
@ -532,17 +601,23 @@ try
h2s.addHandler("/svs", [](auto handler, auto req) {
nlohmann::json ret = nlohmann::json::object();
auto hzCorrection = getHzCorrection(time(0));
for(const auto& s: g_svstats) {
nlohmann::json item = nlohmann::json::object();
if(!s.second.tow) // I know, I know, will suck briefly
continue;
item["gnssid"] = s.first.first;
item["svid"] = s.first.second;
if(s.first.first == 3) {
item["sisa"]=humanUra(s.second.ura);
if(s.second.t0eMSB >= 0 && s.second.t0eLSB >=0)
item["eph-age-m"] = ephAge(s.second.tow, 8.0*((s.second.t0eMSB<<15) + s.second.t0eLSB))/60.0;
}
if(s.second.completeIOD()) {
item["iod"]=s.second.getIOD();
if(s.first.first ==0) {
if(s.first.first == 0 || s.first.first == 3) {
item["sisa"]=humanUra(s.second.ura);
// cout<<"Asked to convert "<<s.second.ura<<" for sv "<<s.first.first<<","<<s.first.second<<endl;
}
@ -564,8 +639,6 @@ try
Vector core2us(core, our);
Vector dx(our, p); // = x-ourx, dy = y-oury, dz = z-ourz;
// https://ds9a.nl/articles/
double elev = acos ( core2us.inner(dx) / (core2us.length() * dx.length()));
double deg = 180.0* (elev/M_PI);
item["elev"] = 90 - deg;
@ -580,22 +653,46 @@ try
if(hzCorrection)
item["delta_hz_corr"] = s.second.deltaHz.second - *hzCorrection;
}
}
item["a0"]=s.second.a0;
item["a1"]=s.second.a1;
item["dtLS"]=s.second.dtLS;
if(s.first.first == 3) { // beidou
item["a0g"]=s.second.a0g;
item["a1g"]=s.second.a1g;
if(s.second.aode > 0)
item["aode"]=s.second.aode;
}
if(s.first.first == 2) { // galileo
item["a0g"]=s.second.a0g;
item["a1g"]=s.second.a1g;
vector<string> options{"ok", "out of service", "will be out of service", "test"};
item["health"] =
options[s.second.e1bhs] +"/" +
options[s.second.e5bhs] +"/" +
(s.second.e1bdvs ? "no guarantee" : "val") +"/"+
(s.second.e5bdvs ? "no guarantee" : "val");
item["e5bdvs"]=s.second.e5bdvs;
item["e1bdvs"]=s.second.e1bdvs;
item["e5bhs"]=s.second.e5bhs;
item["e1bhs"]=s.second.e1bhs;
item["healthissue"]=0;
if(s.second.e1bhs == 2 || s.second.e5bhs == 2)
item["healthissue"] = 1;
if(s.second.e1bhs == 3 || s.second.e5bhs == 3)
item["healthissue"] = 1;
if(s.second.e1bdvs || s.second.e5bdvs || s.second.e1bhs == 1 || s.second.e5bhs == 1)
item["healthissue"] = 2;
}
else if(s.first.first == 0 || s.first.first == 3) {// gps or beidou
item["health"]= s.second.gpshealth ? ("NOT OK: "+to_string(s.second.gpshealth)) : string("OK");
item["healthissue"]= 2* !!s.second.gpshealth;
}
else
item["gpshealth"]=s.second.gpshealth;
nlohmann::json perrecv = nlohmann::json::object();
for(const auto& pr : s.second.perrecv) {
@ -607,6 +704,7 @@ try
}
item["perrecv"]=perrecv;
// xxx this is silly, should use local time
item["last-seen-s"] = s.second.tow ? (7*86400*(latestWN(s.first.first) - s.second.wn) + latestTow(s.first.first) - (int)s.second.tow) : -1;
if(s.second.latestDisco >=0) {
item["latest-disco"]= s.second.latestDisco;
@ -615,7 +713,7 @@ try
item["wn"] = s.second.wn;
item["tow"] = s.second.tow;
ret[fmt::sprintf("%c%02d", s.first.first ? 'E' : 'G', s.first.second)] = item;
ret[fmt::sprintf("%c%02d", getGNSSChar(s.first.first), s.first.second)] = item;
}
return ret;
});
@ -660,8 +758,6 @@ try
g_svstats[id].perrecv[nmm.sourceid()].el = nmm.rd().el();
g_svstats[id].perrecv[nmm.sourceid()].azi = nmm.rd().azi();
// THIS HAS TO SPLIT OUT PER SOURCE
idb.addValue(id, "db", nmm.rd().db());
if(nmm.rd().el() <= 90 && nmm.rd().el() > 0)
@ -919,6 +1015,60 @@ try
if(g_svstats[id].wn < 512)
g_svstats[id].wn += 2048;
}
else if(nmm.type()== NavMonMessage::BeidouInavType) {
pair<int,int> id{nmm.bi().gnssid(), nmm.bi().gnsssv()};
g_svstats[id].perrecv[nmm.sourceid()].t = nmm.localutcseconds();
auto& svstat = g_svstats[id];
uint8_t pageno;
auto cond = getCondensedBeidouMessage(std::basic_string<uint8_t>((uint8_t*)nmm.bi().contents().c_str(), nmm.bi().contents().size()));
auto& bm = svstat.beidouMessage;
int fraid=bm.parse(cond, &pageno);
svstat.tow = nmm.bi().gnsstow();
svstat.wn = nmm.bi().gnsswn();
if(fraid == 1) {
svstat.ura = bm.urai;
svstat.gpshealth = bm.sath1;
svstat.af0 = bm.a0;
svstat.af1 = bm.a1;
svstat.af2 = bm.a2;
svstat.aode = bm.aode;
}
if(fraid == 2) {
svstat.lastBeidouMessage2 = bm;
svstat.t0eMSB = bm.t0eMSB;
}
if(fraid == 3) {
svstat.t0eLSB = bm.t0eLSB;
Point oldpoint, newpoint;
if(bm.sow - svstat.lastBeidouMessage2.sow == 6 && svstat.oldBeidouMessage.sow >= 0 && svstat.oldBeidouMessage.getT0e() != svstat.beidouMessage.getT0e()) {
getCoordinates(svstat.wn, svstat.tow, svstat.oldBeidouMessage, &oldpoint);
getCoordinates(svstat.wn, svstat.tow, bm, &newpoint);
Vector jump(oldpoint ,newpoint);
cout<<fmt::sprintf("Discontinuity C%02d (%f,%f,%f) -> (%f, %f, %f), jump: %f, seconds: %f\n",
id.second, oldpoint.x, oldpoint.y, oldpoint.z,
newpoint.x, newpoint.y, newpoint.z, jump.length(), (double)bm.getT0e() - svstat.oldBeidouMessage.getT0e());
svstat.latestDisco = jump.length();
}
svstat.oldBeidouMessage = bm;
}
if(fraid==5 && pageno == 9) {
svstat.a0g = bm.a0gps;
svstat.a1g = bm.a1gps;
}
if(fraid==5 && pageno == 10) {
svstat.a0 = bm.a0utc;
svstat.a1 = bm.a1utc;
}
Point core, sat;
getCoordinates(svstat.wn, svstat.tow, bm, &sat);
Vector l(core, sat);
cout<<"C"<<id.second<< " "<<bm.sow<<" "<<(bm.sow % 30 )<<" FraID "<<fraid<<" "<<fmt::format("({0}, {1}, {2})", sat.x, sat.y, sat.z) <<", r: "<<l.length()<<" elev: "<<getElevation(sat)<<endl;
}
else {
cout<<"Unknown type "<< (int)nmm.type()<<endl;
}

View File

@ -22,13 +22,16 @@
#include <arpa/inet.h>
#include "navmon.pb.h"
#include "gps.hh"
#include "glonass.hh"
#include "beidou.hh"
#include "CLI/CLI.hpp"
struct timespec g_gstutc;
uint16_t g_wn;
using namespace std;
uint16_t g_srcid{2};
#define BAUDRATE B921600
#define BAUDRATE B115200
#define MODEMDEVICE "/dev/ttyACM0"
namespace {
@ -208,28 +211,48 @@ UBXMessage waitForUBX(int fd, int seconds, uint8_t ubxClass, uint8_t ubxType)
for(int n=0; n < seconds*20; ++n) {
auto [msg, tv] = getUBXMessage(fd);
(void) tv;
// cerr<<"Got: "<<(int)msg.getClass() << " " <<(int)msg.getType() <<endl;
if(msg.getClass() == ubxClass && msg.getType() == ubxType) {
return msg;
}
else
cerr<<"Got: "<<(int)msg.getClass() << " " <<(int)msg.getType() <<" while waiting for "<<(int)ubxClass<<" " <<(int)ubxType<<endl;
}
throw std::runtime_error("Did not get response on time");
}
bool waitForUBXAckNack(int fd, int seconds)
struct TimeoutError{};
bool waitForUBXAckNack(int fd, int seconds, int ubxClass, int ubxType)
{
for(int n=0; n < seconds*4; ++n) {
time_t start = time(0);
for(int n=0; n < 100; ++n) {
auto [msg, tv] = getUBXMessage(fd);
(void)tv;
// cerr<<"Got: "<<(int)msg.getClass() << " " <<(int)msg.getType() <<endl;
if(msg.getClass() == 0x05 && msg.getType() == 0x01) {
if(time(0) - start > seconds) {
throw TimeoutError();
}
if(msg.getClass() != 5 || !(msg.getType() == 0 || msg.getType() == 1)) {
cerr<<"Got: "<<(int)msg.getClass() << " " <<(int)msg.getType() <<" while waiting for ack/nack of " << ubxClass<<" "<<ubxType<<endl;
continue;
}
const auto& payload = msg.getPayload();
if(payload.size() != 2) {
cerr << "Wrong payload size for ack/nack: "<<payload.size()<<endl;
continue;
}
cerr<<"Got an " << (msg.getType() ? "ack" : "nack")<<" for "<<(int)payload[0] <<" " << (int)payload[1]<<" while waiting for "<<ubxClass<<" " <<ubxType<<endl;
if(msg.getClass() == 0x05 && msg.getType() == 0x01 && payload[0]==ubxClass && payload[1]==ubxType) {
return true;
}
else if(msg.getClass() == 0x05 && msg.getType() == 0x00) {
else if(msg.getClass() == 0x05 && msg.getType() == 0x00 && payload[0]==ubxClass && payload[1]==ubxType) {
return false;
}
}
throw std::runtime_error("Did not get ACK/NACK response on time");
throw std::runtime_error("Did not get ACK/NACK response for class "+to_string(ubxClass)+" type "+to_string(ubxType)+" on time");
}
void emitNMM(int fd, const NavMonMessage& nmm)
@ -243,14 +266,23 @@ void emitNMM(int fd, const NavMonMessage& nmm)
writen2(fd, out.c_str(), out.size());
}
void enableUBXMessageUSB(int fd, uint8_t ubxClass, uint8_t ubxType)
void enableUBXMessageUSB(int fd, uint8_t ubxClass, uint8_t ubxType, uint8_t rate=1)
{
auto msg = buildUbxMessage(0x06, 0x01, {ubxClass, ubxType, 0, 0, 0, 1, 0, 0});
writen2(fd, msg.c_str(), msg.size());
if(waitForUBXAckNack(fd, 2))
return;
else
throw std::runtime_error("Got NACK enabling UBX message "+to_string((int)ubxClass)+" "+to_string((int)ubxType));
for(int n=0 ; n < 5; ++n) {
try {
auto msg = buildUbxMessage(0x06, 0x01, {ubxClass, ubxType, 0, 0, 0, rate, 0, 0});
writen2(fd, msg.c_str(), msg.size());
if(waitForUBXAckNack(fd, 2, 0x06, 0x01))
return;
else
throw std::runtime_error("Got NACK enabling UBX message "+to_string((int)ubxClass)+" "+to_string((int)ubxType));
}
catch(TimeoutError& te) {
cerr<<"Had "<<n<<"th timeout in enableUBXMessageUSB"<<endl;
continue;
}
}
throw TimeoutError();
}
bool isCharDevice(string_view fname)
@ -262,33 +294,34 @@ bool isCharDevice(string_view fname)
}
// ubxtool device srcid
int main(int argc, char** argv)
void readSome(int fd)
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
if(argc != 3) {
cout<<"syntax: ubxtool /dev/ttyACM0 1\nDevice name, followed by your assigned source id"<<endl;
return EXIT_FAILURE;
for(int n=0; n < 5; ++n) {
auto [msg, timestamp] = getUBXMessage(fd);
(void)timestamp;
cerr<<"Read some init: "<<(int)msg.getClass() << " " <<(int)msg.getType() <<endl;
if(msg.getClass() == 0x4)
cerr<<string((char*)msg.getPayload().c_str(), msg.getPayload().size()) <<endl;
}
struct termios oldtio,newtio;
}
struct termios g_oldtio;
int initFD(const char* fname)
{
int fd;
if(string(fname) != "stdin" && string(fname) != "/dev/stdin" && isCharDevice(fname)) {
fd = open(fname, O_RDWR | O_NOCTTY );
if (fd <0 ) {
throw runtime_error("Opening file "+string(fname));
}
struct termios newtio;
if(tcgetattr(fd, &g_oldtio)) { /* save current port settings */
perror("tcgetattr");
exit(-1);
}
if(string(argv[1]) != "stdin" && string(argv[1]) != "/dev/stdin" && isCharDevice(argv[1]))
fd = open(argv[1], O_RDWR | O_NOCTTY );
else {
g_fromFile = true;
fd = open(argv[1], O_RDONLY );
}
if (fd <0) {perror(argv[1]); exit(-1); }
g_srcid = atoi(argv[2]);
if(!g_fromFile) {
tcgetattr(fd,&oldtio); /* save current port settings */
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
@ -301,81 +334,162 @@ int main(int argc, char** argv)
newtio.c_cc[VMIN] = 5; /* blocking read until 5 chars received */
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
for(int n=0; n < 5; ++n) {
auto [msg, timestamp] = getUBXMessage(fd);
(void)timestamp;
cerr<<"Read some init: "<<(int)msg.getClass() << " " <<(int)msg.getType() <<endl;
// if(msg.getClass() == 0x2)
//cerr<<string((char*)msg.getPayload().c_str(), msg.getPayload().size()) <<endl;
if(tcsetattr(fd,TCSANOW, &newtio)) {
perror("tcsetattr");
exit(-1);
}
}
else {
g_fromFile = true;
std::basic_string<uint8_t> msg;
cerr<<"Asking for rate"<<endl;
msg = buildUbxMessage(0x06, 0x01, {0x02, 89}); // ask for rates of class 0x02 type 89, RLM
writen2(fd, msg.c_str(), msg.size());
UBXMessage um=waitForUBX(fd, 2, 0x06, 0x01);
cerr<<"Message rate: "<<endl;
for(const auto& c : um.getPayload())
cerr<<(int)c<< " ";
cerr<<endl;
msg = buildUbxMessage(0x06, 0x01, {0x02, 89, 0, 0, 0, 1, 0, 0});
writen2(fd, msg.c_str(), msg.size());
if(waitForUBXAckNack(fd, 2))
cerr<<"Got ack on rate setting"<<endl;
else
cerr<<"Got nack on rate setting"<<endl;
// ver RO maxch cfgs
msg = buildUbxMessage(0x06, 0x3e, {0x00, 0x00, 0xff, 0x02,
// GPS min max res x1 x2 x3, x4
0x00, 0x04, 0x08, 0, 0x01, 0x00, 0x01, 0x00,
// GAL min max res x1 x2 x3, x4
0x00, 0x04, 0x08, 0, 0x01, 0x00, 0x01, 0x00
fd = open(fname, O_RDONLY );
if(fd < 0)
throw runtime_error("Opening file "+string(fname));
}
return fd;
});
writen2(fd, msg.c_str(), msg.size());
}
// ubxtool device srcid
int main(int argc, char** argv)
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
CLI::App app("ubxtool");
if(waitForUBXAckNack(fd, 2))
cerr<<"Got ack on GNSS setting"<<endl;
else
cerr<<"Got nack on GNSS setting"<<endl;
cerr<<"Disabling NMEA"<<endl;
msg = buildUbxMessage(0x06, 0x00, {0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00});
writen2(fd, msg.c_str(), msg.size());
if(waitForUBXAckNack(fd, 2))
cerr<<"NMEA disabled"<<endl;
else
cerr<<"Got NACK disabling NMEA"<<endl;
msg = buildUbxMessage(0x06, 0x00, {0x03});
writen2(fd, msg.c_str(), msg.size());
um=waitForUBX(fd, 2, 0x06, 0x00);
cerr<<"Protocol settings on USB: \n";
for(const auto& c : um.getPayload())
cerr<<(int)c<< " ";
cerr<<endl;
cerr<<"Enabling UBX-RXM-RAWX"<<endl; // RF doppler
enableUBXMessageUSB(fd, 0x02, 0x15);
cerr<<"Enabling UBX-RXM-SFRBX"<<endl; // raw navigation frames
enableUBXMessageUSB(fd, 0x02, 0x13);
cerr<<"Enabling UBX-NAV-POSECEF"<<endl; // position
enableUBXMessageUSB(fd, 0x01, 0x01);
cerr<<"Enabling UBX-NAV-SAT"<<endl; // satellite reception details
enableUBXMessageUSB(fd, 0x01, 0x35);
cerr<<"Enabling UBX-NAV-PVT"<<endl; // position, velocity, time fix
enableUBXMessageUSB(fd, 0x01, 0x07);
vector<std::string> serial;
bool doGPS{true}, doGalileo{true}, doGlonass{false}, doBeidou{true};
app.add_option("serial", serial, "Serial");
app.add_flag("--beidou,-c", doBeidou, "Enable BeiDou reception");
app.add_flag("--gps,-g", doGPS, "Enable GPS reception");
app.add_flag("--glonass,-r", doGlonass, "Enable Glonass reception");
app.add_flag("--galileo,-e", doGalileo, "Enable Galileo reception");
try {
app.parse(argc, argv);
} catch(const CLI::Error &e) {
return app.exit(e);
}
if(serial.size() != 2) {
cout<<app.help()<<endl;
return EXIT_FAILURE;
}
int fd = initFD(serial[0].c_str());
g_srcid = atoi(serial[1].c_str());
if(!g_fromFile) {
bool doInit = true;
if(doInit) {
readSome(fd);
std::basic_string<uint8_t> msg;
cerr<<"Sending a soft reset"<<endl;
msg = buildUbxMessage(0x06, 0x04, {0x00, 0x00, 0x01, 0x00});
writen2(fd, msg.c_str(), msg.size());
usleep(100000);
close(fd);
for(int n=0 ; n< 20; ++n) {
cerr<<"Waiting for device to come back"<<endl;
try {
fd = initFD(serial[0].c_str());
readSome(fd);
}
catch(...)
{
cerr<<"Not yet back"<<endl;
usleep(400000);
continue;
}
break;
}
cerr<<"Sending GNSS query"<<endl;
msg = buildUbxMessage(0x06, 0x3e, {});
writen2(fd, msg.c_str(), msg.size());
UBXMessage um1=waitForUBX(fd, 2, 0x06, 0x3e);
auto payload = um1.getPayload();
cerr<<"GNSS status, got " << (int)payload[3]<<" rows:\n";
for(uint8_t n = 0 ; n < payload[3]; ++n) {
cerr<<"GNSSID "<<(int)payload[4+8*n]<<" enabled "<<(int)payload[8+8*n]<<" minTrk "<< (int)payload[5+8*n] <<" maxTrk "<<(int)payload[6+8*n]<<endl;
}
if(waitForUBXAckNack(fd, 2, 0x06, 0x3e)) {
cerr<<"Got ACK for our poll of GNSS settings"<<endl;
}
// ver RO maxch cfgs
msg = buildUbxMessage(0x06, 0x3e, {0x00, 0x00, 0xff, 0x04,
// GPS min max res x1 x2 x3, x4
0x00, 0x04, 0x08, 0, doGPS, 0x00, 0x01, 0x00,
// BEI min max res x1 x2 x3, x4
0x03, 0x04, 0x08, 0, doBeidou, 0x00, 0x01, 0x00,
// GAL min max res x1 x2 x3, x4
0x02, 0x04, 0x08, 0, doGalileo, 0x00, 0x01, 0x00,
// GLO min max res x1 x2 x3, x4
0x06, 0x04, 0x08, 0, doGlonass, 0x00, 0x01, 0x00
});
cerr<<"Sending GNSS setting, GPS: "<<doGPS<<", Galileo: "<<doGalileo<<", BeiDou: "<<doBeidou<<", GLONASS: "<<doGlonass<<endl;
writen2(fd, msg.c_str(), msg.size());
if(waitForUBXAckNack(fd, 2, 0x06, 0x3e))
cerr<<"Got ack on GNSS setting"<<endl;
else {
cerr<<"Got nack on GNSS setting"<<endl;
exit(-1);
}
cerr<<"Disabling NMEA"<<endl;
msg = buildUbxMessage(0x06, 0x00, {0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00});
writen2(fd, msg.c_str(), msg.size());
if(waitForUBXAckNack(fd, 10, 0x06, 0x00))
cerr<<"NMEA disabled"<<endl;
else
cerr<<"Got NACK disabling NMEA"<<endl;
cerr<<"Polling USB settings"<<endl; // UBX-CFG-PRT, 0x03 == USB
msg = buildUbxMessage(0x06, 0x00, {0x03});
writen2(fd, msg.c_str(), msg.size());
UBXMessage um=waitForUBX(fd, 4, 0x06, 0x00); // UBX-CFG-PRT
cerr<<"Protocol settings on USB: \n";
for(const auto& c : um.getPayload())
cerr<<(int)c<< " ";
cerr<<endl;
if(waitForUBXAckNack(fd, 10, 0x06, 0x00))
cerr<<"Got ACK on USB port config"<<endl;
else
cerr<<"Got NACK on USB port config"<<endl;
cerr<<"Enabling UBX-RXM-RLM"<<endl; // SAR
enableUBXMessageUSB(fd, 0x02, 0x59);
cerr<<"Enabling UBX-RXM-RAWX"<<endl; // RF doppler
enableUBXMessageUSB(fd, 0x02, 0x15);
cerr<<"Enabling UBX-RXM-SFRBX"<<endl; // raw navigation frames
enableUBXMessageUSB(fd, 0x02, 0x13);
cerr<<"Enabling UBX-NAV-POSECEF"<<endl; // position
enableUBXMessageUSB(fd, 0x01, 0x01, 8);
cerr<<"Enabling UBX-NAV-SAT"<<endl; // satellite reception details
enableUBXMessageUSB(fd, 0x01, 0x35, 4);
cerr<<"Enabling UBX-NAV-PVT"<<endl; // position, velocity, time fix
enableUBXMessageUSB(fd, 0x01, 0x07, 1);
}
}
/* goal: isolate UBX messages, ignore everyting else.
@ -392,7 +506,7 @@ int main(int argc, char** argv)
std::map<pair<int,int>, struct timeval> lasttv, tv;
int curCycleTOW{-1}; // means invalid
cerr<<"Entering main loop"<<endl;
for(;;) {
try {
auto [msg, timestamp] = getUBXMessage(fd);
@ -427,7 +541,7 @@ int main(int argc, char** argv)
tm.tm_sec = pvt.sec;
uint32_t satt = timegm(&tm);
double satutc = timegm(&tm) + pvt.nano/1000000000.0; // negative is no problem here
// double satutc = timegm(&tm) + pvt.nano/1000000000.0; // negative is no problem here
if(pvt.nano < 0) {
pvt.sec--;
satt--;
@ -438,17 +552,17 @@ int main(int argc, char** argv)
g_gstutc.tv_nsec = pvt.nano;
double seconds= pvt.sec + pvt.nano/1000000000.0;
// double seconds= pvt.sec + pvt.nano/1000000000.0;
// fmt::fprintf(stderr, "Satellite UTC: %02d:%02d:%06.4f -> %.4f or %d:%f\n", tm.tm_hour, tm.tm_min, seconds, satutc, timegm(&tm), pvt.nano/1000.0);
if(!g_fromFile) {
struct tm ourtime;
time_t ourt = timestamp.tv_sec;
gmtime_r(&ourt, &ourtime);
// struct tm ourtime;
// time_t ourt = timestamp.tv_sec;
// gmtime_r(&ourt, &ourtime);
//
//double ourutc = ourt + timestamp.tv_usec/1000000.0;
double ourutc = ourt + timestamp.tv_usec/1000000.0;
seconds = ourtime.tm_sec + timestamp.tv_usec/1000000.0;
// seconds = ourtime.tm_sec + timestamp.tv_usec/1000000.0;
// fmt::fprintf(stderr, "Our UTC : %02d:%02d:%06.4f -> %.4f or %d:%f -> delta = %.4fs\n", tm.tm_hour, tm.tm_min, seconds, ourutc, timestamp.tv_sec, 1.0*timestamp.tv_usec, ourutc - satutc);
}
}
@ -473,7 +587,7 @@ int main(int argc, char** argv)
uint8_t prStddev = payload[43+23*n] & 0xf;
uint8_t cpStddev = payload[44+23*n] & 0xf;
uint8_t doStddev = payload[45+23*n] & 0xf;
uint8_t trkStat = payload[46+23*n] & 0xf;
// uint8_t trkStat = payload[46+23*n] & 0xf;
NavMonMessage nmm;
nmm.set_type(NavMonMessage::RFDataType);
@ -540,7 +654,7 @@ int main(int argc, char** argv)
nmm.set_sourceid(g_srcid);
// cerr<<"GPS frame, numwords: "<<(int)payload[4]<<", version: "<<(int)payload[6]<<endl;
static int wn, tow;
auto gpsframe = getGPSFromSFRBXMsg(id.second, payload);
auto gpsframe = getGPSFromSFRBXMsg(payload);
auto cond = getCondensedGPSMessage(gpsframe);
auto frameno = getbitu(&cond[0], 24+19, 3);
if(frameno == 1)
@ -557,66 +671,112 @@ int main(int argc, char** argv)
emitNMM(1, nmm);
continue;
}
auto inav = getInavFromSFRBXMsg(payload);
unsigned int wtype = getbitu(&inav[0], 0, 6);
tv[id] = timestamp;
// cerr<<"gnssid "<<id.first<<" sv "<<id.second<<" " << wtype << endl;
uint32_t satTOW;
int msgTOW{0};
if(getTOWFromInav(inav, &satTOW, &g_wn)) { // 0, 6, 5
// cerr<<" "<<wtype<<" sv "<<id.second<<" tow "<<satTOW << " % 30 = "<< satTOW % 30<<", implied start of cycle: "<<(satTOW - (satTOW %30)) <<endl;
msgTOW = satTOW;
curCycleTOW = satTOW - (satTOW %30);
else if(id.first ==2) {
auto inav = getInavFromSFRBXMsg(payload);
unsigned int wtype = getbitu(&inav[0], 0, 6);
tv[id] = timestamp;
// cerr<<"gnssid "<<id.first<<" sv "<<id.second<<" " << wtype << endl;
uint32_t satTOW;
int msgTOW{0};
if(getTOWFromInav(inav, &satTOW, &g_wn)) { // 0, 6, 5
// cerr<<" "<<wtype<<" sv "<<id.second<<" tow "<<satTOW << " % 30 = "<< satTOW % 30<<", implied start of cycle: "<<(satTOW - (satTOW %30)) <<endl;
msgTOW = satTOW;
curCycleTOW = satTOW - (satTOW %30);
}
else {
if(curCycleTOW < 0) // did not yet have a start of cycle
continue;
// cerr<<" "<<wtype<<" sv "<<id.second<<" tow ";
if(wtype == 2) {
// cerr<<"infered to be 1 "<<curCycleTOW + 31<<endl;
msgTOW = curCycleTOW + 31;
}
else if(wtype == 4) {
// cerr<<"infered to be 3 "<<curCycleTOW + 33<<endl;
msgTOW = curCycleTOW + 33;
} // next have '6' which sets TOW
else if(wtype==7 || wtype == 9) {
msgTOW = curCycleTOW + 7;
}
else if(wtype==8 || wtype == 10) {
msgTOW = curCycleTOW + 9;
}
else if(wtype==1) {
msgTOW = curCycleTOW + 21;
}
else if(wtype==3) {
msgTOW = curCycleTOW + 23;
}
else { // dummy
cerr<<"what kind of wtype is this"<<endl;
continue;
}
}
NavMonMessage nmm;
nmm.set_sourceid(g_srcid);
nmm.set_type(NavMonMessage::GalileoInavType);
nmm.set_localutcseconds(g_gstutc.tv_sec);
nmm.set_localutcnanoseconds(g_gstutc.tv_nsec);
nmm.mutable_gi()->set_gnsswn(g_wn);
nmm.mutable_gi()->set_gnsstow(msgTOW);
nmm.mutable_gi()->set_gnssid(id.first);
nmm.mutable_gi()->set_gnsssv(id.second);
nmm.mutable_gi()->set_contents((const char*)&inav[0], inav.size());
emitNMM(1, nmm);
}
else {
if(curCycleTOW < 0) // did not yet have a start of cycle
continue;
// cerr<<" "<<wtype<<" sv "<<id.second<<" tow ";
if(wtype == 2) {
// cerr<<"infered to be 1 "<<curCycleTOW + 31<<endl;
msgTOW = curCycleTOW + 31;
}
else if(wtype == 4) {
// cerr<<"infered to be 3 "<<curCycleTOW + 33<<endl;
msgTOW = curCycleTOW + 33;
} // next have '6' which sets TOW
else if(wtype==7 || wtype == 9) {
msgTOW = curCycleTOW + 7;
}
else if(wtype==8 || wtype == 10) {
msgTOW = curCycleTOW + 9;
}
else if(wtype==1) {
msgTOW = curCycleTOW + 21;
}
else if(wtype==3) {
msgTOW = curCycleTOW + 23;
}
else { // dummy
cerr<<"what kind of wtype is this"<<endl;
else if(id.first==3) {
auto gstr = getGlonassFromSFRBXMsg(payload);
auto cond = getCondensedBeidouMessage(gstr);
static map<int, BeidouMessage> bms;
auto& bm = bms[id.second];
uint8_t pageno;
int fraid=bm.parse(cond, &pageno);
cerr<<"BeiDou C"<<id.second<<" FraID "<< fraid<<" SOW "<< bm.sow<<" ";
if(fraid == 1)
cerr<<" sath1 "<<(int)bm.sath1<<" aodc "<<(int)bm.aodc <<" urai "<<(int)bm.urai<<" WN "<<bm.wn;
else
;
cerr<<endl;
if(bm.wn < 0) {
cerr<<"BeiDou WN unknown, not yet emitting message"<<endl;
continue;
}
NavMonMessage nmm;
nmm.set_type(NavMonMessage::BeidouInavType);
nmm.set_localutcseconds(g_gstutc.tv_sec);
nmm.set_localutcnanoseconds(g_gstutc.tv_nsec);
nmm.set_sourceid(g_srcid);
nmm.mutable_bi()->set_gnsswn(bm.wn);
nmm.mutable_bi()->set_gnsstow(bm.sow);
nmm.mutable_bi()->set_gnssid(id.first);
nmm.mutable_bi()->set_gnsssv(id.second);
nmm.mutable_bi()->set_contents(string((char*)gstr.c_str(), gstr.size()));
emitNMM(1, nmm);
continue;
}
NavMonMessage nmm;
nmm.set_sourceid(g_srcid);
nmm.set_type(NavMonMessage::GalileoInavType);
nmm.set_localutcseconds(g_gstutc.tv_sec);
nmm.set_localutcnanoseconds(g_gstutc.tv_nsec);
nmm.mutable_gi()->set_gnsswn(g_wn);
nmm.mutable_gi()->set_gnsstow(msgTOW);
nmm.mutable_gi()->set_gnssid(id.first);
nmm.mutable_gi()->set_gnsssv(id.second);
nmm.mutable_gi()->set_contents((const char*)&inav[0], inav.size());
emitNMM(1, nmm);
else if(id.first==6) {
cerr<<"SFRBX from GLONASS "<<id.second<<" @ frequency "<<(int)payload[3]<<", msg of "<<(int)payload[4]<< " words"<<endl;
auto gstr = getGlonassFromSFRBXMsg(payload);
static map<int, GlonassMessage> gms;
GlonassMessage& gm = gms[id.second];
int strno = gm.parse(gstr);
cerr<<"R"<<id.second<<" "<<strno<<" ("<<gm.x<<", "<<gm.y<<", "<<gm.z<<") "<<sqrt(ldexp(gm.x, -11)*ldexp(gm.x, -11) + ldexp(gm.y, -11)*ldexp(gm.y, -11) + ldexp(gm.z, -11)*ldexp(gm.z, -11)) <<" -> ("<<gm.dx<<", "<<gm.dy<<", "<<gm.dz<<")"<<endl;
if(strno == 4)
cerr<<" R"<<id.second<<" "<< fmt::sprintf("%d %02d:%02d:%02d", gm.NT, (int)gm.hour,(int)gm.minute, (int)gm.seconds)<<" : P4="<<gm.P4<<" -> " << (int)gm.Tb<<", P1: "<<(int)gm.P1<<endl;
}
else
cerr<<"SFRBX from unsupported GNSSID "<<id.first<<", sv "<<id.second<<", "<<payload.size()<<" bytes"<<endl;
#if 0
if(0 && lasttv.count(id)) {
fmt::fprintf(stderr, "gnssid %d sv %d wtype %d, %d:%d -> %d:%d, delta=%d\n",
payload[0], payload[1], wtype, lasttv[id].tv_sec, lasttv[id].tv_usec, tv[id].tv_sec, tv[id].tv_usec, tv[id].tv_usec - lasttv[id].tv_usec);
}
#endif
lasttv[id]=tv[id];
}
catch(CRCMismatch& cm) {
@ -656,6 +816,6 @@ int main(int argc, char** argv)
}
}
if(!g_fromFile)
tcsetattr(fd,TCSANOW,&oldtio);
tcsetattr(fd, TCSANOW, &g_oldtio);
}