diff --git a/owlps-positioner/Makefile b/owlps-positioner/Makefile index 36a0678..aabc48d 100644 --- a/owlps-positioner/Makefile +++ b/owlps-positioner/Makefile @@ -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 diff --git a/owlps-positioner/src/autocalibration.cc b/owlps-positioner/src/autocalibration.cc new file mode 100644 index 0000000..532e3c2 --- /dev/null +++ b/owlps-positioner/src/autocalibration.cc @@ -0,0 +1,251 @@ +#include "autocalibration.hh" + +#include "accesspoint.hh" +#include "stock.hh" +#include "configuration.hh" +#include "posexcept.hh" + +#include +#include + +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::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 &ref1_cr = + ref1_rp.get_requests() ; + const Measurement *rx_measurement = NULL ; + vector::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::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::const_iterator> + angle_ap(angle, ref) ; + pair::const_iterator> > + weight_angle_ap(weight, angle_ap) ; + sorted_angles.insert(weight_angle_ap) ; + } +} + + +void Autocalibration::weight_aps() +{ + map::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 ; + } +} diff --git a/owlps-positioner/src/autocalibration.hh b/owlps-positioner/src/autocalibration.hh new file mode 100644 index 0000000..aba3035 --- /dev/null +++ b/owlps-positioner/src/autocalibration.hh @@ -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 + +/** + * 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::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::const_iterator> > + sorted_angles ; + /// Characteristics of the virtual mobile + double vmob_gain, vmob_pow ; + /// Generated measurements' list + std::tr1::unordered_map 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_ diff --git a/owlps-positioner/src/stock.cc b/owlps-positioner/src/stock.cc index 36202a3..0d36549 100644 --- a/owlps-positioner/src/stock.cc +++ b/owlps-positioner/src/stock.cc @@ -26,8 +26,6 @@ unordered_map Stock::waypoints ; unordered_map Stock::mobiles ; -uint32_t Stock::nb_virtual_mobiles = 0 ; - unordered_map Stock::aps ; unordered_setRX 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 ¤t_rp = find_create_reference_point(point) ; - - /* Create the timestamp */ - Timestamp time_sent ; - time_sent.now() ; - - /* Create the measurement list */ - unordered_map 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::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::const_iterator> > - sorted_angles ; - for (unordered_map::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::const_iterator> - angle_ap(angle, ref) ; - pair::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::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 &ref1_cr = - ref1_rp.get_requests() ; - const Measurement *rx_measurement = NULL ; - vector::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(¤t_rp) ; - - store_calibration_request(cr) ; } diff --git a/owlps-positioner/src/stock.hh b/owlps-positioner/src/stock.hh index 568b0ce..90ef998 100644 --- a/owlps-positioner/src/stock.hh +++ b/owlps-positioner/src/stock.hh @@ -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 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 aps ; @@ -48,12 +48,6 @@ private: /// List of known CalibrationRequest static std::tr1::unordered_set 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