/* * This file is part of the Owl Positioning System (OwlPS) project. * It is subject to the copyright notice and license terms in the * COPYRIGHT.t2t file found in the top-level directory of this * distribution and at * https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t * No part of the OwlPS Project, including this file, may be copied, * modified, propagated, or distributed except according to the terms * contained in the COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be * distributed along with this file, either separately or by replacing * this notice by the COPYRIGHT.t2t file's contents. */ #include "measurement.hh" #include "posexcept.hh" #include "configuration.hh" #include #include using namespace std ; /* *** Constructors *** */ /** * Note that values pointed by #cp are not deleted. */ Measurement::~Measurement() { ss_list.clear() ; } /* *** Read accessors *** */ /** * @returns The SS of the packet number `pkt_id.` * @returns 0 if the packet ID does not exist in the SS list. */ ss_t Measurement::get_ss(const pkt_id_t pkt_id) const { auto ss = ss_list.find(pkt_id) ; if (ss == ss_list.end()) return 0 ; return ss->second ; } /* *** Write accessors *** */ /** * @param packet_id The sequence number of the packet in the request, * as sent by the mobile. * @param ss_dbm The signal strength to add to #ss_list (in dBm). */ void Measurement:: add_ss(const pkt_id_t packet_id, const ss_t ss_dbm) { // Add the new value (in dBm) along with the packet identifier: pair packet(make_pair(packet_id, ss_dbm)) ; if (! ss_list.insert(packet).second) { cerr << "Cannot insert the packet (#" << packet_id << ", " << static_cast(ss_dbm) << " dBm)\n" ; return ; } // Update the average & variance with the new value: update_average(ss_dbm) ; } /** * Add an SS list to the current Measurement's SS list. * If the new SS list contains a packet with the same ID, the old one * is overwritten. */ void Measurement:: add_ss_list(const map &_ss_list) { // We cannot use insert() here because we want to overwrite the // previous values with the same ID, if any. for (auto i = _ss_list.begin() ; i != _ss_list.end() ; ++i) ss_list[i->first] = i->second ; recalculate_average() ; } /** * Merge consists of adding the SS values of `source` to #ss_list. It * is possible only if the #cp of the two Measurement are identical. * Since add_ss_list() is used, if `source` contains packets with IDs * that already exist, the old ones will be overwritten. * * @throw cannot_merge if the CP of the two Measurement are different. */ void Measurement::merge(const Measurement &source) { if (cp != source.cp) throw cannot_merge( "error when trying to merge measurements, CPs are different") ; add_ss_list(source.ss_list) ; } /** * - #cp is not deleted, only initialised to nullptr. * - #ss_list is cleared. * - average and variance variables are reset to 0. */ void Measurement::clear() { ss_list.clear() ; average_dbm = 0 ; average_mw = 0 ; variance_mw = 0 ; variance_dbm = 0 ; variance_mw_m2 = 0 ; variance_dbm_m2 = 0 ; variance_size = 0 ; cp = nullptr ; } /* *** Operations *** */ /** * @returns The similarity score. A small number means an important * similarity, a big number means an important dissimilarity. The * scale of this score depends on the similarity method used (option * positioning.ss-similarity); the similarity scores are therefore * not comparable across the various methods. */ float Measurement::similarity(const Measurement &source) const { assert(! ss_list.empty()) ; assert(! source.ss_list.empty()) ; string algorithm( Configuration::string_value("positioning.ss-similarity")) ; if (algorithm == "mean") return ss_square_distance(source) ; if (algorithm == "interval") { unsigned int nb = nb_in_interval(source, get_std_deviation_mw()) ; if (nb != 0) return 1 / nb ; return 2 ; } /* Note: this score ranges from 0 (excluded) to 1 if at least one * packet was found within the interval. It is equals to 2 if no * packet was found in the interval. */ if (algorithm == "interval2") { float std_dev = get_std_deviation_mw() ; unsigned int interval1 = nb_in_interval(source, 0.674 * std_dev) ; unsigned int interval2 = nb_in_interval(source, std_dev) ; /* Explanation: with a normal distribution, we normally have: * - 68% of the values within the interval * [mean-std.dev.;mean+std.dev.], and * - 50% of the value within the interval * [mean-.674*std.dev.;mean+.674*std.dev.] */ float percent1 = interval1 * 100 / source.get_nb_ss() ; float percent2 = interval2 * 100 / source.get_nb_ss() ; return (percent1 - 50) * (percent1 - 50) + (percent2 - 68) * (percent2 - 68) ; /* Note: this distance (score) ranges from 0 (with * percent1==50 and percent2==68) to 7124 (with percent1 * and percent2==0). */ } throw bad_configuration( "Bad SS similarity algorithm name \""+ algorithm +"\"!") ; } unsigned int Measurement:: nb_in_interval(const Measurement &source, const float bound) const { unsigned int nb_values = 0 ; for (auto ss = source.ss_list.begin() ; ss != source.ss_list.end() ; ++ss) if (PosUtil::is_in_interval(average_dbm, bound, ss->second)) ++nb_values ; return nb_values ; } void Measurement::recalculate_average() { average_dbm = 0 ; average_mw = 0 ; variance_mw = 0 ; variance_dbm = 0 ; variance_mw_m2 = 0 ; variance_dbm_m2 = 0 ; variance_size = 0 ; for (auto i = ss_list.begin() ; i != ss_list.end() ; ++i) update_average(i->second) ; } void Measurement::update_average(const ss_t ss_dbm) { ++variance_size ; assert(variance_size <= ss_list.size()) ; // Convert the new SS in mW: float ss_mw = pow(10, static_cast(ss_dbm) / 10.0) ; // Update the average: float delta_mw = ss_mw - average_mw ; average_mw += delta_mw / variance_size ; average_dbm = 10.0 * log10(average_mw) ; float delta_dbm = ss_dbm - average_dbm ; // Update the variance: variance_mw_m2 += delta_mw * (ss_mw - average_mw) ; variance_dbm_m2 += delta_dbm * (ss_dbm - average_dbm) ; if (variance_size > 1) { variance_mw = variance_mw_m2 / (variance_size - 1) ; variance_dbm = variance_dbm_m2 / (variance_size - 1) ; } } /* *** Operators *** */ Measurement& Measurement::operator=(const Measurement &m) { if (this == &m) return *this ; cp = m.cp ; ss_list = m.ss_list ; average_dbm = m.average_dbm ; average_mw = m.average_mw ; variance_mw = m.variance_mw ; variance_dbm = m.variance_mw ; variance_mw_m2 = m.variance_mw_m2 ; variance_dbm_m2 = m.variance_mw_m2 ; variance_size = m.variance_size ; return *this ; } bool Measurement::operator==(const Measurement &m) const { if (this == &m) return true ; return cp == m.cp && ss_list == m.ss_list ; } const string Measurement::to_csv() const { ostringstream csv_line ; if (ss_list.empty()) return "" ; string mac_cp("") ; if (cp) mac_cp = cp->get_mac_addr() ; for (auto i = ss_list.begin() ; i != ss_list.end() ; ++i) { if (i != ss_list.begin()) csv_line << ';' ; csv_line << mac_cp << ';' << i->first << ';' << static_cast(i->second) ; } return csv_line.str() ; } ostream &operator<<(ostream &os, const Measurement &m) { // MAC address os << "CP: " << (m.cp ? m.cp->get_mac_addr() : "Unknown_CP") << ": " ; // List of SS if (m.ss_list.empty()) os << "No values" ; else for (auto i = m.ss_list.begin() ; i != m.ss_list.end() ; ++i) { if (i != m.ss_list.begin()) os << ';' ; os << static_cast(i->second) << '(' << i->first << ')' ; } os << " [AVG_dBm=" << m.average_dbm << ";VAR_dBm=" << m.variance_dbm << ";STD_dBm=" << m.get_std_deviation_dbm() << ";AVG_mW=" << m.average_mw << ";VAR_mW=" << m.variance_mw << ";STD_mW=" << m.get_std_deviation_mw() << "]" ; return os ; }