252 lines
8.0 KiB
C++
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
|
|
¤t_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(¤t_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 ;
|
|
}
|
|
}
|