[Positioner] Add class Autocalibration

The new class Autocalibration contains the code from the former huge
function Stock::generate_reference_point(). It is refactored a bit, but
still needs work. The function Stock::generate_reference_points() stays
in the Stock class for now.
This commit is contained in:
Matteo Cypriani 2012-04-27 17:41:11 +02:00
parent d479f9d080
commit 7a631e3962
5 changed files with 328 additions and 235 deletions

View File

@ -74,6 +74,7 @@ SOURCE_TARGET = $(SRC_DIR)/$(TARGET).cc
OBJ_LIST = \
posutil.o \
autocalibration.o \
stock.o \
timestamp.o \
direction.o \
@ -165,6 +166,10 @@ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cc $(SRC_DIR)/%.hh
$(STRIP) $@
# Dependencies
$(OBJ_DIR)/autocalibration.o: \
$(OBJ_DIR)/stock.o \
$(OBJ_DIR)/configuration.o \
$(OBJ_DIR)/posexcept.o
$(OBJ_DIR)/point3d.o: \
$(OBJ_DIR)/posutil.o \
$(OBJ_DIR)/posexcept.o

View File

@ -0,0 +1,251 @@
#include "autocalibration.hh"
#include "accesspoint.hh"
#include "stock.hh"
#include "configuration.hh"
#include "posexcept.hh"
#include <map>
#include <string>
using namespace std ;
using std::tr1::unordered_map ;
/* *** Static attribute definitions *** */
uint32_t Autocalibration::nb_virtual_mobiles = 0 ;
/**
* The reference point P we want to generate will end-up containing
* as many calibration requests as the total number of APs.
* For each of these requests, we consider a transmitter TRX (which is
* a virtual mobile located in P) and a receiver RX (the current AP).
* To generate the signal strength (SS) received by RX from TRX,
* we first select two reference APs REF1 and REF2. Then, we average
* the real measurements REF1->RX and REF2->RX, weighting in function
* of the angles REF-RX-TRX and the distance from RX to TRX to compute
* the SS.
* Hope this is clear enough
*/
void Autocalibration::generate_reference_point()
{
// If the point is the coordinates of an existing AP, we don't
// need to generate it
if (Stock::is_ap_coordinate(point))
return ;
/* Get/create the reference point */
const ReferencePoint
&current_rp = Stock::find_create_reference_point(point) ;
/* Create the timestamp */
Timestamp time_sent ;
time_sent.now() ;
/* Prepare the virtual mobile */
string vmob_mac(PosUtil::int_to_mac(nb_virtual_mobiles++)) ;
// The gain and trx power of the mobile will be the average of
// those of all the known APs (could be better, probably)
vmob_gain = 0 ;
vmob_pow = 0 ;
/* Generate a SS per AP */
for (unordered_map<string, AccessPoint>::const_iterator
rx = Stock::aps.begin() ; rx != Stock::aps.end() ; ++rx)
generate_ss() ;
/* Create the virtual mobile */
Mobile vmob("", vmob_mac, vmob_gain, vmob_pow) ;
const Mobile &mobile = Stock::find_create_mobile(vmob) ;
/* Create the calibration request */
CalibrationRequest cr(OWL_REQUEST_GENERATED) ;
cr.set_time_sent(time_sent) ;
cr.set_mobile(&mobile) ;
cr.set_measurements(measurements) ;
cr.set_reference_point(&current_rp) ;
Stock::store_calibration_request(cr) ;
}
void Autocalibration::generate_ss()
{
/* Update the mobile's attributes */
vmob_gain += rx->second.get_antenna_gain() / Stock::aps.size() ;
vmob_pow += rx->second.get_trx_power() / Stock::aps.size() ;
/* Skip the AP if it is not at the good level (floor) */
const Point3D &rx_coord = rx->second.get_coordinates() ;
if (rx_coord.get_z() != point.get_z())
return ;
/* Choose the 2 nearest APs in angle */
choose_reference_aps() ;
if (sorted_angles.size() < 2)
throw autocalibration_error("Not enough APs in coverage!") ;
/* Compute the angle weight of the 2 reference APs */
weight_aps() ;
/* Compute the SS that would be received from a mobile at a
* distance of RX-P, in the direction of each reference AP */
// Distance RX-P
float point_dst = rx_coord.distance(point) ;
// Constant term
double common_ss = rx->second.friis_constant_term() ;
const string &rx_mac = rx->second.get_mac_addr() ;
const ReferencePoint
&ref1_rp = Stock::get_reference_point(ref1->get_coordinates()),
&ref2_rp = Stock::get_reference_point(ref2->get_coordinates()) ;
if (Configuration::
bool_value("positioning.generate-single-packet-reference-points"))
{
/* Friis indexes for REF1 & REF2 */
float ref1_friis_idx = ref1_rp.friis_index_for_ap(rx_mac) ;
float ref2_friis_idx = ref2_rp.friis_index_for_ap(rx_mac) ;
/* SS for REF1 & REF2 */
double ref1_ss =
common_ss +
ref1->get_trx_power() +
ref1->get_antenna_gain() -
10 * ref1_friis_idx * log10(point_dst) ;
double ref2_ss =
common_ss +
ref2->get_trx_power() +
ref2->get_antenna_gain() -
10 * ref2_friis_idx * log10(point_dst) ;
/* Compute the consolidated SS */
ref1_ss = ref1_ss * ref1_percent / 100 ;
ref2_ss = ref2_ss * ref2_percent / 100 ;
double rx_ss = ref1_ss + ref2_ss ;
/* Create the measurement, add it to the list */
Measurement m(&rx->second) ;
m.add_ss(1, rx_ss) ;
measurements[rx_mac] = m ;
}
else
{
const vector<CalibrationRequest*> &ref1_cr =
ref1_rp.get_requests() ;
const Measurement *rx_measurement = NULL ;
vector<CalibrationRequest*>::const_iterator cr ;
// Search for the first measurement made by RX in ref1_rp
for (cr = ref1_cr.begin() ;
! rx_measurement && cr != ref1_cr.end() ; ++cr)
rx_measurement = (*cr)->get_measurement(rx_mac) ;
// The selected reference APs are in coverage of RX, therefore
// we should always find a measurement of RX in ref1_rp
assert(rx_measurement) ;
for (pkt_id_t pkt_id = 1 ; pkt_id < (*cr)->get_nb_packets() ;
++pkt_id)
{
/* Friis indexes for REF1 & REF2 */
float ref1_friis_idx =
ref1_rp.friis_index_for_ap(rx->second.get_mac_addr(),
pkt_id) ;
if (ref1_friis_idx == 0)
continue ; // The packet pkt_id does not exist in ref1_rp
float ref2_friis_idx =
ref2_rp.friis_index_for_ap(rx->second.get_mac_addr(),
pkt_id) ;
if (ref2_friis_idx == 0)
continue ; // The packet pkt_id does not exist in ref2_rp
/* SS for REF1 & REF2 */
double ref1_ss =
common_ss +
ref1->get_trx_power() +
ref1->get_antenna_gain() -
10 * ref1_friis_idx * log10(point_dst) ;
double ref2_ss =
common_ss +
ref2->get_trx_power() +
ref2->get_antenna_gain() -
10 * ref2_friis_idx * log10(point_dst) ;
/* Compute the consolidated SS */
ref1_ss = ref1_ss * ref1_percent / 100 ;
ref2_ss = ref2_ss * ref2_percent / 100 ;
double rx_ss = ref1_ss + ref2_ss ;
/* Create the measurement, add it to the list */
Measurement m(&rx->second) ;
m.add_ss(pkt_id, rx_ss) ;
measurements[rx_mac] = m ;
}
}
}
void Autocalibration::choose_reference_aps()
{
const Point3D &rx_coord = rx->second.get_coordinates() ;
for (unordered_map<string, AccessPoint>::const_iterator
ref = Stock::aps.begin() ; ref != Stock::aps.end() ; ++ref)
{
if (ref == rx)
continue ;
// Skip the AP if it is not at the same level (floor) than the
// receiver AP:
const Point3D &ref_coord = ref->second.get_coordinates() ;
if (ref_coord.get_z() != rx_coord.get_z())
continue ;
// Skip the AP if it is not in coverage with the receiver AP:
float coverage =
rx->second.received_calibration_from_ap(ref->second) ;
if (coverage < 1) // Less than 1% coverage is ridiculous!
continue ;
double angle = rx_coord.angle_2d(point, ref_coord) ;
double weight = angle / coverage ;
pair<double,
unordered_map<string, AccessPoint>::const_iterator>
angle_ap(angle, ref) ;
pair<double, pair<double,
unordered_map<string, AccessPoint>::const_iterator> >
weight_angle_ap(weight, angle_ap) ;
sorted_angles.insert(weight_angle_ap) ;
}
}
void Autocalibration::weight_aps()
{
map<double, pair<double,
unordered_map<string, AccessPoint>::const_iterator> >
::const_iterator s = sorted_angles.begin() ;
// Angle REF1-RX-P
double ref1_angle = s->second.first ;
ref1 = &s->second.second->second ;
++s ;
ref2 = &s->second.second->second ;
// Angle REF1-RX-REF2
const Point3D &rx_coord = rx->second.get_coordinates() ;
const Point3D &ref1_coord = ref1->get_coordinates() ;
const Point3D &ref2_coord = ref2->get_coordinates() ;
double reference_angle = rx_coord.angle_2d(ref1_coord, ref2_coord) ;
if (reference_angle == 0)
ref1_percent = ref2_percent = 50 ;
else
{
ref1_percent = ref1_angle * 100 / reference_angle ;
if (ref1_percent > 100)
ref1_percent = 100 ;
ref2_percent = 100 - ref1_percent ;
}
}

