/* * 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 #include #include #include #include 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()->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 >()->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(), "CSV file to use for capture points input (when" " data-input.cp-medium = CSV).") ("data-input.mobile-medium", po::value< vector >()->composing(), "Medium from which mobiles are read. You can specify this" " option more than once. Allowed: CSV.") ("data-input.mobile-csv-file", po::value(), "CSV file to use for mobiles input (when" " data-input.mobile-medium = CSV).") ("data-input.topology-medium", po::value< vector >()->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(), "CSV file to use for topology input (when" " data-input.topology-medium = CSV).") ("data-input.waypoints-csv-file", po::value(), "CSV file to use for waypoints input (when" " data-input.topology-medium = CSV).") ("data-input.reference-points-medium", po::value< vector >()->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(), "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(), "Medium from which requests are read. Allowed: CSV, UDP.") ("input.csv-file", po::value(), "CSV file to use for input (when input.medium = CSV).") ("input.udp-port", po::value()->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 >()->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(), "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 >()->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()->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()->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()->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(), "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(), "Coordinates of the last point of the deployment area" " (string format: \"X;Y;Z\").") ("positioning.smallest-ss", po::value()->default_value(DEFAULT_SMALLEST_SS), "Smallest possible value for a received signal strength, in dBm.") ("positioning.ss-similarity", po::value()->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()->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()->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()->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()->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()->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(), "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()->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(), "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()->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()->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()->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()->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()->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()->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()->default_value(0), "Maximal speed at which the mobiles can move, in km/h (0 = unlimited" " speed).") ("positioning.filter.cp-reset-distance", po::value()->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()->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()->default_value(false), "Compute the distance error in two dimensions instead of three" " dimensions.") ("output.medium,O", po::value< vector >()->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(), "CSV file to use for output (when output.medium = CSV).") ("output.udp-host", po::value(), "Host to which the UDP data is sent (when output.medium = UDP).") ("output.udp-port", po::value()->default_value(OWL_DEFAULT_RESULT_PORT), "Port on which the UDP data is sent (when output.medium = UDP).") ("output.tcpevaal-host", po::value()->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()->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()->default_value(true), "Flush output text files after each line.") ("replay", po::value()->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()) ; }