14#include <fmt/ranges.h> 
   58    return "WiFiPositioning";
 
 
   68    return "Data Processor";
 
 
   77    if (ImGui::Button(fmt::format(
"Add Input Pin##{}", 
size_t(
id)).c_str()))
 
   85    if (ImGui::Button(fmt::format(
"Delete Input Pin##{}", 
size_t(
id)).c_str()))
 
   99    if (
auto frame = 
static_cast<int>(
_frame);
 
  100        ImGui::Combo(fmt::format(
"Frame##{}", 
size_t(
id)).c_str(), &frame, 
"ECEF\0LLA\0\0"))
 
  117                devPos.x() = 
rad2deg(devPos.x());
 
  118                devPos.y() = 
rad2deg(devPos.y());
 
  125    if (ImGui::BeginTable(
"AccessPointInput", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX, ImVec2(0.0F, 0.0F)))
 
  128        ImGui::TableSetupColumn(
"MAC address", ImGuiTableColumnFlags_WidthFixed, columnWidth);
 
  131            ImGui::TableSetupColumn(
"X", ImGuiTableColumnFlags_WidthFixed, columnWidth);
 
  132            ImGui::TableSetupColumn(
"Y", ImGuiTableColumnFlags_WidthFixed, columnWidth);
 
  133            ImGui::TableSetupColumn(
"Z", ImGuiTableColumnFlags_WidthFixed, columnWidth);
 
  137            ImGui::TableSetupColumn(
"Latitude", ImGuiTableColumnFlags_WidthFixed, columnWidth);
 
  138            ImGui::TableSetupColumn(
"Longitude", ImGuiTableColumnFlags_WidthFixed, columnWidth);
 
  139            ImGui::TableSetupColumn(
"Altitude", ImGuiTableColumnFlags_WidthFixed, columnWidth);
 
  141        ImGui::TableSetupColumn(
"Bias", ImGuiTableColumnFlags_WidthFixed, columnWidth);
 
  142        ImGui::TableSetupColumn(
"Scale", ImGuiTableColumnFlags_WidthFixed, columnWidth);
 
  145        ImGui::TableHeadersRow();
 
  147        for (
size_t rowIndex = 0; rowIndex < 
_numOfDevices; rowIndex++)
 
  149            ImGui::TableNextRow();
 
  150            ImGui::TableNextColumn();
 
  154            std::regex macRegex(
"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$");
 
  156            ImGui::SetNextItemWidth(columnWidth);
 
  157            if (ImGui::InputText(fmt::format(
"##Mac{}", rowIndex).c_str(), &
_deviceMacAddresses.at(rowIndex), ImGuiInputTextFlags_None))
 
  172                ImGui::TableNextColumn();
 
  173                ImGui::SetNextItemWidth(columnWidth);
 
  174                if (ImGui::InputDouble(fmt::format(
"##InputX{}", rowIndex).c_str(), &devPosRow[0], 0.0, 0.0, 
"%.4fm"))
 
  178                ImGui::TableNextColumn();
 
  179                ImGui::SetNextItemWidth(columnWidth);
 
  180                if (ImGui::InputDouble(fmt::format(
"##InputY{}", rowIndex).c_str(), &devPosRow[1], 0.0, 0.0, 
"%.4fm"))
 
  184                ImGui::TableNextColumn();
 
  185                ImGui::SetNextItemWidth(columnWidth);
 
  186                if (ImGui::InputDouble(fmt::format(
"##InputZ{}", rowIndex).c_str(), &devPosRow[2], 0.0, 0.0, 
"%.4fm"))
 
  193                ImGui::TableNextColumn();
 
  194                ImGui::SetNextItemWidth(columnWidth);
 
  195                if (
ImGui::InputDoubleL(fmt::format(
"##InputLat{}", rowIndex).c_str(), &devPosRow[0], -180, 180, 0.0, 0.0, 
"%.8f°"))
 
  199                ImGui::TableNextColumn();
 
  200                ImGui::SetNextItemWidth(columnWidth);
 
  201                if (
ImGui::InputDoubleL(fmt::format(
"##InputLon{}", rowIndex).c_str(), &devPosRow[1], -180, 180, 0.0, 0.0, 
"%.8f°"))
 
  205                ImGui::TableNextColumn();
 
  206                ImGui::SetNextItemWidth(columnWidth);
 
  207                if (ImGui::InputDouble(fmt::format(
"##InputHeight{}", rowIndex).c_str(), &devPosRow[2], 0.0, 0.0, 
"%.4fm"))
 
  212            ImGui::TableNextColumn();
 
  213            ImGui::SetNextItemWidth(columnWidth);
 
  214            if (ImGui::InputDouble(fmt::format(
"##InputBias{}", rowIndex).c_str(), &
_deviceBias.at(rowIndex), 0.0, 0.0, 
"%.4fm"))
 
  218            ImGui::TableNextColumn();
 
  219            ImGui::SetNextItemWidth(columnWidth);
 
  220            if (ImGui::InputDouble(fmt::format(
"##InputScale{}", rowIndex).c_str(), &
_deviceScale.at(rowIndex), 0.0, 0.0, 
"%.4f"))
 
  227    if (ImGui::Button(fmt::format(
"Add Device##{}", 
size_t(
id)).c_str(), ImVec2(columnWidth * 2.1F, 0)))
 
  237    if (ImGui::Button(fmt::format(
"Delete Device##{}", 
size_t(
id)).c_str(), ImVec2(columnWidth * 2.1F, 0)))
 
  251        ImGui::Combo(fmt::format(
"Solution##{}", 
size_t(
id)).c_str(), &solutionMode, 
"Least squares\0Kalman Filter\0\0"))
 
  271        ImGui::SetNextItemOpen(
true, ImGuiCond_FirstUseEver);
 
  272        if (ImGui::TreeNode(fmt::format(
"x0 - Initial State##{}", 
size_t(
id)).c_str()))
 
  275            llaPos.block<2, 1>(0, 0) = 
