owlps/owlps-positioner/src/autocalibration.cc

252 lines
8.0 KiB
C++

#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 ;
}
}