View File

@ -0,0 +1,61 @@
/*
* This file is part of the Owl Positioning System (OwlPS).
* OwlPS is a project of the University of Franche-Comté
* (Université de Franche-Comté), France.
*/
#ifndef _OWLPS_POSITIONING_AUTOCALIBRATION_HH_
#define _OWLPS_POSITIONING_AUTOCALIBRATION_HH_
class AccessPoint ;
class Point3D ;
class Measurement ;
#include <boost/tr1/unordered_map.hpp>
/**
* The class Autocalibration contains the code used to generate single
* reference points from the autocalibration measurements.
*/
class Autocalibration
{
private:
/// Current AP to generate a SS for
std::tr1::unordered_map<std::string, AccessPoint>::const_iterator rx ;
/// Selected transmitter APs
const AccessPoint *ref1, *ref2 ;
/// Weights of the selected transmitter APs
double ref1_percent, ref2_percent ;
/// Angles of the transmitter APs
std::multimap<double,
std::pair<double,
std::tr1::unordered_map<
std::string, AccessPoint>::const_iterator> >
sorted_angles ;
/// Characteristics of the virtual mobile
double vmob_gain, vmob_pow ;
/// Generated measurements' list
std::tr1::unordered_map<std::string, Measurement> measurements ;
protected:
/// Number of generated "virtual" mobiles
static uint32_t nb_virtual_mobiles ;
/// The coordinates of the reference point to generate
const Point3D &point ;
public:
Autocalibration(const Point3D &_point): point(_point) {}
/// Generates a single reference point
void generate_reference_point(void) ;
/// Generates the SSs for all the APs
void generate_ss(void) ;
/// Chooses the reference APs for a receiver AP
void choose_reference_aps(void) ;
/// Weihgts the selected APs according to their angles
void weight_aps(void) ;
} ;
#endif // _OWLPS_POSITIONING_AUTOCALIBRATION_HH_