rad2deg(llaPos.block<2, 1>(0, 0));
 
  277            ImGui::SetNextItemWidth(configWidth);
 
  281                                    _initialState.e_position.data(), 
"%.4f", ImGuiInputTextFlags_CharsScientific))
 
  287            ImGui::SetNextItemWidth(configWidth);
 
  291                                    llaPos.data(), 
"%.8f", ImGuiInputTextFlags_CharsScientific))
 
  293                llaPos.block<2, 1>(0, 0) = 
deg2rad(llaPos.block<2, 1>(0, 0));
 
  301                ImGui::SetNextItemWidth(configWidth);
 
  302                if (ImGui::InputDouble(fmt::format(
"Bias (m)##{}", 
"m",
 
  305                                       &
_initialState.bias, 0, 0, 
"%.3e", ImGuiInputTextFlags_CharsScientific))
 
  324        ImGui::SetNextItemOpen(
true, ImGuiCond_FirstUseEver);
 
  325        if (ImGui::TreeNode(fmt::format(
"R - Measurement Noise ##{}", 
size_t(
id)).c_str()))
 
  330                                                                  : 
"Standard deviation σ",
 
  335                                                  0, 0, 
"%.3e", ImGuiInputTextFlags_CharsScientific))
 
  347        ImGui::SetNextItemOpen(
true, ImGuiCond_FirstUseEver);
 
  348        if (ImGui::TreeNode(fmt::format(
"Q - Process Noise ##{}", 
size_t(
id)).c_str()))
 
  353                                                                  : 
"Standard deviation σ",
 
  358                                                  0, 0, 
"%.3e", ImGuiInputTextFlags_CharsScientific))
 
  370        ImGui::SetNextItemOpen(
true, ImGuiCond_FirstUseEver);
 
  371        if (ImGui::TreeNode(fmt::format(
"x0 - Initial State##{}", 
size_t(
id)).c_str()))
 
  374            llaPos.block<2, 1>(0, 0) = 
rad2deg(llaPos.block<2, 1>(0, 0));
 
  376            ImGui::SetNextItemWidth(configWidth);
 
  380                                    _initialState.e_position.data(), 
"%.4f", ImGuiInputTextFlags_CharsScientific))
 
  386            ImGui::SetNextItemWidth(configWidth);
 
  390                                    llaPos.data(), 
"%.8f°", ImGuiInputTextFlags_CharsScientific))
 
  392                llaPos.block<2, 1>(0, 0) = 
