892 lines
26 KiB
C
892 lines
26 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.
|
|
*
|
|
***********************************************************************
|
|
*
|
|
* This is the main source file of OwlPS Client, the program used to
|
|
* send positioning requests to the infrastructure.
|
|
*/
|
|
|
|
|
|
#include <owlps-client-config.h>
|
|
|
|
#include <owlps-client.h>
|
|
|
|
#ifdef OWLPS_CLIENT_RECEIVES_POSITION
|
|
# include <owlps-resultreader.h>
|
|
#endif // OWLPS_CLIENT_RECEIVES_POSITION
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
|
|
#include <net/if.h>
|
|
#include <rpc/types.h> // for MAXHOSTNAMELEN
|
|
|
|
|
|
/* Number of packets to send */
|
|
#define DEFAULT_NBPKT_CALIB 20 // 20 packets when calibrating
|
|
#define DEFAULT_NBPKT_NORMAL 10 // 10 packets when requesting the position
|
|
|
|
/* Delay between two packet transmissions (in milliseconds) */
|
|
#define DEFAULT_DELAY_CALIB 50 // Calibration request
|
|
#define DEFAULT_DELAY_NORMAL 25 // Localisation request
|
|
|
|
/* Delay between two requests in loop mode (in milliseconds) */
|
|
#define DEFAULT_FLOOD_DELAY 1000
|
|
|
|
/* Maximal size of a packet */
|
|
#define MAX_PKT_SIZE 1450u
|
|
|
|
/* Program arguments (getopt string) */
|
|
#define OPTIONS "DF::hi:I:l::n:N:p:qs:t:vV"
|
|
|
|
|
|
/* Function headers */
|
|
void parse_command_line(const int argc, char *const *argv) ;
|
|
void parse_main_options(const int argc, char *const *argv) ;
|
|
void check_destination_host(void) ;
|
|
void parse_calibration_data(const int argc, char *const *argv) ;
|
|
void check_configuration(void) ;
|
|
void print_configuration(void) ;
|
|
void create_socket(void) ;
|
|
void send_request(void) ;
|
|
void make_packet(void) ;
|
|
void add_padding(void) ;
|
|
uint_fast16_t initialise_common_fields(const uint_fast8_t packet_type) ;
|
|
uint_fast16_t initialise_calibration_fields(uint_fast16_t offset) ;
|
|
#ifdef OWLPS_CLIENT_RECEIVES_POSITION
|
|
int receive_position(void) ;
|
|
#endif // OWLPS_CLIENT_RECEIVES_POSITION
|
|
void print_usage(void) ;
|
|
void print_version(void) ;
|
|
|
|
|
|
/* Options */
|
|
struct {
|
|
bool daemon ;
|
|
bool verbose ;
|
|
char dest_host[MAXHOSTNAMELEN] ; // Destination host of the packets
|
|
uint_fast16_t dest_port ;
|
|
char iface[IFNAMSIZ + 1] ; // Source network interface
|
|
int_fast32_t delay ; // Time between two packet transmissions
|
|
uint_fast16_t nb_pkt ; // Number of packets to send
|
|
uint_fast16_t pkt_size ; // Size of the packet to send
|
|
int_fast32_t flood_delay ; // Time between two request transmissions
|
|
bool add_flood_delay ; // Add the delay to the transmission time?
|
|
uint_fast16_t nb_requests ; // Number of requests to send
|
|
uint_fast16_t listening_port ;
|
|
// Calibration data:
|
|
owl_direction direction ;
|
|
float x ;
|
|
float y ;
|
|
float z ;
|
|
} options = {
|
|
false, // daemon
|
|
true, // verbose
|
|
"", // dest_host
|
|
OWL_DEFAULT_REQUEST_PORT, // dest_port
|
|
"", // iface
|
|
-1, // delay
|
|
0, // nb_pkt
|
|
0, // pkt_size
|
|
-1, // flood_delay
|
|
false, // add_flood_delay
|
|
0, // nb_requests
|
|
0, // listening_port
|
|
0, 0, 0, 0 // Calibration data
|
|
} ;
|
|
|
|
char *program_name = NULL ;
|
|
|
|
// true if the packet is a calibration request, false if it is
|
|
// a simple positioning request:
|
|
bool is_calibration_request = false ;
|
|
|
|
int sockfd ; // Sending socket descriptor
|
|
struct sockaddr server ; // Server info
|
|
uint8_t *packet = NULL ; // Packet to send
|
|
uint_fast16_t packet_size ; // Packet size
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct sigaction action ; // Signal handler structure
|
|
int ret = 0 ;
|
|
// Number of requests we still have to transmit:
|
|
uint_fast16_t nb_requests_left = 1 ;
|
|
owl_timestamp start_time ; // Time of the transmission's start
|
|
|
|
owl_run = true ;
|
|
|
|
program_name = argv[0] ;
|
|
parse_command_line(argc, argv) ;
|
|
|
|
if (options.daemon)
|
|
{
|
|
if (options.verbose)
|
|
fprintf(stderr, "Detaching to background...\n") ;
|
|
if (daemon(0, 0))
|
|
perror("Cannot daemonize") ;
|
|
}
|
|
|
|
/* Set up signal handlers */
|
|
action.sa_flags = 0 ;
|
|
sigemptyset(&action.sa_mask) ;
|
|
action.sa_handler = owl_sigint_handler ;
|
|
sigaction(SIGINT, &action, NULL) ;
|
|
action.sa_handler = owl_sigterm_handler ;
|
|
sigaction(SIGTERM, &action, NULL) ;
|
|
|
|
create_socket() ;
|
|
|
|
/* Transmit the first request */
|
|
if (! options.add_flood_delay)
|
|
owl_timestamp_now(&start_time) ;
|
|
send_request() ;
|
|
if (options.nb_requests)
|
|
nb_requests_left = options.nb_requests - 1 ;
|
|
|
|
/* Transmit the next requests, if any */
|
|
while (owl_run && options.flood_delay >= 0 && nb_requests_left > 0)
|
|
{
|
|
uint_fast32_t trx_time, sleep_time ;
|
|
owl_timestamp now ;
|
|
|
|
if (options.add_flood_delay)
|
|
owl_msleep(options.flood_delay) ;
|
|
else
|
|
{
|
|
owl_timestamp_now(&now) ;
|
|
trx_time = owl_time_elapsed_ms(&start_time, &now) ;
|
|
printf("Transmission time was %"PRIuFAST32" ms", trx_time) ;
|
|
|
|
// Sleep only if the sleep delay is greater than the
|
|
// transmission time
|
|
if (trx_time < (uint_fast32_t) options.flood_delay)
|
|
{
|
|
sleep_time = options.flood_delay - trx_time ;
|
|
printf(", sleeping for %"PRIuFAST32" ms...\n", sleep_time) ;
|
|
owl_msleep(sleep_time) ;
|
|
}
|
|
else
|
|
printf(" > %"PRIdFAST32" ms, no sleeping required.\n",
|
|
options.flood_delay) ;
|
|
|
|
owl_timestamp_now(&start_time) ;
|
|
}
|
|
|
|
if (owl_run) // owl_run can have been set to false during the sleep
|
|
{
|
|
send_request() ;
|
|
if (options.nb_requests)
|
|
--nb_requests_left ;
|
|
}
|
|
}
|
|
|
|
close(sockfd) ;
|
|
|
|
#ifdef OWLPS_CLIENT_RECEIVES_POSITION
|
|
if (options.listening_port > 0)
|
|
ret = receive_position() ;
|
|
#endif // OWLPS_CLIENT_RECEIVES_POSITION
|
|
|
|
return ret ;
|
|
}
|
|
|
|
|
|
|
|
void parse_command_line(const int argc, char *const *argv)
|
|
{
|
|
parse_main_options(argc, argv) ;
|
|
check_destination_host() ;
|
|
parse_calibration_data(argc, argv) ;
|
|
check_configuration() ;
|
|
|
|
if (options.verbose)
|
|
print_configuration() ;
|
|
}
|
|
|
|
|
|
|
|
void parse_main_options(const int argc, char *const *argv)
|
|
{
|
|
int opt ;
|
|
long arg_long ; // Integer value of optarg
|
|
unsigned long arg_ulong ; // Unsigned integer value of optarg
|
|
char *endptr ; // Return value of strto*()
|
|
|
|
while ((opt = getopt(argc, argv, OPTIONS)) != -1)
|
|
{
|
|
switch (opt)
|
|
{
|
|
case 'D' :
|
|
options.daemon = true ;
|
|
break ;
|
|
case 'F' :
|
|
/* If -F is present, first set the flood delay to its default
|
|
* value (this can appear suboptimal when the user supplies a
|
|
* value, but will simplify the operations).
|
|
*/
|
|
options.flood_delay = DEFAULT_FLOOD_DELAY ;
|
|
|
|
/* Facultative getopt options do not handle separated values
|
|
* (like -F <delay>), so we have to test separately.
|
|
*/
|
|
if (optarg) // We got an option like -F<delay>, it's OK
|
|
{
|
|
arg_long = strtol(optarg, &endptr, 10) ;
|
|
if (endptr != optarg)
|
|
options.flood_delay = arg_long ;
|
|
else
|
|
fprintf(stderr, "Warning! Bad flood_delay:"
|
|
" failing back to the default value.\n") ;
|
|
if (optarg[0] == '+')
|
|
options.add_flood_delay = true ;
|
|
}
|
|
|
|
else // We got -F alone or -F <delay>
|
|
{
|
|
/* If we are not at the end of the string and the next
|
|
* optind is not an option, we have the option with a
|
|
* separate argument. Otherwise we got the option alone,
|
|
* but we have nothing to do since we already set the
|
|
* default value.
|
|
*/
|
|
if (argv[optind] != NULL && argv[optind][0] != '-')
|
|
{
|
|
// Take the optind value:
|
|
arg_long = strtol(argv[optind], &endptr, 10) ;
|
|
if (endptr != argv[optind])
|
|
options.flood_delay = arg_long ;
|
|
else
|
|
fprintf(stderr, "Warning! Bad flood_delay:"
|
|
" failing back to the default value.\n") ;
|
|
if (argv[optind][0] == '+')
|
|
options.add_flood_delay = true ;
|
|
++optind ;
|
|
}
|
|
}
|
|
break ;
|
|
case 'h' :
|
|
print_usage() ;
|
|
exit(0) ;
|
|
case 'I' :
|
|
strncpy(options.iface, optarg, IFNAMSIZ + 1) ;
|
|
break ;
|
|
case 'i' :
|
|
strncpy(options.dest_host, optarg, MAXHOSTNAMELEN) ;
|
|
break ;
|
|
case 'l' :
|
|
#ifdef OWLPS_CLIENT_RECEIVES_POSITION
|
|
/* If -l is present, first set the listening port to its
|
|
* default value (this can appear suboptimal when the user
|
|
* supplies a port number, but will simplify the operations).
|
|
*/
|
|
options.listening_port = OWL_DEFAULT_RESULT_PORT ;
|
|
|
|
/* Facultative getopt options do not handle separated values
|
|
* (like -l <port>), so we have to test separately.
|
|
*/
|
|
if (optarg) // We got an option like -l<port>, it's OK
|
|
{
|
|
arg_ulong = strtoul(optarg, &endptr, 10) ;
|
|
if (endptr != optarg)
|
|
options.listening_port = arg_ulong ;
|
|
else
|
|
fprintf(stderr, "Warning! Bad listening_port:"
|
|
" failing back to the default value.\n") ;
|
|
}
|
|
|
|
else // We got -l alone or -l <port>
|
|
{
|
|
/* If we are not at the end of the string and the next
|
|
* optind is not an option, we have the option with a
|
|
* separate argument. Otherwise we got the option alone,
|
|
* but we have nothing to do since we already set the
|
|
* default value.
|
|
*/
|
|
if (argv[optind] != NULL && argv[optind][0] != '-')
|
|
{
|
|
// Take the optind value:
|
|
arg_ulong = strtoul(argv[optind], &endptr, 10) ;
|
|
if (endptr != argv[optind])
|
|
options.listening_port = arg_ulong ;
|
|
else
|
|
fprintf(stderr, "Warning! Bad listening_port:"
|
|
" failing back to the default value.\n") ;
|
|
++optind ;
|
|
}
|
|
}
|
|
#else // OWLPS_CLIENT_RECEIVES_POSITION
|
|
fprintf(stderr, "Warning! The program was compiled without"
|
|
" enabling the -l option (receive the position from"
|
|
" the positioning server).\n") ;
|
|
#endif // OWLPS_CLIENT_RECEIVES_POSITION
|
|
break ;
|
|
case 'n' :
|
|
options.nb_pkt = strtoul(optarg, NULL, 10) ;
|
|
break ;
|
|
case 'N' :
|
|
options.nb_requests = strtoul(optarg, NULL, 10) ;
|
|
break ;
|
|
case 'p' :
|
|
options.dest_port = strtoul(optarg, NULL, 10) ;
|
|
break ;
|
|
case 'q' :
|
|
options.verbose = false ;
|
|
break ;
|
|
case 's' :
|
|
options.pkt_size = strtoul(optarg, NULL, 10) ;
|
|
break ;
|
|
case 't' :
|
|
arg_long = strtol(optarg, &endptr, 10) ;
|
|
if (endptr != optarg)
|
|
options.delay = arg_long ;
|
|
else
|
|
fprintf(stderr, "Warning! Bad delay:"
|
|
" failing back to the default value.\n") ;
|
|
break ;
|
|
case 'v' :
|
|
options.verbose = true ;
|
|
break ;
|
|
case 'V' :
|
|
print_version() ;
|
|
exit(0) ;
|
|
default :
|
|
print_usage() ;
|
|
exit(OWL_ERR_BAD_USAGE) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void check_destination_host()
|
|
{
|
|
/* Check if we got a destination host */
|
|
if (options.dest_host[0] == '\0')
|
|
{
|
|
fprintf(stderr, "Error! You must specify a destination host"
|
|
" (-i) .\n") ;
|
|
print_usage() ;
|
|
exit(OWL_ERR_BAD_USAGE) ;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Parses remaining arguments (possible calibration data) */
|
|
void parse_calibration_data(const int argc, char *const *argv)
|
|
{
|
|
/* No more arguments to parse */
|
|
if (argc - optind == 0)
|
|
return ;
|
|
|
|
/* Exactly 4 more arguments */
|
|
if (argc - optind == 4)
|
|
{
|
|
char *endptr ;
|
|
|
|
is_calibration_request = true ;
|
|
|
|
options.direction = strtoul(argv[optind], &endptr, 10) ;
|
|
if (endptr == argv[optind])
|
|
{
|
|
fprintf(stderr,
|
|
"Error in calibration data: wrong direction!\n") ;
|
|
goto error ;
|
|
}
|
|
optind++ ;
|
|
|
|
options.x = strtod(argv[optind], &endptr) ;
|
|
if (endptr == argv[optind])
|
|
{
|
|
fprintf(stderr,
|
|
"Error in calibration data: wrong X coordinate!\n") ;
|
|
goto error ;
|
|
}
|
|
optind++ ;
|
|
|
|
options.y = strtod(argv[optind], &endptr) ;
|
|
if (endptr == argv[optind])
|
|
{
|
|
fprintf(stderr,
|
|
"Error in calibration data: wrong Y coordinate!\n") ;
|
|
goto error ;
|
|
}
|
|
optind++ ;
|
|
|
|
options.z = strtod(argv[optind], &endptr) ;
|
|
if (endptr == argv[optind])
|
|
{
|
|
fprintf(stderr,
|
|
"Error in calibration data: wrong Z coordinate!\n") ;
|
|
goto error ;
|
|
}
|
|
|
|
return ; // No error occurred
|
|
}
|
|
|
|
/* Bad number of arguments or parse error */
|
|
error:
|
|
print_usage() ;
|
|
exit(OWL_ERR_BAD_USAGE) ;
|
|
}
|
|
|
|
|
|
|
|
void check_configuration()
|
|
{
|
|
// Delay not specified (or bad delay):
|
|
if (options.delay < 0)
|
|
{
|
|
if (options.verbose)
|
|
fprintf(stderr,
|
|
"Warning! delay: failing back to default value.\n") ;
|
|
if (is_calibration_request)
|
|
options.delay = DEFAULT_DELAY_CALIB ;
|
|
else
|
|
options.delay = DEFAULT_DELAY_NORMAL ;
|
|
}
|
|
|
|
// Number of packet not specified (or bad number)
|
|
if (options.nb_pkt < 1)
|
|
{
|
|
if (options.verbose)
|
|
fprintf(stderr,
|
|
"Warning! nb_pkt: failing back to default value.\n") ;
|
|
if (is_calibration_request)
|
|
options.nb_pkt = DEFAULT_NBPKT_CALIB ;
|
|
else
|
|
options.nb_pkt = DEFAULT_NBPKT_NORMAL ;
|
|
}
|
|
|
|
// Packet size too big
|
|
if (options.pkt_size > MAX_PKT_SIZE)
|
|
{
|
|
if (options.verbose)
|
|
fprintf(stderr,
|
|
"Warning! pkt_size cannot be greater than %d bytes:"
|
|
" failing back to %d.\n", MAX_PKT_SIZE, MAX_PKT_SIZE) ;
|
|
options.pkt_size = MAX_PKT_SIZE ;
|
|
}
|
|
|
|
// Calibration request but bad direction
|
|
if (is_calibration_request)
|
|
if (options.direction < OWL_DIRECTION_MIN ||
|
|
options.direction > OWL_DIRECTION_MAX)
|
|
{
|
|
fprintf(stderr, "Error! \"%"PRIu8"\" is not a valid"
|
|
" direction.\n", options.direction) ;
|
|
exit(OWL_ERR_BAD_USAGE) ;
|
|
}
|
|
|
|
// Check port numbers
|
|
if (options.dest_port < 1 || options.dest_port > 65535)
|
|
{
|
|
fprintf(stderr, "Warning! Bad dest_port:"
|
|
" failing back to default value.\n") ;
|
|
options.dest_port = OWL_DEFAULT_REQUEST_PORT ;
|
|
}
|
|
if (options.listening_port > 65535)
|
|
{
|
|
fprintf(stderr, "Warning! listening_port too high: ignored.\n") ;
|
|
options.listening_port = 0 ;
|
|
}
|
|
|
|
// We want to send a calibration request AND to be located, which is
|
|
// not allowed:
|
|
if (is_calibration_request && options.listening_port > 0)
|
|
{
|
|
fprintf(stderr, "Warning! You cannot wait for a server answer when"
|
|
" you calibrate. Option -l ignored.\n") ;
|
|
options.listening_port = 0 ;
|
|
}
|
|
|
|
if (options.flood_delay >= 0)
|
|
{
|
|
// We want to flood AND to be located, which is not allowed:
|
|
if (options.listening_port > 0)
|
|
{
|
|
fprintf(stderr, "Warning! You cannot wait for a server answer"
|
|
" when you flood. Option -l ignored.\n") ;
|
|
options.listening_port = 0 ;
|
|
}
|
|
}
|
|
else // Flood is unactivated
|
|
{
|
|
if (options.nb_requests)
|
|
{
|
|
fprintf(stderr, "Warning! The -N option can be used only along"
|
|
" with -F. Option -N ignored.\n") ;
|
|
options.nb_requests = 0 ;
|
|
}
|
|
if (options.daemon)
|
|
{
|
|
fprintf(stderr, "Warning! It is useless to detach from"
|
|
" the foreground if the flood mode is not activated"
|
|
" Option -D ignored.\n") ;
|
|
options.daemon = false ;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void print_configuration()
|
|
{
|
|
printf("Options:\n"
|
|
"\tDaemon: %s\n"
|
|
"\tVerbose: %s\n"
|
|
"\tDestination host: %s\n"
|
|
"\tDestination port: %"PRIuFAST16"\n"
|
|
"\tInterface: %s\n"
|
|
"\tDelay (ms): %"PRIdFAST32"\n"
|
|
"\tNumber of packets: %"PRIuFAST16"\n"
|
|
"\tPacket size: %"PRIuFAST16"\n"
|
|
"\tFlood delay (ms): %"PRIdFAST32"\n"
|
|
"\tCumulative flood delay: %s\n"
|
|
"\tNumber of requests: %"PRIuFAST16"\n"
|
|
"\tListening port: %"PRIuFAST16"\n"
|
|
"\tDirection: %"PRIu8"\n"
|
|
"\tX: %f\n"
|
|
"\tY: %f\n"
|
|
"\tZ: %f\n"
|
|
,
|
|
OWL_BOOL_TO_STRING(options.daemon),
|
|
OWL_BOOL_TO_STRING(options.verbose),
|
|
options.dest_host,
|
|
options.dest_port,
|
|
options.iface,
|
|
options.delay,
|
|
options.nb_pkt,
|
|
options.pkt_size,
|
|
options.flood_delay,
|
|
OWL_BOOL_TO_STRING(options.add_flood_delay),
|
|
options.nb_requests,
|
|
options.listening_port,
|
|
options.direction,
|
|
options.x,
|
|
options.y,
|
|
options.z
|
|
) ;
|
|
}
|
|
|
|
|
|
|
|
void create_socket()
|
|
{
|
|
sockfd =
|
|
owl_create_trx_socket(options.dest_host, options.dest_port,
|
|
&server, options.iface) ;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Prepares a new request and sends it.
|
|
*/
|
|
void send_request()
|
|
{
|
|
make_packet() ;
|
|
owl_send_request(sockfd, &server, packet, packet_size,
|
|
options.nb_pkt, options.delay, options.verbose) ;
|
|
free(packet) ;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Creates the packet to send.
|
|
*/
|
|
void make_packet()
|
|
{
|
|
uint_fast16_t offset ; // Index used to create the packet
|
|
|
|
if (is_calibration_request) // Calibration packet
|
|
{
|
|
if (options.verbose)
|
|
printf("\nPreparing calibration request packet...\n") ;
|
|
|
|
packet_size =
|
|
sizeof(uint8_t) * 2 + sizeof(owl_timestamp) + sizeof(float) * 3 +
|
|
sizeof(uint16_t) * 2 ;
|
|
add_padding() ;
|
|
packet = malloc(packet_size) ;
|
|
if (! packet)
|
|
{
|
|
perror("Cannot allocate memory") ;
|
|
abort() ;
|
|
}
|
|
|
|
offset = initialise_common_fields(OWL_REQUEST_CALIBRATION) ;
|
|
offset += initialise_calibration_fields(offset) ;
|
|
}
|
|
|
|
else // Standard packet
|
|
{
|
|
if (options.verbose)
|
|
printf("\nPreparing request packet...\n") ;
|
|
|
|
packet_size =
|
|
sizeof(uint8_t) + sizeof(owl_timestamp) + sizeof(uint16_t) * 2 ;
|
|
add_padding() ;
|
|
packet = malloc(packet_size) ;
|
|
if (! packet)
|
|
{
|
|
perror("Cannot allocate memory") ;
|
|
abort() ;
|
|
}
|
|
|
|
offset = initialise_common_fields(OWL_REQUEST_NORMAL) ;
|
|
}
|
|
|
|
// Initialize padding bytes with 0xFF:
|
|
while (offset < packet_size)
|
|
packet[offset++] = 0xFF ;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Increases the packet size to add padding.
|
|
*/
|
|
void add_padding()
|
|
{
|
|
if (options.pkt_size > packet_size)
|
|
packet_size = options.pkt_size ;
|
|
|
|
if (options.verbose)
|
|
printf("Packet size: %"PRIuFAST16"\n", packet_size) ;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Initialises the fields of a normal positioning request.
|
|
*/
|
|
uint_fast16_t initialise_common_fields(const uint_fast8_t packet_type)
|
|
{
|
|
uint_fast16_t offset = 0 ;
|
|
uint16_t npkt ;
|
|
owl_timestamp request_time ;
|
|
char request_time_str[OWL_TIMESTAMP_STRLEN] ;
|
|
|
|
// Get the current time and copy it as a string before to switch it to
|
|
// network endianness:
|
|
owl_timestamp_now(&request_time) ;
|
|
owl_timestamp_to_string(&request_time, request_time_str) ;
|
|
owl_hton_timestamp(&request_time) ;
|
|
|
|
// Packet type:
|
|
memset(&packet[offset++], packet_type, 1) ;
|
|
|
|
// Number of the current packet (1 for the first):
|
|
npkt = htons(1u) ;
|
|
memcpy(&packet[offset], &npkt, sizeof(uint16_t)) ;
|
|
offset += sizeof(uint16_t) ;
|
|
|
|
// Number of packets:
|
|
npkt = htons(options.nb_pkt) ;
|
|
memcpy(&packet[offset], &npkt, sizeof(uint16_t)) ;
|
|
offset += sizeof(uint16_t) ;
|
|
|
|
// Request time:
|
|
memcpy(&packet[offset], &request_time, sizeof(request_time)) ;
|
|
offset += sizeof(request_time) ;
|
|
|
|
if (options.verbose)
|
|
printf("Packet timestamp: %s\n", request_time_str) ;
|
|
|
|
return offset ;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Initialises the calibration data fields.
|
|
*/
|
|
uint_fast16_t initialise_calibration_fields(uint_fast16_t offset)
|
|
{
|
|
float x, y, z ;
|
|
|
|
// Direction:
|
|
packet[offset++] = options.direction ;
|
|
|
|
if (options.verbose)
|
|
printf("Direction = %d, X = %f, Y = %f, Z = %f\n",
|
|
packet[offset - 1], options.x, options.y, options.z) ;
|
|
|
|
// Convert the coordinates to the network endianness:
|
|
x = owl_htonf(options.x) ;
|
|
y = owl_htonf(options.y) ;
|
|
z = owl_htonf(options.z) ;
|
|
|
|
// Copy the coordinates to the packet:
|
|
memcpy(&packet[offset], &x, sizeof(float)) ;
|
|
offset += sizeof(float) ;
|
|
memcpy(&packet[offset], &y, sizeof(float)) ;
|
|
offset += sizeof(float) ;
|
|
memcpy(&packet[offset], &z, sizeof(float)) ;
|
|
offset += sizeof(float) ;
|
|
|
|
return offset ;
|
|
}
|
|
|
|
|
|
|
|
#ifdef OWLPS_CLIENT_RECEIVES_POSITION
|
|
/*
|
|
* Receives a position computed by the infrastructure.
|
|
* Note that it is currently not guaranteed that the received result
|
|
* correspond to the request sent.
|
|
* Returns 0, or a non-zero value in case of error.
|
|
*/
|
|
int receive_position()
|
|
{
|
|
owl_result *result ;
|
|
|
|
printf("Waiting for the result from the infrastructure...\n") ;
|
|
|
|
sockfd = owl_create_udp_listening_socket(options.listening_port) ;
|
|
if (sockfd < 0)
|
|
return OWL_ERR_SOCKET_CREATE ;
|
|
|
|
result = owl_receive_position(sockfd) ;
|
|
if (result == NULL)
|
|
return OWL_ERR_SOCKET_RECV ;
|
|
|
|
close(sockfd) ;
|
|
owl_print_result(result) ;
|
|
owl_free_result(result) ;
|
|
|
|
return 0 ;
|
|
}
|
|
#endif // OWLPS_CLIENT_RECEIVES_POSITION
|
|
|
|
|
|
|
|
/*
|
|
* Prints the usage message on the standard output.
|
|
*
|
|
* /!\ Don't forget to update the documentation when modifying something
|
|
* here!
|
|
*/
|
|
void print_usage()
|
|
{
|
|
printf("Usage:\n"
|
|
"Localisation request:\n"
|
|
"\t%s"
|
|
" [-v | -q]"
|
|
" -i dest_host"
|
|
" [-p dest_port]"
|
|
" [-I iface]"
|
|
" [-t delay]"
|
|
"\n\t"
|
|
" [-n nb_packets]"
|
|
" [-s packet_size]"
|
|
" [-F [[+]delay] [-N nb_requests] [-D]]"
|
|
"\n\t"
|
|
" [-l [port]]\n"
|
|
"Calibration request:\n"
|
|
"\t%s"
|
|
" [-v | -q]"
|
|
" -i dest_host"
|
|
" [-p dest_port]"
|
|
" [-I iface]"
|
|
" [-t delay]"
|
|
"\n\t"
|
|
" [-n nb_packets]"
|
|
" [-s packet_size]"
|
|
" [-F [[+]delay] [-N nb_requests] [-D]]"
|
|
"\n\t"
|
|
" direction x y z\n"
|
|
|
|
"Options:\n"
|
|
"\t-h\t\tPrint this help.\n"
|
|
"\t-V\t\tPrint version information.\n"
|
|
"\t-v\t\tTurn on verbose mode (default).\n"
|
|
"\t-q\t\tDo not print informational messages and some (less\n"
|
|
"\t\t\timportant) warnings.\n"
|
|
"\t-i dest_host\tName or IP address of the destination host of"
|
|
" the\n\t\t\tlocalisation request.\n"
|
|
"\t-p dest_port\tDestination port of the localisation request"
|
|
" (default:\n\t\t\t%d).\n"
|
|
"\t-t delay\tTime between each packet transmission in"
|
|
" milliseconds\n\t\t\t(default: %d ms for a normal request,"
|
|
" %d ms for a\n\t\t\tcalibration request).\n"
|
|
"\t-n nb_packets\tNumber of packet transmitted for the request"
|
|
" (default:\n\t\t\t%d for a normal request, %d for a"
|
|
" calibration request).\n"
|
|
"\t-s packet_size\tData size of the transmitted packets. The"
|
|
" minimal value\n\t\t\tis the size of the request's data"
|
|
" fields; if\n\t\t\t<packet_size> is less than this size, it is"
|
|
" ignored.\n\t\t\tNote that this size does not take into"
|
|
" account the\n\t\t\theaders, so the whole 802.11 frame will be"
|
|
" bigger.\n"
|
|
"\t-I iface\tName of the network interface used to transmit the"
|
|
"\n\t\t\trequest (e.g. \"eth2\"). If this option is absent, the"
|
|
"\n\t\t\tinterface is selected automatically. You must be root"
|
|
" to\n\t\t\tuse this option.\n"
|
|
"\t-F [delay]\t\"Flood mode\": loop indefinitely, sending a"
|
|
" new request\n\t\t\tevery <delay> milliseconds (default:"
|
|
" %d ms). If\n\t\t\t<delay> starts with a +, it is the time"
|
|
" between two\n\t\t\trequests instead of the time between the"
|
|
" start of the\n\t\t\ttransmission of two requests.\n"
|
|
"\t-N nb_requests\tWith -F, stop after <nb_requests> requests"
|
|
" transmitted\n\t\t\tinstead of looping indefinitely.\n"
|
|
"\t-D\t\tDaemon mode. Useful only in flood mode.\n"
|
|
"\t-l [port]\tWait for the computed position and display it."
|
|
" The\n\t\t\toptional argument <port> allows to specify the"
|
|
" listening\n\t\t\tport (default: %d). Available only if the"
|
|
" program was\n\t\t\tcompiled with the compilation-time option"
|
|
"\n\t\t\tOWLPS_CLIENT_RECEIVES_POSITION.\n"
|
|
,
|
|
program_name,
|
|
program_name,
|
|
OWL_DEFAULT_REQUEST_PORT,
|
|
DEFAULT_DELAY_NORMAL,
|
|
DEFAULT_DELAY_CALIB,
|
|
DEFAULT_NBPKT_NORMAL,
|
|
DEFAULT_NBPKT_CALIB,
|
|
DEFAULT_FLOOD_DELAY,
|
|
OWL_DEFAULT_RESULT_PORT
|
|
) ;
|
|
}
|
|
|
|
|
|
void print_version()
|
|
{
|
|
printf("This is OwlPS Client, part of the Owl Positioning System"
|
|
" project.\n"
|
|
"Version: %s.\n"
|
|
"Compilation-time options:\n"
|
|
"\tOption -l: %s.\n",
|
|
#ifdef OWLPS_VERSION
|
|
OWLPS_VERSION
|
|
#else // OWLPS_VERSION
|
|
"unknown version"
|
|
#endif // OWLPS_VERSION
|
|
,
|
|
#ifdef OWLPS_CLIENT_RECEIVES_POSITION
|
|
"YES"
|
|
#else // OWLPS_CLIENT_RECEIVES_POSITION
|
|
"NO"
|
|
#endif // OWLPS_CLIENT_RECEIVES_POSITION
|
|
) ;
|
|
}
|