View File

@ -26,8 +26,6 @@ unordered_map<Point3D, Waypoint> Stock::waypoints ;
unordered_map<string, Mobile> Stock::mobiles ;
uint32_t Stock::nb_virtual_mobiles = 0 ;
unordered_map<string, AccessPoint> Stock::aps ;
unordered_set<ReferencePoint,
@ -464,233 +462,17 @@ void Stock::regenerate_reference_points()
for (float z = start.get_z() ; z <= stop.get_z() ; z += step_z)
{
Point3D current_point(x,y,z) ;
generate_reference_point(current_point) ;
}
}
/**
* The reference point P we want to generate will end-up containing
* as many calibration requests as the total number of APs.
* For each of these requests, we consider a transmitter TRX (which is
* a virtual mobile located in P) and a receiver RX (the current AP).
* To generate the signal strength (SS) received by RX from TRX,
* we first select two reference APs REF1 and REF2. Then, we average
* the real measurements REF1->RX and REF2->RX, weighting in function
* of the angles REF-RX-TRX and the distance from RX to TRX to compute
* the SS.
* Hope this is clear enough
*/
void Stock::generate_reference_point(const Point3D &point)
{
// If the point is the coordinates of an existing AP, we don't
// need to generate it
if (is_ap_coordinate(point))
return ;
/* Get/create the reference point */
const ReferencePoint &current_rp = find_create_reference_point(point) ;
/* Create the timestamp */
Timestamp time_sent ;
time_sent.now() ;
/* Create the measurement list */
unordered_map<string, Measurement> measurements ;
/* Prepare the virtual mobile */
string vmob_mac(PosUtil::int_to_mac(nb_virtual_mobiles++)) ;
// The gain and trx power of the mobile will be the average of
// those of all the known APs (could be better, probably)
double vmob_gain = 0 ;
double vmob_pow = 0 ;
for (unordered_map<string, AccessPoint>::const_iterator
rx = aps.begin() ; rx != aps.end() ; ++rx)
{
/* Update the mobile's attributes */
vmob_gain += rx->second.get_antenna_gain() / aps.size() ;
vmob_pow += rx->second.get_trx_power() / aps.size() ;
/* Skip the AP if it is not at the good level (floor) */
const Point3D &rx_coord = rx->second.get_coordinates() ;
if (rx_coord.get_z() != point.get_z())
continue ;
/* Choose the 2 nearest APs in angle */
multimap<double, pair<double,
unordered_map<string, AccessPoint>::const_iterator> >
sorted_angles ;
for (unordered_map<string, AccessPoint>::const_iterator
ref = aps.begin() ; ref != aps.end() ; ++ref)
{
if (ref == rx)
continue ;
// Skip the AP if it is not at the same level (floor) than the
// receiver AP:
const Point3D &ref_coord = ref->second.get_coordinates() ;
if (ref_coord.get_z() != rx_coord.get_z())
continue ;
// Skip the AP if it is not in coverage with the receiver AP:
float coverage =
rx->second.received_calibration_from_ap(ref->second) ;
if (coverage < 1) // Less than 1% coverage is ridiculous!
continue ;
double angle = rx_coord.angle_2d(point, ref_coord) ;
double weight = angle / coverage ;
pair<double,
unordered_map<string, AccessPoint>::const_iterator>
angle_ap(angle, ref) ;
pair<double, pair<double,
unordered_map<string, AccessPoint>::const_iterator> >
weight_angle_ap(weight, angle_ap) ;
sorted_angles.insert(weight_angle_ap) ;
}
if (sorted_angles.size() < 2)
{
if (Configuration::is_configured("verbose"))
cerr << "Can't generate reference point, not enough APs"
<< " in coverage!\n" ;
return ;
}
/* Compute the angle weight of the 2 reference APs */
map<double, pair<double,
unordered_map<string, AccessPoint>::const_iterator> >
::const_iterator s = sorted_angles.begin() ;
// Angle REF1-RX-P
double ref1_angle = s->second.first ;
const AccessPoint &ref1 = s->second.second->second ;
++s ;
const AccessPoint &ref2 = s->second.second->second ;
// Angle REF1-RX-REF2
const Point3D &ref1_coord = ref1.get_coordinates() ;
const Point3D &ref2_coord = ref2.get_coordinates() ;
double reference_angle = rx_coord.angle_2d(ref1_coord, ref2_coord) ;
double ref1_percent, ref2_percent ;
if (reference_angle == 0)
ref1_percent = ref2_percent = 50 ;
else
{
ref1_percent = ref1_angle * 100 / reference_angle ;
if (ref1_percent > 100)
ref1_percent = 100 ;
ref2_percent = 100 - ref1_percent ;
}
/* Compute the SS that would be received from a mobile at a
* distance of RX-P, in the direction of each reference AP */
// Distance RX-P
float point_dst = rx_coord.distance(point) ;
// Constant term
double common_ss = rx->second.friis_constant_term() ;
const string &rx_mac = rx->second.get_mac_addr() ;
const ReferencePoint &ref1_rp = get_reference_point(ref1_coord) ;
const ReferencePoint &ref2_rp = get_reference_point(ref2_coord) ;
if (Configuration::
bool_value("positioning.generate-single-packet-reference-points"))
{
/* Friis indexes for REF1 & REF2 */
float ref1_friis_idx = ref1_rp.friis_index_for_ap(rx_mac) ;
float ref2_friis_idx = ref2_rp.friis_index_for_ap(rx_mac) ;
/* SS for REF1 & REF2 */
double ref1_ss =
common_ss +
ref1.get_trx_power() +
ref1.get_antenna_gain() -
10 * ref1_friis_idx * log10(point_dst) ;
double ref2_ss =
common_ss +
ref2.get_trx_power() +
ref2.get_antenna_gain() -
10 * ref2_friis_idx * log10(point_dst) ;
/* Compute the consolidated SS */
ref1_ss = ref1_ss * ref1_percent / 100 ;
ref2_ss = ref2_ss * ref2_percent / 100 ;
double rx_ss = ref1_ss + ref2_ss ;
/* Create the measurement, add it to the list */
Measurement m(&rx->second) ;
m.add_ss(1, rx_ss) ;
measurements[rx_mac] = m ;
}
else
{
const vector<CalibrationRequest*> &ref1_cr =
ref1_rp.get_requests() ;
const Measurement *rx_measurement = NULL ;
vector<CalibrationRequest*>::const_iterator cr ;
// Search for the first measurement made by RX in ref1_rp
for (cr = ref1_cr.begin() ;
! rx_measurement && cr != ref1_cr.end() ; ++cr)
rx_measurement = (*cr)->get_measurement(rx_mac) ;
// The selected reference APs are in coverage of RX, therefore
// we should always find a measurement of RX in ref1_rp
assert(rx_measurement) ;
for (pkt_id_t pkt_id = 1 ; pkt_id < (*cr)->get_nb_packets() ;
++pkt_id)
Autocalibration ac(current_point) ;
try
{
/* Friis indexes for REF1 & REF2 */
float ref1_friis_idx =
ref1_rp.friis_index_for_ap(rx->second.get_mac_addr(),
pkt_id) ;
if (ref1_friis_idx == 0)
continue ; // The packet pkt_id does not exist in ref1_rp
float ref2_friis_idx =
ref2_rp.friis_index_for_ap(rx->second.get_mac_addr(),
pkt_id) ;
if (ref2_friis_idx == 0)
continue ; // The packet pkt_id does not exist in ref2_rp
/* SS for REF1 & REF2 */
double ref1_ss =
common_ss +
ref1.get_trx_power() +
ref1.get_antenna_gain() -
10 * ref1_friis_idx * log10(point_dst) ;
double ref2_ss =
common_ss +
ref2.get_trx_power() +
ref2.get_antenna_gain() -
10 * ref2_friis_idx * log10(point_dst) ;
/* Compute the consolidated SS */
ref1_ss = ref1_ss * ref1_percent / 100 ;
ref2_ss = ref2_ss * ref2_percent / 100 ;
double rx_ss = ref1_ss + ref2_ss ;
/* Create the measurement, add it to the list */
Measurement m(&rx->second) ;
m.add_ss(pkt_id, rx_ss) ;
measurements[rx_mac] = m ;
ac.generate_reference_point() ;
}
catch (autocalibration_error &e)
{
if (Configuration::is_configured("verbose"))
cerr << e.what() << '\n' ;
}
}
}
/* Create the virtual mobile */
Mobile vmob("", vmob_mac, vmob_gain, vmob_pow) ;
const Mobile &mobile = find_create_mobile(vmob) ;
/* Create the calibration request */
CalibrationRequest cr(OWL_REQUEST_GENERATED) ;
cr.set_time_sent(time_sent) ;
cr.set_mobile(&mobile) ;
cr.set_measurements(measurements) ;
cr.set_reference_point(&current_rp) ;
store_calibration_request(cr) ;
}

View File

@ -8,6 +8,7 @@
#ifndef _OWLPS_POSITIONING_STOCK_HH_
#define _OWLPS_POSITIONING_STOCK_HH_
#include "autocalibration.hh"
#include "building.hh"
#include "waypoint.hh"
#include "mobile.hh"
@ -21,6 +22,8 @@
/// Storage class
class Stock
{
friend class Autocalibration ;
private:
/// List of known Building
/** The string key of the map is the Building name. */
@ -33,9 +36,6 @@ private:
/** The string key of the map is the Mobile MAC address. */
static std::tr1::unordered_map<std::string, Mobile> mobiles ;
/// Number of generated "virtual" mobiles
static uint32_t nb_virtual_mobiles ;
/// List of known AccessPoint
/** The string key of the map is the AccessPoint MAC address. */
static std::tr1::unordered_map<std::string, AccessPoint> aps ;
@ -48,12 +48,6 @@ private:
/// List of known CalibrationRequest
static std::tr1::unordered_set<CalibrationRequest> calibration_requests ;
/** @name ReferencePoint operations */
//@{
/// Generates a single reference point
static void generate_reference_point(const Point3D &point) ;
//@}
/** @name CalibrationRequest operations */
//@{
/// Delete calibration requests that do not come from the APs