owlps/owlps-positioner/src/autocalibration.cc

316 lines
9.2 KiB
C++
Raw Normal View History

#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 ;
/* *** Operations *** */
/**
* 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 the SS(s) for each 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 RX AP if it is not at the same floor as the point
* to generate */
const Point3D &rx_coord = rx->second.get_coordinates() ;
if (rx_coord.get_z() != point.get_z())
return ;
/* Choose the nearest AP(s) in angle */
sort_reference_aps() ;
// We need at least one reference AP:
if (sorted_negative_angles.empty() && sorted_positive_angles.empty())
throw autocalibration_error("Not enough APs in coverage!") ;
/* Compute the angle weight of the 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 */
compute_ss() ;
}
/**
* The reference (transmitter) APs are first filtered (floor, coverage),
* then sorted according to their angles with RX and P.
*/
void Autocalibration::sort_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 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 ;
Point3D ref_coord_r(ref_coord) ;
ref_coord_r.rotate_2d(rx_coord, angle_p) ;
double angle = rx_coord.angle_2d(point, ref_coord_r) ;
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) ;
if (ref_coord_r.get_y() < rx_coord.get_y())
sorted_negative_angles.insert(weight_angle_ap) ;
else
sorted_positive_angles.insert(weight_angle_ap) ;
}
}
void Autocalibration::weight_aps()
{
/* Retrieve the reference APs & angles */
map<double, pair<double,
unordered_map<string, AccessPoint>::const_iterator> >
::const_iterator s ;
s = sorted_negative_angles.begin() ;
if (s != sorted_negative_angles.end())
init_ap(s) ;
s = sorted_positive_angles.begin() ;
if (s != sorted_positive_angles.end())
init_ap(s) ;
/* Weight the APs */
if (ref_aps.size() == 1)
ref_aps[0].weight = 100 ;
else if (ref_aps.size() == 2)
weight_2_aps() ;
else
throw autocalibration_error(
"We should not be here... error when sorting the reference APs?") ;
}
void Autocalibration::init_ap(
map<double, pair<double, unordered_map<
string, AccessPoint>::const_iterator> >::const_iterator s)
{
struct ap ref ;
ref.ap = &s->second.second->second ;
ref.angle = s->second.first ; // Angle REF-RX-P
ref_aps.push_back(ref) ;
}
void Autocalibration::weight_2_aps()
{
assert(ref_aps.size() > 1) ;
/* Compute the angle REF1-RX-REF2 */
const Point3D &rx_coord = rx->second.get_coordinates() ;
const Point3D &ref1_coord = ref_aps[0].ap->get_coordinates() ;
const Point3D &ref2_coord = ref_aps[1].ap->get_coordinates() ;
double reference_angle = rx_coord.angle_2d(ref1_coord, ref2_coord) ;
/* Weight the two APs */
if (reference_angle == 0)
ref_aps[0].weight = ref_aps[1].weight = 50 ;
else
{
ref_aps[0].weight = ref_aps[0].angle * 100 / reference_angle ;
if (ref_aps[0].weight > 100)
ref_aps[0].weight = 100 ;
ref_aps[1].weight = 100 - ref_aps[0].weight ;
}
}
void Autocalibration::compute_ss()
{
if (Configuration::
bool_value("positioning.generate-single-packet-reference-points"))
compute_single_packet_ss(0) ;
else
compute_multi_packet_ss() ;
}
void Autocalibration::compute_multi_packet_ss()
{
const ap &ref1 = ref_aps[0] ;
const ReferencePoint &ref1_rp =
Stock::get_reference_point(ref1.ap->get_coordinates()) ;
const vector<CalibrationRequest*> &ref1_cr =
ref1_rp.get_requests() ;
/* Search for the first measurement made by RX in ref1_rp */
const Measurement *rx_measurement = NULL ;
vector<CalibrationRequest*>::const_iterator cr ;
const string &rx_mac = rx->second.get_mac_addr() ;
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) ;
/* Generate the SS for each packet */
for (pkt_id_t pkt_id = 1 ; pkt_id <= (*cr)->get_nb_packets() ;
++pkt_id)
compute_single_packet_ss(pkt_id) ;
}
/**
* @arg pkt_id The number of the packet to create. Must be 0 if all
* the generated reference points contain only one packet.
*/
void Autocalibration::compute_single_packet_ss(pkt_id_t pkt_id)
{
/* Distance RX-P */
const Point3D &rx_coord = rx->second.get_coordinates() ;
float point_dst = rx_coord.distance(point) ;
/* Compute the consolidated SS */
double rx_ss = 0 ;
for (unsigned int i = 0 ; i < ref_aps.size() ; ++i)
{
double tmp_ss = compute_weighted_ss(i, pkt_id, point_dst) ;
if (tmp_ss > 0) // error
return ;
rx_ss += tmp_ss ;
}
/* Add the SS to the measurements' list */
const string &rx_mac = rx->second.get_mac_addr() ;
// 0 is not a valid packet ID, so we use 1 if we generate only one
// packet per reference point:
if (pkt_id == 0)
pkt_id = 1 ;
// The Measurement measurements[rx_mac] is created if needed:
measurements[rx_mac].add_ss(pkt_id, rx_ss) ;
// Useful only if the measurement was just created, but it is not
// worth a test:
measurements[rx_mac].set_ap(&rx->second) ;
}
/**
* @arg ap_idx The index of the AP in #ref_aps.
* @arg pkt_id The number of the packet to generate, or 0 if we generate
* only one packet per reference point.
* @arg point_dst The distance RX-P.
*
* @returns The weighted SS value in dBm, or 1 in case of error.
*/
double Autocalibration::compute_weighted_ss(
unsigned int ap_idx, pkt_id_t pkt_id, float point_dst)
{
const ap &ref = ref_aps[ap_idx] ;
const ReferencePoint &rp =
Stock::get_reference_point(ref.ap->get_coordinates()) ;
/* Friis index */
const string &rx_mac = rx->second.get_mac_addr() ;
float friis_idx ;
if (pkt_id == 0)
friis_idx = rp.friis_index_for_ap(rx_mac) ;
else
{
friis_idx = rp.friis_index_for_ap(rx_mac, pkt_id) ;
if (friis_idx == 0)
return 1 ; // The packet pkt_id does not exist in rp
}
/* SS */
// Constant term
double common_ss = rx->second.friis_constant_term() ;
double ss =
common_ss +
ref.ap->get_trx_power() +
ref.ap->get_antenna_gain() -
10 * friis_idx * log10(point_dst) ;
return ss * ref.weight / 100 ;
}