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.