255 lines
7.6 KiB
C++
255 lines
7.6 KiB
C++
/*
|
|
* 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 "autocalibrationline.hh"
|
|
|
|
#include "point3d.hh"
|
|
#include "capturepoint.hh"
|
|
#include "stock.hh"
|
|
#include "configuration.hh"
|
|
#include "posexcept.hh"
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
using namespace std ;
|
|
|
|
|
|
|
|
/* *** Constructors *** */
|
|
|
|
|
|
/**
|
|
* @param _point The coordinates of the reference point to generate.
|
|
* @param _cp1 The first capture point (receiver) to use the measurements
|
|
* of to generate the reference points; it cannot be null.
|
|
* @param _cp2 The second receiver capture point (optional).
|
|
*/
|
|
AutocalibrationLine::AutocalibrationLine(
|
|
const Point3D &_point,
|
|
const CapturePoint *const _cp1, const CapturePoint *const _cp2):
|
|
Autocalibration(_point), cp1(_cp1), cp2(_cp2),
|
|
trx_cp(nullptr), trx_rp(nullptr)
|
|
{
|
|
assert(cp1) ;
|
|
}
|
|
|
|
|
|
|
|
/* *** Operations *** */
|
|
|
|
|
|
void AutocalibrationLine::generate_ss()
|
|
{
|
|
/* With AutocalibrationLine, we want to generate measurements only
|
|
* for the CP(s) we want to use. */
|
|
assert(&rx->second) ;
|
|
if (&rx->second != cp1 && &rx->second != cp2)
|
|
return ;
|
|
|
|
/* Choose the reference CP: try the other extremity first */
|
|
if (&rx->second == cp2) // rx is cp2
|
|
trx_cp = cp1 ;
|
|
else if (cp2) // rx is cp1 and cp2 is defined
|
|
trx_cp = cp2 ;
|
|
else // rx is cp1 and cp2 is not defined
|
|
/* Choose another reference CP */
|
|
sort_reference_cps() ;
|
|
|
|
/* Get the ReferencePoint corresponding to trx_cp */
|
|
try
|
|
{
|
|
trx_rp = &Stock::get_reference_point(trx_cp->get_coordinates()) ;
|
|
}
|
|
catch (element_not_found &e)
|
|
{
|
|
ostringstream oss ;
|
|
oss << "No measurements from transmitter CP "
|
|
<< trx_cp->get_mac_addr()
|
|
<< ": cannot generate RP " << point
|
|
<< " (receiver CP: " << rx->second.get_mac_addr() << ")." ;
|
|
throw autocalibration_error(oss.str()) ;
|
|
}
|
|
assert(trx_rp) ;
|
|
|
|
compute_ss() ;
|
|
}
|
|
|
|
|
|
/**
|
|
* The reference (transmitter) CPs are first filtered (floor, coverage),
|
|
* then sorted according to the signal strength RX receives from them.
|
|
*/
|
|
void AutocalibrationLine::sort_reference_cps()
|
|
{
|
|
// Transmitter capture points, sorted by coverage with rx
|
|
std::map<float, const CapturePoint*> sorted_ref_cps ;
|
|
|
|
const Point3D &rx_coord = rx->second.get_coordinates() ;
|
|
|
|
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 ;
|
|
|
|
/* Store the CP according to the SS rx receives from it */
|
|
const ReferencePoint &ref_rp =
|
|
Stock::get_reference_point(ref->second.get_coordinates()) ;
|
|
const vector<CalibrationRequest*> &ref_cr = ref_rp.get_requests() ;
|
|
/* Search for the first measurement made by RX in ref_rp */
|
|
const Measurement *rx_measurement = nullptr ;
|
|
const string &rx_mac = rx->second.get_mac_addr() ;
|
|
for (auto cr = ref_cr.begin() ; cr != ref_cr.end() ; ++cr)
|
|
{
|
|
rx_measurement = (*cr)->get_measurement(rx_mac) ;
|
|
if (rx_measurement)
|
|
break ;
|
|
}
|
|
// We skip CPs in bad coverage, so we should have a measurement
|
|
assert(rx_measurement) ;
|
|
float avg_ss = rx_measurement->get_average_dbm() ;
|
|
sorted_ref_cps[avg_ss] = &ref->second ;
|
|
}
|
|
|
|
if (sorted_ref_cps.empty())
|
|
{
|
|
ostringstream oss ;
|
|
oss << "No CPs in coverage with receiver CP "
|
|
<< rx->second.get_mac_addr() << " to generate RP "
|
|
<< point << "." ;
|
|
throw autocalibration_error(oss.str()) ;
|
|
}
|
|
|
|
// Select the last CP from sorted_ref_cps (the CP with the highest SS)
|
|
trx_cp = sorted_ref_cps.rbegin()->second ;
|
|
}
|
|
|
|
|
|
void AutocalibrationLine::compute_multi_packet_ss()
|
|
{
|
|
const vector<CalibrationRequest*> &ref_cr = trx_rp->get_requests() ;
|
|
|
|
/* Search for the first measurement made by RX in trx_rp */
|
|
const Measurement *rx_measurement = nullptr ;
|
|
vector<CalibrationRequest*>::const_iterator cr ;
|
|
const string &rx_mac = rx->second.get_mac_addr() ;
|
|
for (cr = ref_cr.begin() ; cr != ref_cr.end() ; ++cr)
|
|
{
|
|
rx_measurement = (*cr)->get_measurement(rx_mac) ;
|
|
if (rx_measurement)
|
|
break ;
|
|
}
|
|
|
|
/* The selected reference CP is supposed to be in coverage of RX,
|
|
* but it is possible that it is not, in case we use cp1 and cp2
|
|
* and no calibration request was transmitted in both directions
|
|
* yet. */
|
|
if (! rx_measurement)
|
|
{
|
|
ostringstream oss ;
|
|
oss << "Transmitter CP " << trx_cp->get_mac_addr()
|
|
<< " is not in coverage with receiver CP "
|
|
<< rx->second.get_mac_addr() << " to generate RP "
|
|
<< point << "." ;
|
|
throw autocalibration_error(oss.str()) ;
|
|
}
|
|
|
|
/* 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 AutocalibrationLine::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 SS */
|
|
double rx_ss = compute_single_ss(pkt_id, point_dst) ;
|
|
if (rx_ss > 127) // error
|
|
return false ;
|
|
|
|
/* 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 ;
|
|
}
|
|
|
|
|
|
double AutocalibrationLine::
|
|
compute_single_ss(const pkt_id_t pkt_id, const float point_dst)
|
|
{
|
|
/* Friis index */
|
|
const string &rx_mac = rx->second.get_mac_addr() ;
|
|
float friis_idx ;
|
|
if (pkt_id == 0)
|
|
friis_idx = trx_rp->friis_index_for_cp(rx_mac) ;
|
|
else
|
|
{
|
|
friis_idx = trx_rp->friis_index_for_cp(rx_mac, pkt_id) ;
|
|
if (friis_idx == 0)
|
|
return 128 ; // The packet pkt_id does not exist in trx_rp
|
|
}
|
|
|
|
/* SS */
|
|
// Constant term
|
|
double common_ss = rx->second.friis_constant_term() ;
|
|
double ss =
|
|
common_ss +
|
|
trx_cp->get_trx_power() +
|
|
trx_cp->get_antenna_gain() -
|
|
10 * friis_idx * log10(point_dst) ;
|
|
|
|
return ss ;
|
|
}
|