/* * This file is part of the rtap localisation project. */ #include #include #include #include #include #include #include #include #include /* 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 /* Program arguments (getopt string) */ #define OPTIONS "F::hi:I:l::n:p:t:V" /* Function headers */ void parse_command_line(int argc, char **argv) ; void parse_main_options(int argc, char **argv) ; void check_destination_ip(void) ; void parse_calibration_data(int argc, char **argv) ; void check_configuration(void) ; #ifdef DEBUG void print_configuration(void) ; #endif // DEBUG void create_socket(void) ; void make_packet(void) ; void send_request(void) ; int receive_position(void) ; void print_usage(void) ; void print_version(void) ; /* Options */ struct { char dest_ip[INET_ADDRSTRLEN] ; // Destination IP 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 int_fast32_t flood_delay ; // Time between two request transmissions uint_fast16_t listening_port ; // Calibration data: owl_direction direction ; float x ; float y ; float z ; } options = { "", OWL_DEFAULT_REQUEST_PORT, "", -1, 0, -1, 0, 0, 0, 0, 0 } ; char *program_name = NULL ; // owl_true if the packet is a calibration request, owl_false if it is // a simple positioning request: owl_bool is_calibration_request = owl_false ; int sockfd ; // Sending socket descriptor struct sockaddr_in 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 ; program_name = argv[0] ; parse_command_line(argc, argv) ; /* 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() ; send_request() ; while (owl_run && options.flood_delay >= 0) { owl_msleep(options.flood_delay) ; if (owl_run) // owl_run can have been set to false during the sleep send_request() ; } close(sockfd) ; if (options.listening_port > 0) ret = receive_position() ; return ret ; } void parse_command_line(int argc, char **argv) { parse_main_options(argc, argv) ; check_destination_ip() ; parse_calibration_data(argc, argv) ; check_configuration() ; #ifdef DEBUG print_configuration() ; #endif // DEBUG } void parse_main_options(int argc, char **argv) { int opt ; while ((opt = getopt(argc, argv, OPTIONS)) != -1) { switch (opt) { case 'F' : /* Facultative getopt options do not handle separated values * (like -F ), so we have to test separately. */ if (optarg) // We got an option like -F, it's OK options.flood_delay = strtoul(optarg, NULL, 0) ; else // We got -F alone or -F { /* If we are at the end of the string, or the next optind * is an option, we have the option without argument */ if (argv[optind] == NULL || argv[optind][0] == '-') // Take the default value: options.flood_delay = DEFAULT_FLOOD_DELAY ; else { // Take the optind value: options.flood_delay = strtoul(argv[optind], NULL, 0) ; ++optind ; } } break ; case 'h' : print_usage() ; exit(0) ; case 'I' : strncpy(options.iface, optarg, IFNAMSIZ + 1) ; break ; case 'i' : strncpy(options.dest_ip, optarg, INET_ADDRSTRLEN) ; break ; case 'l' : /* Facultative getopt options do not handle separated values * (like -l ), so we have to test separately. */ if (optarg) // We got an option like -l, it's OK options.listening_port = strtoul(optarg, NULL, 0) ; else // We got -l alone or -l { /* If we are at the end of the string, or the next optind * is an option, we have -l without a port number */ if (argv[optind] == NULL || argv[optind][0] == '-') // Take the default value: options.listening_port = OWL_DEFAULT_RESULT_PORT ; else { // Take the optind value: options.listening_port = strtoul(argv[optind], NULL, 0) ; ++optind ; } } break ; case 'n' : options.nb_pkt = strtoul(optarg, NULL, 0) ; break ; case 'p' : options.dest_port = strtoul(optarg, NULL, 0) ; break ; case 't' : options.delay = strtol(optarg, NULL, 0) ; break ; case 'V' : print_version() ; exit(0) ; default : print_usage() ; exit(OWL_ERR_BAD_USAGE) ; } } } void check_destination_ip() { /* Check if we got a destination IP address */ if (options.dest_ip[0] == '\0') { fprintf(stderr, "Error! You must specify a destination IP address" " (-i) .\n") ; print_usage() ; exit(OWL_ERR_BAD_USAGE) ; } } void parse_calibration_data(int argc, char **argv) { /* Parse remaining arguments (possible calibration data) */ if (argc - optind != 0) { if (argc - optind == 4) { is_calibration_request = owl_true ; options.direction = strtoul(argv[optind++], NULL, 0) ; options.x = strtod(argv[optind++], NULL) ; options.y = strtod(argv[optind++], NULL) ; options.z = strtod(argv[optind], NULL) ; } else // Bad number of arguments { print_usage() ; exit(OWL_ERR_BAD_USAGE) ; } } } void check_configuration() { // Delay not specified (or bad delay): if (options.delay < 0) { #ifdef DEBUG fprintf(stderr, "Warning! delay: failing back to default value.\n") ; #endif // DEBUG 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) { #ifdef DEBUG fprintf(stderr, "Warning! nb_pkt: failing back to default value.\n") ; #endif // DEBUG if (is_calibration_request) options.nb_pkt = DEFAULT_NBPKT_CALIB ; else options.nb_pkt = DEFAULT_NBPKT_NORMAL ; } // 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) { #ifdef DEBUG fprintf(stderr, "Warning! Bad dest_port:" " failing back to default value.\n") ; options.dest_port = OWL_DEFAULT_REQUEST_PORT ; #endif // DEBUG } if (options.listening_port > 65535) { #ifdef DEBUG fprintf(stderr, "Warning! listening_port too high: ignored.\n") ; options.listening_port = 0 ; #endif // DEBUG } // We want to send a calibration request AND to be located, which is // not allowed: if (is_calibration_request && options.listening_port > 0) { #ifdef DEBUG fprintf(stderr, "Warning! You cannot wait for a server answer when" " you calibrate. Option -l ignored…\n") ; #endif // DEBUG options.listening_port = 0 ; } // We want to flood AND to be located, which is not allowed: if (options.flood_delay >= 0 && options.listening_port > 0) { #ifdef DEBUG fprintf(stderr, "Warning! You cannot wait for a server answer when" " you flood. Option -l ignored…\n") ; #endif // DEBUG options.listening_port = 0 ; } } #ifdef DEBUG void print_configuration() { fprintf(stderr, "Options:\n" "\tDestination IP: %s\n" "\tDestination port: %"PRIuFAST16"\n" "\tInterface: %s\n" "\tDelay (ms): %"PRIdFAST32"\n" "\tNumber of packets: %"PRIuFAST16"\n" "\tFlood delay (ms): %"PRIdFAST32"\n" "\tListening port: %"PRIuFAST16"\n" "\tDirection: %"PRIu8"\n" "\tX: %f\n" "\tY: %f\n" "\tZ: %f\n" , options.dest_ip, options.dest_port, options.iface, options.delay, options.nb_pkt, options.flood_delay, options.listening_port, options.direction, options.x, options.y, options.z ) ; } #endif // DEBUG void create_socket() { sockfd = owl_create_trx_socket(options.dest_ip, 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) ; free(packet) ; } /* * Creates the packet to send. */ void make_packet() { uint_fast16_t offset ; // Index used to create the packet 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 endianess: owl_timestamp_now(&request_time) ; owl_timestamp_to_string(&request_time, request_time_str) ; owl_hton_timestamp(&request_time) ; if (is_calibration_request) // Calibration packet { printf("Preparing calibration request packet…\n") ; offset = 0 ; packet_size = sizeof(uint8_t) * 2 + sizeof(owl_timestamp) + sizeof(float) * 3 ; packet = malloc(packet_size) ; memset(&packet[offset], OWL_REQUEST_CALIBRATION, 1) ; // Packet type ++offset ; memcpy(&packet[offset], &request_time, sizeof(request_time)) ; offset += sizeof(request_time) ; packet[offset++] = options.direction ; // Direction #ifdef DEBUG printf("Direction = %d, X = %f, Y = %f, Z = %f\n", packet[offset - 1], options.x, options.y, options.z) ; #endif // DEBUG // Convert the coordinates to the network endianess options.x = owl_htonf(options.x) ; options.y = owl_htonf(options.y) ; options.z = owl_htonf(options.z) ; // Copy the coordinates to the packet memcpy(&packet[offset], &options.x, sizeof(float)) ; offset += sizeof(float) ; memcpy(&packet[offset], &options.y, sizeof(float)) ; offset += sizeof(float) ; memcpy(&packet[offset], &options.z, sizeof(float)) ; // Convert the coordinates back to the host endianess (mandatory // in flood mode) options.x = owl_ntohf(options.x) ; options.y = owl_ntohf(options.y) ; options.z = owl_ntohf(options.z) ; } else // Standard packet { printf("Preparing request packet…\n") ; packet_size = sizeof(uint8_t) + sizeof(owl_timestamp) ; packet = malloc(packet_size) ; memset(&packet[0], OWL_REQUEST_NORMAL, 1) ; // Packet type memcpy(&packet[1], &request_time, sizeof(request_time)) ; } printf("Packet timestamp: %s\n", request_time_str) ; } /* * 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 ; } void print_usage() { printf("Usage:\n" "Localisation request:\n" "\t%s" " -i dest_ip" " [-p dest_port]" " [-I iface]" " [-t delay]" " [-n nb_packets]" " [-F [delay]]" " [-l [port]]\n" "Calibration request:\n" "\t%s" " -i dest_ip" " [-p dest_port]" " [-I iface]" " [-t delay]" " [-n nb_packets]" " direction x y z\n" "\n" "Options:\n" "\t-h\t\tPrint this help.\n" "\t-V\t\tPrint version information.\n" "\t-i dest_ip\tDestination IP address of the localisation" " request.\n" "\t-p dest_port\tDestination port of the localisation request" " (default: %d).\n" "\t-t delay\tTime between each packet transmission in" " milliseconds (default: %d ms for a normal request," " %d ms for a calibration request).\n" "\t-n nb_packets\tNumber of packet transmitted for the request" " (default: %d for a normal request, %d for a calibration" " request).\n" "\t-I iface\tName of the network interface used to transmit the" " request (e.g. \"eth2\"). If this option is absent, interface" " is selected automatically. You must be root to use this" " option.\n" "\t-F [delay]\t\"Flood mode\": loop undefinitely, sending a" " new request every milliseconds (default: %d ms).\n" "\t-l [port]\tWait for the computed position and display it." " The Optional argument allows to specify the listening" " port (default: %d).\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 Open Wireless" " Positioning System project.\n" "Version: %s.\n", #ifdef OWLPS_VERSION OWLPS_VERSION #else // OWLPS_VERSION "unknown version" #endif // OWLPS_VERSION ) ; }