deg2rad(llaPos.block<2, 1>(0, 0));
 
  398            ImGui::SetNextItemWidth(configWidth);
 
  402                                    _state.e_velocity.data(), 
"%.3e", ImGuiInputTextFlags_CharsScientific))
 
  410                ImGui::SetNextItemWidth(configWidth);
 
  411                if (ImGui::InputDouble(fmt::format(
"Bias (m)##{}", 
"m",
 
  414                                       &
_state.bias, 0, 0, 
"%.3e", ImGuiInputTextFlags_CharsScientific))
 
  428        ImGui::SetNextItemOpen(
true, ImGuiCond_FirstUseEver);
 
  429        if (ImGui::TreeNode(fmt::format(
"P - Error covariance matrix (init)##{}", 
size_t(
id)).c_str()))
 
  434                                                                   : 
"Standard deviation σ",
 
  439                                                   "%.2e", ImGuiInputTextFlags_CharsScientific))
 
  449                                                                   : 
"Standard deviation σ",
 
  454                                                   "%.2e", ImGuiInputTextFlags_CharsScientific))
 
  466                                                                      : 
"Standard deviation σ",
 
  471                                                      0, 0, 
"%.2e", ImGuiInputTextFlags_CharsScientific))
 
  486    if (ImGui::Checkbox(fmt::format(
"Estimate Bias##{}", 
size_t(
id)).c_str(), &
_estimateBias))
 
  503    if (ImGui::Checkbox(fmt::format(
"Weighted Solution##{}", 
size_t(
id)).c_str(), &
_weightedSolution))
 
  521        if (ImGui::Checkbox(fmt::format(
"Use Initial Values##{}", 
size_t(
id)).c_str(), &
_useInitialValues))
 
 
  555    j[
"e_position"] = 
_state.e_position;
 
  556    j[
"e_velocity"] = 
_state.e_velocity;
 
 
  579    if (j.contains(
"nWifiInputPins"))
 
  584    if (j.contains(
"numStates"))
 
  588    if (j.contains(
"numMeasurements"))
 
  592    if (j.contains(
"frame"))
 
  594        j.at(
"frame").get_to(
_frame);
 
  596    if (j.contains(
"estimateBias"))
 
  600    if (j.contains(
"weightedSolution"))
 
  604    if (j.contains(
"useInitialValues"))
 
  608    if (j.contains(
"deviceMacAddresses"))
 
  612    if (j.contains(
"devicePositions"))
 
  616    if (j.contains(
"deviceBias"))
 
  620    if (j.contains(
"deviceScale"))
 
  624    if (j.contains(
"numOfDevices"))
 
  628    if (j.contains(
"solutionMode"))
 
  632    if (j.contains(
"e_position"))
 
  634        j.at(
"e_position").get_to(
_state.e_position);
 
  636    if (j.contains(
"e_velocity"))
 
  638        j.at(
"e_velocity").get_to(
_state.e_velocity);
 
  640    if (j.contains(
"bias"))
 
  642        j.at(
"bias").get_to(
_state.bias);
 
  644    if (j.contains(
"intialStatePosition"))
 
  646        j.at(
"intialStatePosition").get_to(
_initialState.e_position);
 
  648    if (j.contains(
"initialStateVelocity"))
 
  650        j.at(
"initialStateVelocity").get_to(
_initialState.e_velocity);
 
  652    if (j.contains(
"initialStateBias"))
 
  656    if (j.contains(
"initCovariancePosition"))
 
  660    if (j.contains(
"initCovariancePositionUnit"))
 
  664    if (j.contains(
"initCovarianceVelocity"))
 
  668    if (j.contains(
"initCovarianceVelocityUnit"))
 
  672    if (j.contains(
"initCovarianceBias"))
 
  676    if (j.contains(
"initCovarianceBiasUnit"))
 
  680    if (j.contains(
"measurementNoise"))
 
  684    if (j.contains(
"measurementNoiseUnit"))
 
  688    if (j.contains(
"processNoise"))
 
  692    if (j.contains(
"processNoiseUnit"))
 
 
  713    Eigen::Vector3d variance_vel = Eigen::Vector3d::Zero();
 
  724    Eigen::Vector3d variance_pos = Eigen::Vector3d::Zero();
 
  735    double variance_bias = 0.0;
 
  746        _kalmanFilter.P.diagonal() << variance_pos, variance_vel, variance_bias;
 
  769    LOG_DEBUG(
"WiFiPositioning initialized");
 
 
  793    auto obs = std::static_pointer_cast<const WiFiObs>(queue.
extract_front());
 
  801        bool deviceExists = 
