/* * This file is part of the Owl Positioning System (OwlPS) project. * It is subject to the copyright notice and license terms in the * COPYRIGHT.t2t file found in the top-level directory of this * distribution and at * https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t * No part of the OwlPS Project, including this file, may be copied, * modified, propagated, or distributed except according to the terms * contained in the COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be * distributed along with this file, either separately or by replacing * this notice by the COPYRIGHT.t2t file's contents. */ #include "autocalibrationmesh.hh" #include "point3d.hh" #include "capturepoint.hh" #include "stock.hh" #include "configuration.hh" #include "posexcept.hh" #include #include #include #include using namespace std ; /* *** Operations *** */ void AutocalibrationMesh::generate_ss() { /* Compute the origin angle P-RX-O */ const Point3D &rx_coord = rx->second.get_coordinates() ; Point3D origin(rx_coord) ; origin.set_x(rx_coord.get_x() + 10) ; origin_angle = rx_coord.angle_2d(point, origin) ; if (point.get_y() > rx_coord.get_y()) origin_angle = -origin_angle ; /* Choose the nearest CP(s) in angle */ sort_reference_cps() ; // We need at least one reference CP: if (sorted_negative_angles.empty() && sorted_positive_angles.empty()) throw autocalibration_error("Not enough CPs in coverage!") ; /* Compute the angle weight of the reference CPs */ weight_cps() ; /* Compute the SS that would be received from a mobile at a * distance of RX-P, in the direction of each reference CP */ compute_ss() ; } /** * The reference (transmitter) CPs are first filtered (floor, coverage), * then sorted according to their angles with RX and P. */ void AutocalibrationMesh::sort_reference_cps() { const Point3D &rx_coord = rx->second.get_coordinates() ; sorted_negative_angles.clear() ; sorted_positive_angles.clear() ; for (auto ref = Stock::cps.begin() ; ref != Stock::cps.end() ; ++ref) { if (ref == rx) continue ; /* Skip the CP if it is not at the same floor than the * receiver CP */ const Point3D &ref_coord = ref->second.get_coordinates() ; if (ref_coord.get_z() != rx_coord.get_z()) continue ; /* Skip the CP if it is not in coverage with the receiver CP */ float coverage = rx->second.received_calibration_from_cp(ref->second) ; if (coverage < 1) // Less than 1% coverage is ridiculous! continue ; /* Angle P-RX-REF */ double angle = rx_coord.angle_2d(point, ref_coord) ; /* Weight in the CPs' list according to coverage and angle */ double weight = angle / coverage ; /* Note: this weight is used only to sort the CPs, it has nothing to do with the angle weight (see weight_cps()) used to compute the SSs. */ /* Create the list entry */ pair::const_iterator> angle_cp(angle, ref) ; pair::const_iterator> > weight_angle_cp(weight, angle_cp) ; /* Rotate the CP's coordinates to know the angle direction */ Point3D ref_coord_r(ref_coord) ; ref_coord_r.rotate_2d(rx_coord, origin_angle) ; // Insert the CP in the right list, according to the direction: if (ref_coord_r.get_y() < rx_coord.get_y()) sorted_negative_angles.insert(weight_angle_cp) ; else sorted_positive_angles.insert(weight_angle_cp) ; } } void AutocalibrationMesh::weight_cps() { /* Retrieve the reference CPs & angles */ ref_cps.clear() ; map::const_iterator> > ::const_iterator s ; s = sorted_negative_angles.begin() ; if (s != sorted_negative_angles.end()) init_cp(s) ; s = sorted_positive_angles.begin() ; if (s != sorted_positive_angles.end()) init_cp(s) ; /* Weight the CPs */ if (ref_cps.size() == 1) ref_cps[0].weight = 100 ; else if (ref_cps.size() == 2) weight_2_cps() ; else { ostringstream oss ; oss << "We should not be here... error when sorting the reference" << " CPs? (" << ref_cps.size() << " CPs sorted)" ; throw autocalibration_error(oss.str()) ; } } void AutocalibrationMesh::init_cp( const map::const_iterator> >::const_iterator s) { struct cp ref ; ref.cp = &s->second.second->second ; ref.angle = s->second.first ; // Angle P-REF-RX ref_cps.push_back(ref) ; } void AutocalibrationMesh::weight_2_cps() { assert(ref_cps.size() > 1) ; /* Compute the angle REF1-RX-REF2 */ double reference_angle = ref_cps[0].angle + ref_cps[1].angle ; /* Note: the reference angle must be the sum of the two angles P-RX-REF1 and P-RX-REF2 instead of simply REF1-RX-REF2, to allow the angle to be within [0;360[° instead of [0;180[°. This is needed if P-RX-REF1 or P-RX-REF2 are greater than 90°, otherwise the weights would be wrong. */ /* Weight the two CPs */ if (reference_angle == 0) ref_cps[0].weight = ref_cps[1].weight = 50 ; else { ref_cps[0].weight = ref_cps[0].angle * 100 / reference_angle ; if (ref_cps[0].weight > 100) ref_cps[0].weight = 100 ; ref_cps[1].weight = 100 - ref_cps[0].weight ; } } void AutocalibrationMesh::compute_multi_packet_ss() { const cp &ref1 = ref_cps[0] ; const ReferencePoint &ref1_rp = Stock::get_reference_point(ref1.cp->get_coordinates()) ; const vector &ref1_cr = ref1_rp.get_requests() ; /* Search for the first measurement made by RX in ref1_rp */ const Measurement *rx_measurement = nullptr ; vector::const_iterator cr ; const string &rx_mac = rx->second.get_mac_addr() ; for (cr = ref1_cr.begin() ; cr != ref1_cr.end() ; ++cr) { rx_measurement = (*cr)->get_measurement(rx_mac) ; if (rx_measurement) break ; } // The selected reference CPs 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 */ bool packet_generated = false ; for (pkt_id_t pkt_id = 1 ; pkt_id <= (*cr)->get_nb_packets() ; ++pkt_id) if (compute_single_packet_ss(pkt_id)) packet_generated = true ; if (! packet_generated) { if (Configuration::is_configured("verbose")) cerr << "No packet has been generated for RX CP " << rx_mac << "... failing back to single packet request. Calibration" << " request involved : " << **cr << "\n" ; compute_single_packet_ss(0) ; } } /** * @param pkt_id The number of the packet to create. Must be 0 if all * the generated reference points contain only one packet. */ bool AutocalibrationMesh::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 ; // Reminder: ref_cps.size() can be 1 or 2 for (unsigned int i = 0 ; i < ref_cps.size() ; ++i) { double tmp_ss = compute_weighted_ss(i, pkt_id, point_dst) ; if (tmp_ss > 127) // error return false ; 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_cp(&rx->second) ; return true ; } /** * @param cp_idx The index of the CP in #ref_cps. * @param pkt_id The number of the packet to generate, or 0 if we * generate only one packet per reference point. * @param point_dst The distance RX-P. * * @returns The weighted SS value in dBm, or 128 in case of error. */ double AutocalibrationMesh::compute_weighted_ss( const unsigned int cp_idx, const pkt_id_t pkt_id, const float point_dst) { const cp &ref = ref_cps[cp_idx] ; const ReferencePoint &rp = Stock::get_reference_point(ref.cp->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_cp(rx_mac) ; else { friis_idx = rp.friis_index_for_cp(rx_mac, pkt_id) ; if (friis_idx == 0) return 128 ; // 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.cp->get_trx_power() + ref.cp->get_antenna_gain() - 10 * friis_idx * log10(point_dst) ; return ss * ref.weight / 100 ; }