owlps/owlps-positioner/src/measurement.cc

366 lines
9.5 KiB
C++

/*
* This file is part of the Owl Positioning System (OwlPS).
* OwlPS is a project of the University of Franche-Comte
* (Université de Franche-Comté), France.
*
* Copyright © Université de Franche-Comté 2007-2012.
*
* Corresponding author: Matteo Cypriani <mcy@lm7.fr>
*
***********************************************************************
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL:
* http://www.cecill.info
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided
* only with a limited warranty and the software's authors, the holder
* of the economic rights, and the successive licensors have only
* limited liability.
*
* In this respect, the user's attention is drawn to the risks
* associated with loading, using, modifying and/or developing or
* reproducing the software by the user in light of its specific status
* of free software, that may mean that it is complicated to manipulate,
* and that also therefore means that it is reserved for developers and
* experienced professionals having in-depth computer knowledge. Users
* are therefore encouraged to load and test the software's suitability
* as regards their requirements in conditions enabling the security of
* their systems and/or data to be ensured and, more generally, to use
* and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*
***********************************************************************
*/
#include "measurement.hh"
#include "posexcept.hh"
#include "configuration.hh"
#include <iostream>
#include <sstream>
using namespace std ;
/* *** Constructors *** */
/**
* Note that values pointed by #ap are not deleted.
*/
Measurement::~Measurement()
{
ss_list.clear() ;
}
/* *** Read accessors *** */
/**
* @returns The SS of the packet number \em pkt_id.
* @returns 0 if the packet ID does not exist in the SS list.
*/
ss_t Measurement::get_ss(pkt_id_t pkt_id) const
{
std::map<pkt_id_t, ss_t>::const_iterator 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<pkt_id_t, ss_t> packet(make_pair(packet_id, ss_dbm)) ;
if (! ss_list.insert(packet).second)
{
cerr
<< "Cannot insert the packet (#" << packet_id << ", "
<< static_cast<int_fast16_t>(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<pkt_id_t, ss_t> &_ss_list)
{
// We cannot use insert() here because we want to overwrite the
// previous values with the same ID, if any.
for (map<pkt_id_t, ss_t>::const_iterator
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 \em source to #ss_list. It
* is possible only if the #ap of the two Measurement are identical.
* @throw cannot_merge if the AP of the two Measurement are different.
*/
void Measurement::merge(const Measurement &source)
{
if (ap != source.ap)
throw cannot_merge(
"error when trying to merge measurements, APs are different") ;
add_ss_list(source.ss_list) ;
}
/**
* - #ap is not deleted, only initialised to NULL.
* - #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 ;
ap = NULL ;
}
/* *** 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() ;
float interval1 = nb_in_interval(source, 0.674 * std_dev) ;
float 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, float bound) const
{
unsigned int nb_values = 0 ;
for (map<pkt_id_t, ss_t>::const_iterator 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 (map<pkt_id_t, ss_t>::const_iterator
i = ss_list.begin() ; i != ss_list.end() ; ++i)
update_average(i->second) ;
}
void Measurement::update_average(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<float>(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 ;
ap = m.ap ;
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
ap == m.ap &&
ss_list == m.ss_list ;
}
const string Measurement::to_csv() const
{
ostringstream csv_line ;
if (ss_list.empty())
return "" ;
string mac_ap("") ;
if (ap)
mac_ap = ap->get_mac_addr() ;
for (map<pkt_id_t, ss_t>::const_iterator
i = ss_list.begin() ; i != ss_list.end() ; ++i)
{
if (i != ss_list.begin())
csv_line << ';' ;
csv_line
<< mac_ap << ';'
<< i->first << ';'
<< static_cast<int_fast16_t>(i->second) ;
}
return csv_line.str() ;
}
ostream &operator<<(ostream &os, const Measurement &m)
{
// MAC address
os
<< "AP: " << (m.ap ? m.ap->get_mac_addr() : "Unknown_AP")
<< ": " ;
// List of SS
if (m.ss_list.empty())
os << "No values" ;
else
for (map<pkt_id_t, ss_t>::const_iterator
i = m.ss_list.begin() ; i != m.ss_list.end() ; ++i)
{
if (i != m.ss_list.begin())
os << ';' ;
os << static_cast<int_fast16_t>(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 ;
}