false;
 
  810                    device.distanceStd = obs->distanceStd * 
_deviceScale.at(index);
 
  811                    device.time = obs->insTime;
 
  818                ecefPos.block<2, 1>(0, 0) = 
deg2rad(ecefPos.block<2, 1>(0, 0));
 
  820                if (device.position == ecefPos)
 
  824                    device.distanceStd = obs->distanceStd * 
_deviceScale.at(index);
 
  825                    device.time = obs->insTime;
 
  837                llaPos.block<2, 1>(0, 0) = 
deg2rad(llaPos.block<2, 1>(0, 0));
 
  847        auto wifiPositioningSolution = std::make_shared<NAV::WiFiPositioningSolution>();
 
  848        wifiPositioningSolution->insTime = obs->insTime;
 
  855                wifiPositioningSolution->setPositionAndCov_e(
lsqSolution.solution.block<3, 1>(0, 0),
 
  857                if (wifiPositioningSolution->lla_position().hasNaN())
 
  859                    LOG_WARN(
"{}: WiFi LSQ calculation failed, invalid position", 
nameId());
 
  864                    wifiPositioningSolution->bias = 
_state.bias;
 
  865                    wifiPositioningSolution->biasStdev = 
lsqSolution.variance(3, 3);
 
  874            wifiPositioningSolution->setPosVelAndCov_e(
_kalmanFilter.x.block<3, 1>(0, 0),
 
  877            if (wifiPositioningSolution->lla_position().hasNaN())
 
  879                LOG_WARN(
"{}: WiFi KF calculation failed, invalid position", 
nameId());
 
  890        LOG_DATA(
"{}: [{}] Received distance to device {} at position {} with distance {}", 
nameId(), obs->insTime.toYMDHMS(
GPST),
 
 
  909    Eigen::MatrixXd e_H = Eigen::MatrixXd::Zero(
static_cast<int>(
_devices.size()), n);
 
  910    Eigen::MatrixXd W = Eigen::MatrixXd::Identity(
static_cast<int>(
_devices.size()), 
static_cast<int>(
_devices.size()));
 
  911    Eigen::VectorXd ddist = Eigen::VectorXd::Zero(
static_cast<int>(
_devices.size()));
 
  912    size_t numMeasurements = 
_devices.size();
 
  925    for (
size_t o = 0; o < 15; o++)
 
  928        for (
size_t i = 0; i < numMeasurements; i++)
 
  931            double distEst = (
_devices.at(i).position - 
_state.e_position).norm();
 
  938            ddist(
static_cast<int>(i)) = 
_devices.at(i).distance - distEst;
 
  940            Eigen::Vector3d e_lineOfSightUnitVector = Eigen::Vector3d::Zero();
 
  947            e_H.block<1, 3>(
static_cast<int>(i), 0) = -e_lineOfSightUnitVector;
 
  950                e_H(
static_cast<int>(i), 3) = 1;
 
  956                W(
static_cast<int>(i), 
static_cast<int>(i)) = 1 / std::pow(
_devices.at(i).distanceStd, 2);
 
  982        bool solInaccurate = pow(lsq.
solution.norm(), 2) > 1e-3;
 
  991            lsq.
solution.setConstant(std::numeric_limits<double>::quiet_NaN());
 
  992            lsq.
variance.setConstant(std::numeric_limits<double>::quiet_NaN());
 
  995                _state.bias = std::numeric_limits<double>::quiet_NaN();
 
 
 1032        _kalmanFilter.Q.block(0, 0, 3, 3) = std::pow(tau_i, 3) / 3.0 * Eigen::Matrix3d::Identity();
 
 1033        _kalmanFilter.Q.block(3, 0, 3, 3) = std::pow(tau_i, 2) / 2.0 * Eigen::Matrix3d::Identity();
 
 1034        _kalmanFilter.Q.block(0, 3, 3, 3) = std::pow(tau_i, 2) / 2.0 * Eigen::Matrix3d::Identity();
 
 1035        _kalmanFilter.Q.block(3, 3, 3, 3) = tau_i * Eigen::Matrix3d::Identity();
 
 1069    Eigen::MatrixXd H = Eigen::MatrixXd::Zero(1, 
_numStates);
 
 
Transformation collection.
nlohmann::json json
json namespace
Text Help Marker (?) with Tooltip.
Utility class for logging to console and file.
#define LOG_DEBUG
Debug information. Should not be called on functions which receive observations (spamming)
#define LOG_DATA
All output which occurs repeatedly every time observations are received.
#define LOG_WARN
Error occurred, but a fallback option exists and program continues to work normally.
#define LOG_TRACE
Detailled info to trace the execution of the program. Should not be called on functions which receive...
Vector Utility functions.
Espressif Observation Class.
WiFi Positioning Algorithm output.
Generalized Kalman Filter class.
ImVec2 _guiConfigDefaultWindowSize
Node(std::string name)
Constructor.
std::vector< InputPin > inputPins
List of input pins.
std::string nameId() const
Node name and id.
std::string name
Name of the Node.
void invokeCallbacks(size_t portIndex, const std::shared_ptr< const NodeData > &data)
Calls all registered callbacks on the specified output port.
bool _hasConfig
Flag if the config window should be shown.
auto extract_front()
Returns a copy of the first element in the container and removes it from the container.
static std::string type()
Returns the type of the data class.
static std::string type()
Returns the type of the data class.
std::vector< double > _deviceBias
Input of biases.
uint8_t _numStates
Number of states.
double _measurementNoise
GUI selection of the process noise (standard deviation σ or Variance σ²)
KalmanFilter _kalmanFilter
Kalman Filter representation - States: 3xVel, 3xPos, (1xBias) - Measurements: 1xDist.
std::vector< Device > _devices
Devices which are used for the positioning.
Frame _frame
Frame to calculate the position in.
size_t _nWifiInputPins
Amount of wifi input pins.
Eigen::Vector3d _initCovarianceVelocity
GUI selection of the initial covariance diagonal values for velocity (standard deviation σ or Varianc...
std::string type() const override
String representation of the Class Type.
void restore(const json &j) override
Restores the node from a json object.
uint8_t _numMeasurements
Number of measurements.
@ meter2
Variance NED [m^2, m^2, m^2].
@ meter
Standard deviation NED [m, m, m].
json save() const override
Saves the node into a json object.
@ LLA
Latitude-Longitude-Altitude frame.
@ ECEF
Earth-Centered Earth-Fixed frame.
@ m_s
Standard deviation [m/s].
@ m2_s2
Variance [m^2/s^2].
Eigen::Vector3d _initCovariancePosition
GUI selection of the initial covariance diagonal values for position (standard deviation σ or Varianc...
WiFiPositioning()
Default constructor.
void guiConfig() override
ImGui config window which is shown on double click.
@ meter2
Variance NED [m^2, m^2, m^2].
@ meter
Standard deviation NED [m, m, m].
std::vector< double > _deviceScale
Input of scales.
LeastSquaresResult< Eigen::VectorXd, Eigen::MatrixXd > lsqSolution()
Calculate the position using the least squares method.
std::vector< std::string > _deviceMacAddresses
Input of mac addresses.
InitCovarianceVelocityUnit _initCovarianceVelocityUnit
Gui selection for the Unit of the initial covariance for the velocity.
bool _estimateBias
Selection of whether the bias will be additionally estimated.
void deinitialize() override
Deinitialize the node.
double _processNoise
GUI selection of the process noise (standard deviation σ or Variance σ²)
void updateNumberOfInputPins()
Adds/Deletes Input Pins depending on the variable _nNavInfoPins.
bool initialize() override
Initialize the node.
SolutionMode _solutionMode
Solution Mode.
InsTime _lastPredictTime
Time when the last prediction was triggered.
@ meter
Standard deviation [m].
~WiFiPositioning() override
Destructor.
bool _useInitialValues
Selection of whether the initial values should always be used or those of the last position.
double _initCovarianceBias
GUI selection of the initial covariance diagonal values for bias (standard deviation σ or Variance σ²...
InitCovariancePositionUnit _initCovariancePositionUnit
Gui selection for the Unit of the initial covariance for the position.
static std::string category()
String representation of the Class Category.
State _initialState
Initial state.
static std::string typeStatic()
String representation of the Class Type.
bool _weightedSolution
Selection of whether the solution will be weighted.
@ meter2
Variance NED [m^2, m^2, m^2].
@ meter
Standard deviation NED [m, m, m].
void kfSolution()
Calculate the position.
ProcessNoiseUnit _processNoiseUnit
Gui selection for the Unit of the initial covariance for the position.
InitCovarianceBiasUnit _initCovarianceBiasUnit
Gui selection for the Unit of the initial covariance for the bias.
std::vector< Eigen::Vector3d > _devicePositions
Input of positions.
void recvWiFiObs(InputPin::NodeDataQueue &queue, size_t pinIdx)
Receive Function for the WiFi Observations.
State _state
State estimated by the algorithm.
static constexpr size_t OUTPUT_PORT_INDEX_WIFISOL
WiFiPositioningSolution.
MeasurementNoiseUnit _measurementNoiseUnit
Gui selection for the Unit of the initial covariance for the position.
size_t _numOfDevices
Number of devices.
static float windowFontRatio()
Ratio to multiply for GUI window elements.
bool InputDouble3(const char *label, double v[3], const char *format, ImGuiInputTextFlags flags)
Shows an InputText GUI element for an array of 'double[3]'.
bool InputDoubleL(const char *label, double *v, double v_min, double v_max, double step, double step_fast, const char *format, ImGuiInputTextFlags flags)
Shows a value limited InputText GUI element for 'double'.
OutputPin * CreateOutputPin(Node *node, const char *name, Pin::Type pinType, const std::vector< std::string > &dataIdentifier, OutputPin::PinData data=static_cast< void * >(nullptr), int idx=-1)
Create an Output Pin object.
bool DeleteInputPin(InputPin &pin)
Deletes the input pin. Invalidates the pin reference given.
InputPin * CreateInputPin(Node *node, const char *name, Pin::Type pinType, const std::vector< std::string > &dataIdentifier={}, InputPin::Callback callback=static_cast< InputPin::FlowFirableCallbackFunc >(nullptr), InputPin::FlowFirableCheckFunc firable=nullptr, int priority=0, int idx=-1)
Create an Input Pin object.
void ApplyChanges()
Signals that there have been changes to the flow.
Eigen::Vector3< typename Derived::Scalar > ecef2lla_WGS84(const Eigen::MatrixBase< Derived > &e_position)
Converts Earth-centered-Earth-fixed coordinates into latitude, longitude and altitude using WGS84.
Eigen::Vector3< typename Derived::Scalar > lla2ecef_WGS84(const Eigen::MatrixBase< Derived > &lla_position)
Converts latitude, longitude and altitude into Earth-centered-Earth-fixed coordinates using WGS84.
Derived::PlainObject transitionMatrix_Phi_Taylor(const Eigen::MatrixBase< Derived > &F, double tau_s, size_t order)
Calculates the state transition matrix 𝚽 limited to specified order in 𝐅𝜏ₛ
KeyedLeastSquaresResult< Scalar, StateKeyType > solveWeightedLinearLeastSquaresUncertainties(const KeyedMatrixX< Scalar, MeasKeyType, StateKeyType > &H, const KeyedMatrixX< Scalar, MeasKeyType, MeasKeyType > &W, const KeyedVectorX< Scalar, MeasKeyType > &dz)
Finds the "weighted least squares" solution.
constexpr auto deg2rad(const T °)
Convert Degree to Radians.
constexpr auto rad2deg(const T &rad)
Convert Radians to Degree.
Eigen::Vector3< typename DerivedA::Scalar > e_calcLineOfSightUnitVector(const Eigen::MatrixBase< DerivedA > &e_posAnt, const Eigen::MatrixBase< DerivedB > &e_posSat)
Calculates the line-of-sight unit vector from the user antenna to the satellite.
Least Squares Uncertainties return value.
VarianceMatrix variance
Least squares variance.
SolutionVector solution
Least squares solution.