owlps/owlps-positioner/userinterface.cc

480 lines
16 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 "userinterface.hh"
#include "configuration.hh"
#include <owlps-config.h>
#include <owlps.h>
#include <iostream>
#include <fstream>
#include <boost/program_options.hpp>
using namespace std ;
namespace po = boost::program_options ;
/* *** Default value definitions *** */
/* General options */
#define DEFAULT_CONFIG_FILE OWL_CONFIG_PREFIX "/owlps-positioner.conf"
/* Positioning options */
#define DEFAULT_SS_SIMILARITY "mean"
#define DEFAULT_MESHING_GRAIN 0.5
#define DEFAULT_Z_MESHING_GRAIN 1
#define DEFAULT_SMALLEST_SS -99
/* Output options */
#define DEFAULT_TCPEVAAL_HOST "127.0.0.1"
#define DEFAULT_TCPEVAAL_PORT 4444
/* *** Constructors *** */
/**
* `argc` and `argv` are (normally) the ones passed to the main()
* function.
* @param argc Number of arguments passed to the program.
* @param argv Values of the arguments.
*/
UserInterface::UserInterface(const int argc,
const char *const *const argv):
cli_argument_count(argc), cli_argument_values(argv)
{
assert(cli_argument_values) ;
try
{
cli_options = new po::options_description("General options") ;
file_options = new po::options_description("Parameters") ;
}
catch (bad_alloc &e)
{
throw ;
}
fill_options() ;
parse_options() ;
}
UserInterface::~UserInterface()
{
delete cli_options ;
delete file_options ;
}
/* *** Operations *** */
void UserInterface::fill_options()
{
fill_cli_options() ;
fill_file_options() ;
// File options are also accepted on the command line
cli_options->add(*file_options) ;
}
void UserInterface::fill_cli_options()
{
cli_options->add_options()
("help,h",
"Print help.")
("version,V",
"Print version information.")
("config-file,f",
po::value<string>()->default_value(DEFAULT_CONFIG_FILE),
"Alternative configuration file.")
("verbose,v",
"Verbose mode.")
; // End of options
}
void UserInterface::fill_file_options()
{
fill_data_input_options() ;
fill_input_options() ;
fill_log_options() ;
fill_positioning_options() ;
fill_output_options() ;
fill_misc_options() ;
}
void UserInterface::fill_data_input_options()
{
po::options_description options("Data input options") ;
options.add_options()
("data-input.cp-medium",
po::value< vector<string> >()->composing(),
"Medium from which capture points are read. You can specify this"
" option more than once. Allowed: CSV.")
("data-input.cp-csv-file",
po::value<string>(),
"CSV file to use for capture points input (when"
" data-input.cp-medium = CSV).")
("data-input.mobile-medium",
po::value< vector<string> >()->composing(),
"Medium from which mobiles are read. You can specify this"
" option more than once. Allowed: CSV.")
("data-input.mobile-csv-file",
po::value<string>(),
"CSV file to use for mobiles input (when"
" data-input.mobile-medium = CSV).")
("data-input.topology-medium",
po::value< vector<string> >()->composing(),
"Medium from which topology (buildings, areas and waypoints) is"
" read. You can specify this option more than once. Allowed: CSV.")
("data-input.areas-csv-file",
po::value<string>(),
"CSV file to use for topology input (when"
" data-input.topology-medium = CSV).")
("data-input.waypoints-csv-file",
po::value<string>(),
"CSV file to use for waypoints input (when"
" data-input.topology-medium = CSV).")
("data-input.reference-points-medium",
po::value< vector<string> >()->composing(),
"Medium from which reference points are read. You can specify this"
" option more than once. Allowed: CSV.")
("data-input.reference-points-csv-file",
po::value<string>(),
"CSV file to use for reference points input (when"
" data-input.reference-points-medium = CSV).")
;
file_options->add(options) ;
}
void UserInterface::fill_input_options()
{
po::options_description options("Input options") ;
options.add_options()
("input.medium,I",
po::value<string>(),
"Medium from which requests are read. Allowed: CSV, UDP.")
("input.csv-file",
po::value<string>(),
"CSV file to use for input (when input.medium = CSV).")
("input.udp-port",
po::value<int>()->default_value(OWL_DEFAULT_AGGREGATION_PORT),
"Port on which the UDP socket listens (when input.medium = UDP).")
;
file_options->add(options) ;
}
void UserInterface::fill_log_options()
{
po::options_description options("Log options") ;
options.add_options()
("log.medium,L",
po::value< vector<string> >()->composing(),
"Medium to which the requests will be logged. You can specify"
" this option more than once. Allowed: none, CSV. The `none`"
" value completely disables logging.")
("log.csv-file",
po::value<string>(),
"CSV file to use for logging (when log.medium = CSV).")
;
file_options->add(options) ;
}
void UserInterface::fill_positioning_options()
{
po::options_description options("Positioning options") ;
options.add_options()
("positioning.algorithm,a",
po::value< vector<string> >()->composing(),
"Algorithms used to compute positions. You can specify"
" this option more than once (but at least once). Allowed: Real,"
" FBCM, FRBHMBasic, InterlinkNetworks, NSS.")
("positioning.accept-new-mobiles",
po::value<bool>()->default_value(false),
"When receiving requests, add unknown mobiles (mobiles which are not"
" declared in the mobiles' configuration file) to the mobiles' list.")
("positioning.accept-new-cps",
po::value<bool>()->default_value(false),
"When receiving requests, add unknown CPs (CPs which are not"
" declared in the CPs' configuration file) to the CPs' list"
" (default is false, for security purposes).")
("positioning.update-cp-coordinates-online",
po::value<bool>()->default_value(false),
"Allow CP's coordinates to be updated when a calibration request"
" with new coordinates is received from the CP (default is false,"
" for security purposes).")
("positioning.area-start",
po::value<string>(),
"Coordinates of the first point of the deployment area; this is"
" used to delimit the area in which the MinMax trilateration"
" method tests points and in which the reference points are"
" generated, if the corresponding options are activated"
" (string format: \"X;Y;Z\").")
("positioning.area-stop",
po::value<string>(),
"Coordinates of the last point of the deployment area"
" (string format: \"X;Y;Z\").")
("positioning.smallest-ss",
po::value<int>()->default_value(DEFAULT_SMALLEST_SS),
"Smallest possible value for a received signal strength, in dBm.")
("positioning.ss-similarity",
po::value<string>()->default_value(DEFAULT_SS_SIMILARITY),
"Algorithm to calculate the similarity, in the signal strength space,"
" between two measurements. Allowed: mean, interval, interval2.")
("positioning.generate-reference-points",
po::value<string>()->default_value("false"),
"Generate reference points from the (auto)calibration requests"
" received. Can be 'false', 'mesh' to generate reference points"
" regularly according to the meshing grain specified and the"
" positioning area defined, 'line' to generate reference points"
" regularly according to a straight path (cf. options"
" generated-line-path and generated-line-step), 'list' to generate"
" reference points from a list (cf. option generated-points-list),"
" or a combination of the three previous options (separated by any"
" character) to generate points in several ways.")
("positioning.generate-multi-packet-reference-points",
po::value<bool>()->default_value(true),
"Generate several packets per reference point by trying to match the"
" packets in the real requests. If false, the SS values of the"
" requests are averaged and only one packet is generated.")
("positioning.generated-meshing-grain-x",
po::value<float>()->default_value(DEFAULT_MESHING_GRAIN),
"When generate-reference-points includes 'mesh', this distance (in"
" metres) will separate each point to the next in X.")
("positioning.generated-meshing-grain-y",
po::value<float>()->default_value(DEFAULT_MESHING_GRAIN),
"When generate-reference-points includes 'mesh', this distance (in"
" metres) will separate each point to the next in Y.")
("positioning.generated-meshing-grain-z",
po::value<float>()->default_value(DEFAULT_Z_MESHING_GRAIN),
"When generate-reference-points includes 'mesh', this parameter"
" represents the number of the floor. Currently, each increment of Y"
" is a new floor (full 3-D coordinates are not supported yet).")
("positioning.generated-line-path",
po::value<string>(),
"Path of reference points to generate, if generate-reference-points"
" includes 'line'; the reference points are generated in straight"
" lines between the given points, and one of the points must be the"
" coordinates of a CP (string format: \"(X1;Y1;Z1);(X2;Y2;Z2);...\""
" or \"X1;Y1;Z1;X2;Y2;Z2;...\").")
("positioning.generated-line-step",
po::value<float>()->default_value(DEFAULT_MESHING_GRAIN),
"When generate-reference-points includes 'line', two generated points"
" will be separated approximately by this distance (in metres).")
("positioning.generated-points-list",
po::value<string>(),
"List of reference points to generate, if generate-reference-points"
" includes 'list' (string format: \"(X1;Y1;Z1);(X2;Y2;Z2);...\" or"
" \"X1;Y1;Z1;X2;Y2;Z2;...\").")
("positioning.accept-new-calibration-requests",
po::value<bool>()->default_value(false),
"Add the calibration requests received during the run-time to"
" the calibration requests' list; this is required for the"
" self-calibration. If unactivated, the calibration requests"
" are handled as positioning requests (default is unactivated,"
" for security purposes).")
("positioning.calibration-requests-timeout",
po::value<unsigned int>()->default_value(0),
"Maximum age of a calibration request before to delete it"
" (0 = unlimited). See also the \"replay\" option.")
("positioning.unique-calibration-requests",
po::value<bool>()->default_value(true),
"Always delete existing former calibration requests when receiving"
" a new calibration request for an existing reference point.")
("positioning.position-calibration-requests",
po::value<bool>()->default_value(false),
"When accept-new-calibration-requests is activated, allow the"
" calibration requests to be positioned as normal requests."
" The default is to add them to the calibration requests' list"
" without position them.")
("positioning.nss.average-reference-points",
po::value<bool>()->default_value(false),
"With the NSS algorithm, for a given positioning request, average"
" all the calibration requests associated with a reference point"
" before to compute the SS similarity."
" The default is false, i.e the positioning request is compared"
" directly to each calibration request.")
("positioning.nss.ignore-cp-reference-points",
po::value<bool>()->default_value(false),
"With the NSS algorithm, try to avoid selecting the reference"
" points which are coordinates of a CP.")
("positioning.filter.max-speed",
po::value<float>()->default_value(0),
"Maximal speed at which the mobiles can move, in km/h (0 = unlimited"
" speed).")
("positioning.filter.cp-reset-distance",
po::value<float>()->default_value(0),
"Use max-speed-cp instead of max-speed when the computed position is"
" at this distance (in m) from a CP or closer (0 = never use"
" max-speed-cp).")
("positioning.filter.max-speed-cp",
po::value<float>()->default_value(0),
"Maximal (virtual) speed at which the mobiles can move when they are"
" within cp-reset-distance, in km/h (0 = unlimited speed, i.e."
" completely disables filtering when the mobile is close to a CP).")
;
file_options->add(options) ;
}
void UserInterface::fill_output_options()
{
po::options_description options("Output options") ;
options.add_options()
("output.2d-error",
po::value<bool>()->default_value(false),
"Compute the distance error in two dimensions instead of three"
" dimensions.")
("output.medium,O",
po::value< vector<string> >()->composing(),
"Medium to which the results will be written. You can specify"
" this option more than once."
" Allowed: Terminal, CSV, UDP, TCPEvAAL (EvAAL 2012 format)."
" If this option is absent, the results are printed on the terminal.")
("output.csv-file",
po::value<string>(),
"CSV file to use for output (when output.medium = CSV).")
("output.udp-host",
po::value<string>(),
"Host to which the UDP data is sent (when output.medium = UDP).")
("output.udp-port",
po::value<int>()->default_value(OWL_DEFAULT_RESULT_PORT),
"Port on which the UDP data is sent (when output.medium = UDP).")
("output.tcpevaal-host",
po::value<string>()->default_value(DEFAULT_TCPEVAAL_HOST),
"IP address of the host to which the TCP data is sent (when"
" output.medium = TCPEvAAL).")
("output.tcpevaal-port",
po::value<int>()->default_value(DEFAULT_TCPEVAAL_PORT),
"Port on which the TCP data is sent (when output.medium = TCPEvAAL).")
;
file_options->add(options) ;
}
void UserInterface::fill_misc_options()
{
po::options_description options("Miscellaneous options") ;
options.add_options()
("flush-output-files",
po::value<bool>()->default_value(true),
"Flush output text files after each line.")
("replay",
po::value<bool>()->default_value(false),
"\"Replay\" mode. With this option enabled, the current time is the"
" transmission timestamp of the most recent request; you will want"
" to enable this when reading inputs (requests) offline to replay"
" scenarios, if time-related options are enabled (e.g."
" \"positioning.calibration-requests-timeout\"). To be useful, this"
" option requires the listeners' clocks to be synchronised.")
;
file_options->add(options) ;
}
void UserInterface::parse_options()
{
parse_command_line() ;
print_information() ;
if (! owl_run)
return ;
parse_file() ;
po::notify(Configuration::getw_configuration()) ;
}
void UserInterface::parse_command_line() const
{
po::store(po::parse_command_line(cli_argument_count,
cli_argument_values,
*cli_options),
Configuration::getw_configuration()) ;
}
/**
* If information is printed, owl_run is set to false, to indicate that
* the program should stop.
*/
void UserInterface::print_information() const
{
// Print version if requested
if (Configuration::is_configured("version"))
{
cout
<< "This is OwlPS Positioner, part of the Owl"
<< " Positioning System project.\nVersion: " <<
#ifdef OWLPS_VERSION
OWLPS_VERSION
#else // OWLPS_VERSION
"unknown version"
#endif // OWLPS_VERSION
<< ".\n" ;
owl_run = false ; // Tell main() to exit
}
// Print usage if requested
if (Configuration::is_configured("help"))
{
cout << *cli_options ;
owl_run = false ; // Tell main() to exit
}
}
void UserInterface::parse_file() const
{
string config_file_name = Configuration::string_value("config-file") ;
ifstream config_file(config_file_name.c_str()) ;
if (! config_file)
{
cerr
<< "Warning! Error opening input configuration file \""
<< config_file_name
<< "\"! Using command line and default values..."
<< endl ;
return ;
}
po::store(po::parse_config_file(config_file, *file_options),
Configuration::getw_configuration()) ;
}