843 lines
23 KiB
C++
843 lines
23 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
|
|
* http://code.lm7.fr/p/owlps/source/tree/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 "stock.hh"
|
|
#include "configuration.hh"
|
|
#include "posexcept.hh"
|
|
#include "area.hh"
|
|
#include "csvstringreader.hh"
|
|
#include "autocalibrationmesh.hh"
|
|
#include "autocalibrationline.hh"
|
|
|
|
#include <iostream>
|
|
|
|
using namespace std ;
|
|
|
|
|
|
|
|
/* *** Attribute definitions *** */
|
|
|
|
unordered_map<string, Building> Stock::buildings ;
|
|
|
|
unordered_map<Point3D, Waypoint> Stock::waypoints ;
|
|
|
|
unordered_map<string, Mobile> Stock::mobiles ;
|
|
|
|
unordered_map<string, CapturePoint> Stock::cps ;
|
|
|
|
unordered_set<ReferencePoint> Stock::reference_points ;
|
|
|
|
unordered_set<CalibrationRequest> Stock::calibration_requests ;
|
|
|
|
|
|
void Stock::clear()
|
|
{
|
|
buildings.clear() ;
|
|
|
|
// Waypoints are normally deleted in Building::~Building(), so we must
|
|
// not clear waypoints before buildings, but we clear it *after*, just
|
|
// in case.
|
|
waypoints.clear() ;
|
|
|
|
mobiles.clear() ;
|
|
cps.clear() ;
|
|
reference_points.clear() ;
|
|
calibration_requests.clear() ;
|
|
}
|
|
|
|
|
|
|
|
/* *** Building operations *** */
|
|
|
|
|
|
/**
|
|
* The name of the Building is initialised.
|
|
*/
|
|
const Building& Stock::find_create_building(const string &name)
|
|
{
|
|
unordered_map<string, Building>::const_iterator i =
|
|
buildings.find(name) ;
|
|
if (i != buildings.end())
|
|
return i->second ;
|
|
|
|
Building &building = buildings[name] ;
|
|
building.set_name(name) ;
|
|
return building ;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param name The name of the Building to search for.
|
|
* It must be a valid name, as no check is performed.
|
|
* @returns A const reference to the Building.
|
|
* @throw element_not_found is thrown if the Building corresponding
|
|
* to `name` does not exist.
|
|
*/
|
|
const Building& Stock::get_building(const string &name)
|
|
{
|
|
unordered_map<string, Building>::const_iterator i =
|
|
buildings.find(name) ;
|
|
if (i != buildings.end())
|
|
return i->second ;
|
|
throw element_not_found("No Building with name \"" + name + "\"!") ;
|
|
}
|
|
|
|
|
|
/**
|
|
* @returns A pointer to the first Area in which `point` was found.
|
|
* @returns NULL if `point` does not belong to any Area.
|
|
*/
|
|
const Area* Stock::in_which_area_is(const Point3D &point)
|
|
{
|
|
for (unordered_map<string, Building>::const_iterator
|
|
b = buildings.begin() ; b != buildings.end() ; ++b)
|
|
{
|
|
const unordered_map<string, Area*> &areas = b->second.get_areas() ;
|
|
for (unordered_map<string, Area*>::const_iterator
|
|
a = areas.begin() ; a != areas.end() ; ++a)
|
|
if (a->second->contains_point(point))
|
|
return a->second ;
|
|
}
|
|
|
|
return NULL ;
|
|
}
|
|
|
|
|
|
|
|
/* *** Waypoint operations *** */
|
|
|
|
|
|
const Waypoint& Stock::
|
|
find_create_waypoint(const Point3D &point)
|
|
{
|
|
unordered_map<Point3D, Waypoint>::const_iterator i =
|
|
waypoints.find(point) ;
|
|
if (i != waypoints.end())
|
|
return i->second ;
|
|
|
|
Waypoint &waypoint = waypoints[point] ;
|
|
waypoint.set_coordinates(point) ;
|
|
return waypoint ;
|
|
}
|
|
|
|
|
|
void Stock::waypoint_remove_building(const Waypoint &point,
|
|
const Building *building)
|
|
{
|
|
unordered_map<Point3D, Waypoint>::iterator i =
|
|
waypoints.find(point) ;
|
|
|
|
Waypoint *waypoint = const_cast<Waypoint*>(&i->second) ;
|
|
waypoint->remove_building(building) ;
|
|
|
|
// If the Waypoint is not linked to any Building any more, we
|
|
// delete it
|
|
if (waypoint->get_1st_building() == NULL)
|
|
waypoints.erase(i) ;
|
|
}
|
|
|
|
|
|
|
|
/* *** Mobile operations *** */
|
|
|
|
|
|
/**
|
|
* @param mac The MAC address of the Mobile to search for.
|
|
* It must be a valid MAC address, as no check is performed.
|
|
* @returns `true` if a mobile with this MAC address exists.
|
|
* @returns `false` if this MAC address was not found.
|
|
*/
|
|
bool Stock::mobile_exists(const std::string &mac)
|
|
{
|
|
unordered_map<string, Mobile>::const_iterator i = mobiles.find(mac) ;
|
|
return i != mobiles.end() ;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param mac The MAC address of the Mobile to search for.
|
|
* It must be a valid MAC address, as no check is performed.
|
|
* @returns A const reference to the Mobile.
|
|
* @throw element_not_found is thrown if the Mobile corresponding
|
|
* to `mac` does not exist.
|
|
*/
|
|
const Mobile& Stock::get_mobile(const string &mac)
|
|
{
|
|
unordered_map<string, Mobile>::const_iterator i = mobiles.find(mac) ;
|
|
if (i != mobiles.end())
|
|
return i->second ;
|
|
throw element_not_found("No Mobile with MAC address \"" +
|
|
mac + "\"!") ;
|
|
}
|
|
|
|
|
|
/**
|
|
* If created, the MAC address of the Mobile is initialised.
|
|
*/
|
|
const Mobile& Stock::find_create_mobile(const string &mac)
|
|
{
|
|
unordered_map<string, Mobile>::const_iterator i = mobiles.find(mac) ;
|
|
if (i != mobiles.end())
|
|
return i->second ;
|
|
|
|
Mobile &mobile = mobiles[mac] ;
|
|
mobile.set_mac_addr(mac) ;
|
|
return mobile ;
|
|
}
|
|
|
|
|
|
/**
|
|
* If the Mobile already exists, it is replaced by `source.`
|
|
*/
|
|
const Mobile& Stock::find_create_mobile(const Mobile &source)
|
|
{
|
|
const string &mac = source.get_mac_addr() ;
|
|
unordered_map<string, Mobile>::const_iterator i = mobiles.find(mac) ;
|
|
if (i != mobiles.end())
|
|
return i->second ;
|
|
|
|
mobiles[mac] = source ;
|
|
return mobiles[mac] ;
|
|
}
|
|
|
|
|
|
|
|
/* *** CapturePoint operations *** */
|
|
|
|
|
|
/**
|
|
* @param mac The MAC address of the CapturePoint to search for.
|
|
* It must be a valid MAC address, as no check is performed.
|
|
* @returns `true` if a CP with this MAC address exists.
|
|
* @returns `false` if this MAC address was not found.
|
|
*/
|
|
bool Stock::cp_exists(const std::string &mac)
|
|
{
|
|
unordered_map<string, CapturePoint>::const_iterator i = cps.find(mac) ;
|
|
return i != cps.end() ;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param mac The MAC address of the CapturePoint to search for.
|
|
* It must be a valid MAC address, as no check is performed.
|
|
* @returns A const reference to the CapturePoint.
|
|
* @throw element_not_found is thrown if the CapturePoint corresponding
|
|
* to `mac` does not exist.
|
|
*/
|
|
const CapturePoint& Stock::get_cp(const string &mac)
|
|
{
|
|
unordered_map<string, CapturePoint>::const_iterator i = cps.find(mac) ;
|
|
if (i != cps.end())
|
|
return i->second ;
|
|
throw element_not_found("No CapturePoint with MAC address \"" +
|
|
mac + "\"!") ;
|
|
}
|
|
|
|
|
|
/**
|
|
* The MAC address of the CapturePoint is initialised.
|
|
*/
|
|
const CapturePoint& Stock::find_create_cp(const string &mac)
|
|
{
|
|
unordered_map<string, CapturePoint>::const_iterator i = cps.find(mac) ;
|
|
if (i != cps.end())
|
|
return i->second ;
|
|
|
|
CapturePoint &cp = cps[mac] ;
|
|
cp.set_mac_addr(mac) ;
|
|
|
|
if (Configuration::is_configured("verbose"))
|
|
cerr
|
|
<< "New CP \"" << mac
|
|
<< "\" (total: " << nb_cps() << " CPs).\n" ;
|
|
|
|
return cp ;
|
|
}
|
|
|
|
|
|
/**
|
|
* If the CapturePoint already exists, it is replaced by `source.`
|
|
*/
|
|
const CapturePoint& Stock::find_create_cp(const CapturePoint &source)
|
|
{
|
|
const string &mac = source.get_mac_addr() ;
|
|
unordered_map<string, CapturePoint>::const_iterator i = cps.find(mac) ;
|
|
if (i != cps.end())
|
|
return i->second ;
|
|
|
|
cps[mac] = source ;
|
|
return cps[mac] ;
|
|
}
|
|
|
|
|
|
void Stock::update_all_friis_indexes()
|
|
{
|
|
for (unordered_map<string, CapturePoint>::iterator cp = cps.begin() ;
|
|
cp != cps.end() ; ++cp)
|
|
{
|
|
double friis_idx_sum = 0 ;
|
|
int nb_friis_idx = 0 ;
|
|
|
|
// Compute main general term, independant from scans
|
|
double const_term = cp->second.friis_constant_term() ;
|
|
|
|
/*
|
|
* Compute indexes for each ReferencePoint. The Friis index for an
|
|
* CP is the average of all indexes in all ReferencePoint.
|
|
*/
|
|
for (unordered_set<ReferencePoint>::const_iterator rp =
|
|
reference_points.begin() ; rp != reference_points.end() ;
|
|
++rp)
|
|
{
|
|
int nb_idx = 0 ;
|
|
float cp_friis_idx_sum =
|
|
rp->friis_indexes_for_cp(cp->second, const_term, nb_idx) ;
|
|
if (nb_idx != 0)
|
|
{
|
|
friis_idx_sum += cp_friis_idx_sum ;
|
|
nb_friis_idx += nb_idx ;
|
|
}
|
|
}
|
|
|
|
if (nb_friis_idx > 0)
|
|
cp->second.set_friis_index(friis_idx_sum / nb_friis_idx) ;
|
|
}
|
|
}
|
|
|
|
|
|
double Stock::cp_matrix_get_ss(const std::string &mac_transmitter,
|
|
const std::string &mac_receiver)
|
|
{
|
|
const CapturePoint &receiver = get_cp(mac_receiver) ;
|
|
const ReferencePoint &rp =
|
|
get_reference_point(receiver.get_coordinates()) ;
|
|
|
|
return rp.average_measurements(mac_transmitter) ;
|
|
}
|
|
|
|
|
|
/**
|
|
* @returns A const pointer to the CP `coord` is the coordinates of.
|
|
* @returns NULL if `coord` is not the coordinates of any CP.
|
|
*/
|
|
const CapturePoint* Stock::is_cp_coordinate(const Point3D &coord)
|
|
{
|
|
for (auto cp = cps.begin() ; cp != cps.end() ; ++cp)
|
|
if (cp->second.get_coordinates() == coord)
|
|
return &cp->second ;
|
|
|
|
return NULL ;
|
|
}
|
|
|
|
|
|
|
|
/* *** ReferencePoint operations *** */
|
|
|
|
|
|
bool Stock::reference_point_exists(const ReferencePoint &point)
|
|
{
|
|
unordered_set<ReferencePoint>::const_iterator i =
|
|
reference_points.find(point) ;
|
|
return i != reference_points.end() ;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param point Coordinates of the wanted ReferencePoint.
|
|
* @returns The ReferencePoint at the given coordinates, if found.
|
|
* @throw element_not_found is thrown if no ReferencePoint exists at the
|
|
* given coordinates.
|
|
*/
|
|
const ReferencePoint& Stock::
|
|
get_reference_point(const ReferencePoint &point)
|
|
{
|
|
unordered_set<ReferencePoint>::const_iterator i =
|
|
reference_points.find(point) ;
|
|
if (i != reference_points.end())
|
|
return *i ;
|
|
throw element_not_found("No ReferencePoint with coordinates " +
|
|
static_cast<string>(point) + "!") ;
|
|
}
|
|
|
|
|
|
const ReferencePoint& Stock::
|
|
find_create_reference_point(const ReferencePoint &point)
|
|
{
|
|
// unordered_set::insert() do all the job: see the documentation at
|
|
// http://en.cppreference.com/w/cpp/container/unordered_set/insert
|
|
pair<unordered_set<ReferencePoint>::iterator, bool> ret =
|
|
reference_points.insert(point) ;
|
|
return *ret.first ;
|
|
}
|
|
|
|
|
|
const ReferencePoint& Stock::
|
|
closest_reference_point(const Request &request)
|
|
{
|
|
if (reference_points.empty())
|
|
throw element_not_found(
|
|
"Cannot search for the closest reference point: reference points'"
|
|
" list is empty!") ;
|
|
|
|
bool ignore_cps = Configuration::bool_value(
|
|
"positioning.nss.ignore-cp-reference-points") ;
|
|
unordered_set<ReferencePoint>::const_iterator i =
|
|
reference_points.begin() ;
|
|
|
|
if (ignore_cps)
|
|
{
|
|
// Fast-forward to the next non-CP reference point
|
|
while (i != reference_points.end() && is_cp_coordinate(*i))
|
|
++i ;
|
|
|
|
// No non-CP reference point was found, we are forced to consider
|
|
// the CP reference points
|
|
if (i == reference_points.end())
|
|
ignore_cps = false ;
|
|
}
|
|
|
|
float similarity = i->similarity(request) ;
|
|
unordered_set<ReferencePoint>::const_iterator closest = i ;
|
|
|
|
for (++i ; i != reference_points.end() ; ++i)
|
|
{
|
|
if (ignore_cps && is_cp_coordinate(*i))
|
|
continue ;
|
|
|
|
float tmp_similarity = i->similarity(request) ;
|
|
if (tmp_similarity < similarity)
|
|
{
|
|
similarity = tmp_similarity ;
|
|
closest = i ;
|
|
}
|
|
}
|
|
|
|
return *closest ;
|
|
}
|
|
|
|
|
|
void Stock::regenerate_reference_points()
|
|
{
|
|
assert(Configuration::autocalibration_enabled()) ;
|
|
assert(! cps.empty()) ;
|
|
assert(! reference_points.empty()) ;
|
|
|
|
delete_non_cp_calibration_requests() ;
|
|
|
|
const string &ac_mode = Configuration::string_value(
|
|
"positioning.generate-reference-points") ;
|
|
assert(! ac_mode.empty()) ;
|
|
|
|
if (ac_mode.find("mesh") != string::npos)
|
|
generate_reference_points_mesh() ;
|
|
|
|
if (ac_mode.find("line") != string::npos)
|
|
generate_reference_points_line() ;
|
|
|
|
if (ac_mode.find("list") != string::npos)
|
|
generate_reference_points_list() ;
|
|
}
|
|
|
|
|
|
void Stock::generate_reference_points_mesh()
|
|
{
|
|
if (! (Configuration::is_configured("positioning.area-start") &&
|
|
Configuration::is_configured("positioning.area-stop")))
|
|
throw missing_configuration(
|
|
"In order to generate a meshing of reference points, you must"
|
|
" define the deployment area.") ;
|
|
|
|
if (reference_points.size() < 3)
|
|
{
|
|
if (Configuration::is_configured("verbose"))
|
|
cerr << reference_points.size() << " true reference points is"
|
|
<< " not enough to generate a meshing of reference"
|
|
<< " points.\n" ;
|
|
return ;
|
|
}
|
|
|
|
Point3D start(Configuration::string_value("positioning.area-start")) ;
|
|
Point3D stop(Configuration::string_value("positioning.area-stop")) ;
|
|
float step_x =
|
|
Configuration::float_value("positioning.generated-meshing-grain-x") ;
|
|
float step_y =
|
|
Configuration::float_value("positioning.generated-meshing-grain-y") ;
|
|
float step_z =
|
|
Configuration::float_value("positioning.generated-meshing-grain-z") ;
|
|
|
|
/* Generate RPs */
|
|
for (float x = start.get_x() ; x <= stop.get_x() ; x += step_x)
|
|
for (float y = start.get_y() ; y <= stop.get_y() ; y += step_y)
|
|
for (float z = start.get_z() ; z <= stop.get_z() ; z += step_z)
|
|
{
|
|
AutocalibrationMesh ac(Point3D(x,y,z)) ;
|
|
generate_reference_point(ac) ;
|
|
}
|
|
}
|
|
|
|
|
|
void Stock::generate_reference_points_line()
|
|
{
|
|
if (! Configuration::is_configured("positioning.generated-line-path"))
|
|
throw missing_configuration(
|
|
"In order to generate a line of reference points, you must"
|
|
" define a path.") ;
|
|
|
|
if (reference_points.empty())
|
|
{
|
|
if (Configuration::is_configured("verbose"))
|
|
cerr << "At least one true reference points is necessary to"
|
|
<< " generate a path of reference points.\n" ;
|
|
return ;
|
|
}
|
|
|
|
const string &path_csv =
|
|
Configuration::string_value("positioning.generated-line-path") ;
|
|
CSVStringReader path_parser(path_csv) ;
|
|
Point3D p ;
|
|
vector<Point3D> read_points ;
|
|
const CapturePoint *cp1 = NULL ;
|
|
|
|
// Read points until the first CP coordinate
|
|
while (path_parser.read_point3d(p))
|
|
{
|
|
cp1 = Stock::is_cp_coordinate(p) ;
|
|
if (cp1)
|
|
break ;
|
|
read_points.push_back(p) ;
|
|
}
|
|
|
|
// We reached the end of the points' list without finding the
|
|
// coordinates of a CP
|
|
if (! cp1)
|
|
{
|
|
cerr << "Warning! positioning.generated-line-path doesn't contain"
|
|
<< "any point that is the coordinates of capture point!"
|
|
<< endl ;
|
|
return ;
|
|
}
|
|
|
|
// Generate the first RPs (between the begining and cp1)
|
|
if (! read_points.empty())
|
|
{
|
|
auto first = read_points.begin() ;
|
|
for (auto end = first + 1 ; end != read_points.end() ; ++end)
|
|
{
|
|
generate_interpolated_reference_points(*first, *end, cp1) ;
|
|
first = end ;
|
|
}
|
|
generate_interpolated_reference_points(
|
|
*first, cp1->get_coordinates(), cp1) ;
|
|
read_points.clear() ;
|
|
}
|
|
|
|
// Read the next points and generate the RPs
|
|
const CapturePoint *cp2 = NULL ;
|
|
while (path_parser.read_point3d(p))
|
|
{
|
|
cp2 = Stock::is_cp_coordinate(p) ;
|
|
if (! cp2)
|
|
{
|
|
read_points.push_back(p) ;
|
|
continue ;
|
|
}
|
|
|
|
// We don't have any intermediate points between the two CPs
|
|
if (read_points.empty())
|
|
generate_interpolated_reference_points(
|
|
cp1->get_coordinates(), cp2->get_coordinates(), cp1, cp2) ;
|
|
|
|
// Generate the RPs between cp1, the intermediate points, and cp2
|
|
else
|
|
{
|
|
auto first = read_points.begin() ;
|
|
generate_interpolated_reference_points(
|
|
cp1->get_coordinates(), *first, cp1, cp2) ;
|
|
for (auto end = first + 1 ; end != read_points.end() ; ++end)
|
|
{
|
|
generate_interpolated_reference_points(*first, *end,
|
|
cp1, cp2) ;
|
|
first = end ;
|
|
}
|
|
generate_interpolated_reference_points(
|
|
*first, cp2->get_coordinates(), cp1, cp2) ;
|
|
read_points.clear() ;
|
|
}
|
|
|
|
cp1 = cp2 ;
|
|
cp2 = NULL ;
|
|
}
|
|
|
|
// Generate the last RPs (after the last RP)
|
|
if (read_points.empty())
|
|
return ;
|
|
auto first = read_points.begin() ;
|
|
generate_interpolated_reference_points(
|
|
cp1->get_coordinates(), *first, cp1) ;
|
|
for (auto end = first + 1 ; end != read_points.end() ; ++end)
|
|
{
|
|
generate_interpolated_reference_points(*first, *end, cp1) ;
|
|
first = end ;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* This function interpolates the coordinates between `start` and `end`
|
|
* and generates all the corresponding reference points (the two
|
|
* extremities are generated as well).
|
|
*
|
|
* @param start The first point of the line.
|
|
* @param end The last point of the line.
|
|
* @param cp1 The first capture point to use to generate the reference
|
|
* points.
|
|
* @param cp2 The second capture point to use (optional).
|
|
*/
|
|
void Stock::generate_interpolated_reference_points(
|
|
const Point3D &start, const Point3D &end,
|
|
const CapturePoint *const cp1, const CapturePoint *const cp2)
|
|
{
|
|
assert(cp1) ;
|
|
|
|
// Generate the first RP
|
|
AutocalibrationLine ac_start(start, cp1, cp2) ;
|
|
generate_reference_point(ac_start) ;
|
|
|
|
/* Compute the intermediate coordinates */
|
|
float step_hint =
|
|
Configuration::float_value("positioning.generated-line-step") ;
|
|
vector<Point3D> interpolated ; // List of intermediate points
|
|
// Generate the intermediate coordinates
|
|
start.interpolate(end, step_hint, interpolated) ;
|
|
|
|
// Generate the corresponding reference points
|
|
for (auto i = interpolated.begin() ; i != interpolated.end() ; ++i)
|
|
{
|
|
AutocalibrationLine ac(*i, cp1, cp2) ;
|
|
generate_reference_point(ac) ;
|
|
}
|
|
|
|
// Generate the last RP
|
|
AutocalibrationLine ac_end(end, cp1, cp2) ;
|
|
generate_reference_point(ac_end) ;
|
|
}
|
|
|
|
|
|
void Stock::generate_reference_points_list()
|
|
{
|
|
if (! Configuration::is_configured("positioning.generated-points-list"))
|
|
throw missing_configuration(
|
|
"In order to generate a list of reference points, you must"
|
|
" define the list.") ;
|
|
|
|
if (reference_points.size() < 3)
|
|
{
|
|
if (Configuration::is_configured("verbose"))
|
|
cerr << reference_points.size() << " true reference points is"
|
|
<< " not enough to generate a meshing of reference"
|
|
<< " points.\n" ;
|
|
return ;
|
|
}
|
|
|
|
const string &rp_list_csv =
|
|
Configuration::string_value("positioning.generated-points-list") ;
|
|
|
|
CSVStringReader rp_list_parser(rp_list_csv) ;
|
|
|
|
Point3D rp ;
|
|
while (rp_list_parser.read_point3d(rp))
|
|
{
|
|
AutocalibrationMesh ac(rp) ;
|
|
generate_reference_point(ac) ;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param ac The pre-initialised Autocalibration to use.
|
|
*/
|
|
void Stock::generate_reference_point(Autocalibration &ac)
|
|
{
|
|
try
|
|
{
|
|
ac.generate_reference_point() ;
|
|
}
|
|
catch (autocalibration_error &e)
|
|
{
|
|
if (Configuration::is_configured("verbose"))
|
|
cerr << e.what() << '\n' ;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* *** CalibrationRequest operations *** */
|
|
|
|
|
|
/**
|
|
* @returns The CalibrationRequest created or found.
|
|
*/
|
|
const CalibrationRequest& Stock::
|
|
store_calibration_request(const CalibrationRequest &request)
|
|
{
|
|
const CalibrationRequest &calibration_request =
|
|
find_create_calibration_request(request) ;
|
|
calibration_request.reference_point_backward_link() ;
|
|
return calibration_request ;
|
|
}
|
|
|
|
|
|
void Stock::
|
|
delete_calibration_request(const CalibrationRequest &request)
|
|
{
|
|
#ifndef NDEBUG
|
|
unordered_set<CalibrationRequest>::const_iterator found =
|
|
calibration_requests.find(request) ;
|
|
if (found == calibration_requests.end())
|
|
{
|
|
cerr
|
|
<< "Error! Cannot delete an unstored CalibrationRequest: "
|
|
<< request << endl ;
|
|
return ;
|
|
}
|
|
#endif // NDEBUG
|
|
|
|
calibration_requests.erase(request) ;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param timeout The maximum age of the calibration requests (seconds).
|
|
*/
|
|
void Stock::delete_calibration_requests_older_than(int timeout)
|
|
{
|
|
assert(timeout > 0) ;
|
|
|
|
unordered_set<CalibrationRequest>::iterator cr =
|
|
calibration_requests.begin() ;
|
|
while (cr != calibration_requests.end())
|
|
{
|
|
Timestamp request_time ;
|
|
if (Configuration::bool_value("replay"))
|
|
request_time = cr->get_time_sent() ;
|
|
else
|
|
request_time = cr->get_time_received() ;
|
|
Timestamp elapsed(request_time.elapsed()) ;
|
|
uint64_t elapsed_sec = static_cast<uint64_t>(elapsed) / 1000 ;
|
|
if (elapsed_sec >= static_cast<unsigned int>(timeout))
|
|
{
|
|
ReferencePoint *rp = cr->get_reference_point() ;
|
|
if (rp)
|
|
{
|
|
rp->delete_request(&*cr) ;
|
|
if (rp->get_requests().empty())
|
|
reference_points.erase(*rp) ;
|
|
}
|
|
cr = calibration_requests.erase(cr) ;
|
|
}
|
|
else
|
|
++cr ;
|
|
}
|
|
}
|
|
|
|
|
|
const CalibrationRequest& Stock::
|
|
find_create_calibration_request(const CalibrationRequest &request)
|
|
{
|
|
pair<unordered_set<CalibrationRequest>::iterator, bool> ret =
|
|
calibration_requests.insert(request) ;
|
|
return *ret.first ;
|
|
}
|
|
|
|
|
|
const CalibrationRequest& Stock::
|
|
closest_calibration_request(const Request &request)
|
|
{
|
|
if (calibration_requests.empty())
|
|
throw element_not_found(
|
|
"Cannot search for the closest calibration request: calibration"
|
|
" requests' list is empty!") ;
|
|
|
|
bool ignore_cps = Configuration::bool_value(
|
|
"positioning.nss.ignore-cp-reference-points") ;
|
|
unordered_set<CalibrationRequest>::const_iterator i =
|
|
calibration_requests.begin() ;
|
|
|
|
if (ignore_cps)
|
|
{
|
|
// Fast-forward to the next non-CP reference point
|
|
while (i != calibration_requests.end() &&
|
|
is_cp_coordinate(*i->get_reference_point()))
|
|
++i ;
|
|
|
|
// No non-CP reference point was found, we are forced to consider
|
|
// the CP reference points
|
|
if (i == calibration_requests.end())
|
|
{
|
|
i = calibration_requests.begin() ;
|
|
ignore_cps = false ;
|
|
}
|
|
}
|
|
|
|
float similarity = i->similarity(request) ;
|
|
unordered_set<CalibrationRequest>::const_iterator closest = i ;
|
|
|
|
for (++i ; i != calibration_requests.end() ; ++i)
|
|
{
|
|
if (ignore_cps && is_cp_coordinate(*i->get_reference_point()))
|
|
continue ;
|
|
|
|
assert(! i->get_measurements().empty()) ;
|
|
|
|
float tmp_similarity = i->similarity(request) ;
|
|
if (tmp_similarity < similarity)
|
|
{
|
|
similarity = tmp_similarity ;
|
|
closest = i ;
|
|
}
|
|
}
|
|
|
|
return *closest ;
|
|
}
|
|
|
|
|
|
void Stock::delete_non_cp_calibration_requests()
|
|
{
|
|
unordered_set<ReferencePoint>::iterator rp = reference_points.begin() ;
|
|
while (rp != reference_points.end())
|
|
{
|
|
ReferencePoint rp_copy(*rp) ;
|
|
if (rp_copy.delete_generated_requests()) // rp_copy was modified
|
|
{
|
|
rp = reference_points.erase(rp) ;
|
|
// Reinsert the modified copy if it still contains at least
|
|
// one CalibrationRequest:
|
|
if (! rp_copy.get_requests().empty())
|
|
reference_points.insert(rp_copy) ;
|
|
/* __Note on the above insert__
|
|
* From the boost::unordered documentation:
|
|
* "insert is only allowed to invalidate iterators when the
|
|
* insertion causes the load factor to be greater than or
|
|
* equal to the maximum load factor."
|
|
* In our case, we always remove an element prior to insert,
|
|
* so the load factor will never change; therefore insert()
|
|
* should be safe here.
|
|
*/
|
|
}
|
|
else // rp_copy was not modified
|
|
++rp ;
|
|
}
|
|
}
|