the whole thingus
parent
8ed9401cab
commit
0962be6b8b
10
Makefile
10
Makefile
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
36
ephemeris.hh
36
ephemeris.hh
|
@ -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) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 ¤t, 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 ¤t, 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
|
|
@ -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 <rim(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 <rim(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
|
|
@ -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(); }
|
|
@ -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
|
|
@ -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
|
|
@ -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";
|
||||
|
|
11
navdump.cc
11
navdump.cc
|
@ -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!
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
186
navparse.cc
186
navparse.cc
|
@ -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;
|
||||
}
|
||||
|
|
504
ubxtool.cc
504
ubxtool.cc
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue