#ifndef WIFIPROBABILITYGRID_H #define WIFIPROBABILITYGRID_H #include #include "../../math/Distributions.h" #include "../../data/Timestamp.h" #include "WiFiGridNode.h" #include "WiFiProbability.h" #include /** * probability is calculated by comparing pre-calculated wifi-signal-strengths * attached to each grid-node with a given WiFiMeasurements data structure */ template class WiFiObserverGrid : public WiFiProbability { private: /** base-sigma to use for comparison */ float sigma = 8.0f; /** additional sigma-per-second (measurement age) to*/ float sigmaPerSecond = 3; std::unordered_set knownAPs; public: /** ctor with uncertainty */ WiFiObserverGrid(const float sigma) : sigma(sigma) { //StaticAssert::AinheritsB(); for (const AccessPoint& ap : Node::getMapAPs()) { knownAPs.insert(ap.getMAC()); } Assert::isFalse(knownAPs.empty(), "no APs known to the grid nodes?!"); } /** * get the given GridNode's probability. * compares the predicted signal-strengths stored on the given node * with the provided WiFi measurements */ double getProbability(const Node& n, const Timestamp curTime, const WiFiMeasurements& obs) const { // compile-time sanity check. Node must be a subclass off WiFiGridNode //StaticAssert::AinheritsB(); double prob = 1; int numMatchingAPs = 0; // after some runtime, check whether the wifi timestamps make sense // those must not be zero, otherwise something is wrong! if (!obs.entries.empty()) { Assert::isFalse(curTime.ms() > 10000 && obs.entries.front().getTimestamp().isZero(), "WiFiMeasurement timestamp is 0. this does not make sense..."); } // process each observed measurement for (const WiFiMeasurement& measurement : obs.entries) { // if an AP is not known to any of the nodes, just skip it if (knownAPs.find(measurement.getAP().getMAC()) == knownAPs.end()) { continue;} // determine the age for this measurement const Timestamp age = curTime - measurement.getTimestamp(); // sigma grows with measurement age float sigma = this->sigma + this->sigmaPerSecond * age.sec(); // the RSSI from the scan const float measuredRSSI = measurement.getRSSI(); // the RSSI from the model (if available!) float modelRSSI = n.getRSSI(measurement.getAP().getMAC()); // if no model RSSI is available, that means, // the AP in question is not / only barely visible at this location // assume a very low signal-strength and increase the sigma if (modelRSSI == 0) { modelRSSI = -100; sigma *= 2; } // compare both const double p = Distribution::Normal::getProbability(measuredRSSI, sigma, modelRSSI); //const double p = Distribution::Region::getProbability(measuredRSSI, sigma, modelRSSI); // adjust using log //prob += std::log(p); prob *= p; ++numMatchingAPs; } // sanity check // Assert::isTrue(numMatchingAPs > 0, "not a single measured AP was matched against known ones. coding error? model error?"); // if (numMatchingAPs == 0) {return 0;} // as not every node has the same number of visible/matching APs // we MUST return something like the average probability return prob; //return std::pow(prob, 1.0/3.0); } /** gnuplot debug dump */ void dump(Grid& grid, const Timestamp curTime, const WiFiMeasurements& obs, const std::string& fileName) { std::ofstream out(fileName); out << "splot '-' with points palette\n"; for (const Node& n : grid) { const float p = getProbability(n, curTime, obs); out << n.x_cm << " " << n.y_cm << " " << n.z_cm << " " << p << "\n"; } out << "e\n"; out.close(); } }; #endif // WIFIPROBABILITYGRID_H