From 084daed33f55e4913a62639186eafedea2a487f7 Mon Sep 17 00:00:00 2001 From: Matteo Cypriani Date: Thu, 18 Aug 2011 23:10:49 +0200 Subject: [PATCH] Add libowlps-resultreader-udp This new library is to help external programs to receive and interpret results sent by OwlPS Positioning through an UDP socket. --- Makefile | 6 + libowlps-resultreader-udp/Makefile | 81 ++++++ .../libowlps-resultreader-udp.c | 267 ++++++++++++++++++ .../owlps-resultreader-udp.c | 34 +++ .../owlps-resultreader-udp.h | 70 +++++ libowlps/owlps.h | 4 + 6 files changed, 462 insertions(+) create mode 100644 libowlps-resultreader-udp/Makefile create mode 100644 libowlps-resultreader-udp/libowlps-resultreader-udp.c create mode 100644 libowlps-resultreader-udp/owlps-resultreader-udp.c create mode 100644 libowlps-resultreader-udp/owlps-resultreader-udp.h diff --git a/Makefile b/Makefile index 037415e..51d916b 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ all c clean purge help \ libowlps \ libowlps-client \ + libowlps-resultreader-udp \ owlps-client \ owlps-listener \ owlps-aggregator \ @@ -26,6 +27,7 @@ all: \ c: \ libowlps \ libowlps-client \ + libowlps-resultreader-udp \ owlps-client \ owlps-listener \ owlps-aggregator @@ -34,6 +36,8 @@ libowlps: @make -C $@ libowlps-client: libowlps @make -C $@ +libowlps-resultreader-udp: libowlps + @make -C $@ owlps-client: libowlps libowlps-client @make -C $@ owlps-listener: libowlps libowlps-client @@ -91,6 +95,7 @@ uninstall-owlps-positioning: clean : @make -C libowlps clean @make -C libowlps-client clean + @make -C libowlps-resultreader-udp clean @make -C owlps-client clean @make -C owlps-listener clean @make -C owlps-aggregator clean @@ -99,6 +104,7 @@ clean : purge : @make -C libowlps purge @make -C libowlps-client purge + @make -C libowlps-resultreader-udp purge @make -C owlps-client purge @make -C owlps-listener purge @make -C owlps-aggregator purge diff --git a/libowlps-resultreader-udp/Makefile b/libowlps-resultreader-udp/Makefile new file mode 100644 index 0000000..21213a4 --- /dev/null +++ b/libowlps-resultreader-udp/Makefile @@ -0,0 +1,81 @@ +# Compilateur +COLORGCC := $(shell which colorgcc >/dev/null 2>&1 ; echo $$?) +ifeq ($(COLORGCC), 0) + CC = colorgcc +endif + +# Autres outils +AR = ar +RANLIB = ranlib +RM = rm -f + +# Variables générales +LIB_CIBLE = libowlps-resultreader-udp +VERSION = 1.0 + +# Cibles à construire +STATIC = $(LIB_CIBLE).a +HEADER = owlps-resultreader-udp.h +EXAMPLE = owlps-resultreader-udp + +# Composition de la bibliothèque +OBJS = $(LIB_CIBLE).o + +# Flags +LIBOWLPS_DIR = ../libowlps +CFLAGS = -O2 -Wall -Wextra -Wstrict-prototypes -O -I$(LIBOWLPS_DIR) +CFLAGS += -D DEBUG +DEPFLAGS = -MMD +XCFLAGS = $(CFLAGS) $(DEPFLAGS) $(WARN) $(HEADERS) +PICFLAG = -fPIC +#STRIPFLAGS = -Wl,-s +#LDFLAGS = + + +## Cibles de compilation standard ## + +.PHONY : all static clean purge help + +all : static example +static : $(STATIC) +example : $(EXAMPLE) + +# Cancel implicit make rule +%: %.c + +%.o : %.c $(HEADER) + $(CC) $(XCFLAGS) -c $< + +% : %.o $(HEADER) $(STATIC) + $(CC) $(STRIPFLAGS) $(XCFLAGS) -o $@ $< \ + $(STATIC) -L$(LIBOWLPS_DIR) -lowlps -lrt + +# Compilation de la bibliothèque statique +$(STATIC) : $(OBJS) + $(RM) $@ + $(AR) cru $@ $^ + $(RANLIB) $@ + + +## Nettoyage ## + +clean : + @$(RM) *~ *.o *.d + +purge : clean + @$(RM) $(STATIC) $(EXAMPLE) + + +## Aide ## + +help : + @echo "Bibliothèques nécessaires à la compilation :" + @echo " libowlps-dev" + @echo + @echo "Cibles possibles :" + @echo " static : Compile la bibliothèque statique (.a)." + @echo " example : Compile le programme d'exemple." + @echo " (Cible par défaut : static et example.)" + @echo + @echo " clean : Supprime les fichiers temporaires." + @echo " purge : Supprime le résultat de la compilation." diff --git a/libowlps-resultreader-udp/libowlps-resultreader-udp.c b/libowlps-resultreader-udp/libowlps-resultreader-udp.c new file mode 100644 index 0000000..bd09ba6 --- /dev/null +++ b/libowlps-resultreader-udp/libowlps-resultreader-udp.c @@ -0,0 +1,267 @@ +#include "owlps-resultreader-udp.h" + +#include +#include +#include + + +#define CSV_DELIMITER ";" + + +/* + * Receives a result from the socket of file descriptor 'sockfd' and + * fills an owl_result structure 'result'. + * + * Note that *result will be set by this function, and **result will + * point on a valid owl_result structure if everything goes well. + * You will have to free *result yourself with owl_free_result(). + * + * In case of error, *result is set to NULL. + */ +void owl_receive_position(int sockfd, owl_result **result) +{ + ssize_t nread ; // recvfrom return value + char csv[OWL_CSV_RESULT_STRLEN] ; // Read string + + nread = recvfrom(sockfd, &csv, OWL_CSV_RESULT_STRLEN, + 0, NULL, NULL) ; + + if (nread <= 0) + { + perror("No request received from listener") ; + *result = NULL ; + return ; + } + + owl_fill_result(result, csv) ; +} + + +/* Splits the 'csv' string received from OwlPS Positioning and stores + * the fields in 'result' (which is allocated). + * + * Handled CSV format: + * Mobile_MAC;Request_type;Request_timestamp;Algorithm;X;Y;Z;Error;Area + * The Request_timestamp format is: + * seconds.nanoseconds + */ +void owl_fill_result(owl_result **result, char *csv) +{ + char *csv_field = NULL ; + int nb_algorithms = 0 ; + owl_algorithm_result *current_algo = NULL ; + + *result = malloc(sizeof(owl_result)) ; + (*result)->results = NULL ; + + /* Mobile MAC address */ + csv_field = strsep(&csv, CSV_DELIMITER) ; + if (! csv_field) + { + fprintf(stderr, + "Error reading the mobile's MAC address from the CSV" + " string (empty string?)!\n") ; + goto error ; + } + (*result)->mobile_mac_addr = + strndup(csv_field, OWL_ETHER_ADDR_STRLEN) ; + + /* Request type */ + csv_field = strsep(&csv, CSV_DELIMITER) ; + if (! csv_field) + { + fprintf(stderr, + "Error reading the request type from the CSV string!\n") ; + goto error ; + } + (*result)->request_type = atoi(csv_field) ; + + /* Timestamp */ + // Seconds + csv_field = strsep(&csv, ".") ; + if (! csv_field) + { + fprintf(stderr, + "Error reading the timestamp (seconds) from the CSV" + " string!\n") ; + goto error ; + } + (*result)->mobile_timestamp.tv_sec = atol(csv_field) ; + // Nanoseconds + csv_field = strsep(&csv, CSV_DELIMITER) ; + if (! csv_field) + { + fprintf(stderr, + "Error reading the timestamp (nanoseconds) from the CSV" + " string!\n") ; + goto error ; + } + (*result)->mobile_timestamp.tv_nsec = atol(csv_field) ; + + /* Algorithm results */ + csv_field = strsep(&csv, CSV_DELIMITER) ; + do + { + ++nb_algorithms ; + current_algo = malloc(sizeof(owl_algorithm_result)) ; + + // Algorithm name + if (! csv_field) + { + fprintf(stderr, + "Error reading the algorithm name from the CSV" + " string (algorithm #%d)!\n", nb_algorithms) ; + goto error ; + } + current_algo->algorithm = + strndup(csv_field, OWL_ALGORITHM_STRLEN) ; + + // X coordinate + csv_field = strsep(&csv, CSV_DELIMITER) ; + if (! csv_field) + { + fprintf(stderr, + "Error reading the X coordinate from the CSV" + " string (algorithm #%d)!\n", nb_algorithms) ; + goto error ; + } + current_algo->x = atof(csv_field) ; + + // Y coordinate + csv_field = strsep(&csv, CSV_DELIMITER) ; + if (! csv_field) + { + fprintf(stderr, + "Error reading the Y coordinate from the CSV" + " string (algorithm #%d)!\n", nb_algorithms) ; + goto error ; + } + current_algo->y = atof(csv_field) ; + + // Z coordinate + csv_field = strsep(&csv, CSV_DELIMITER) ; + if (! csv_field) + { + fprintf(stderr, + "Error reading the Z coordinate from the CSV" + " string (algorithm #%d)!\n", nb_algorithms) ; + goto error ; + } + current_algo->z = atof(csv_field) ; + + // Distance error + csv_field = strsep(&csv, CSV_DELIMITER) ; + if (! csv_field) + { + fprintf(stderr, + "Error reading the distance error from the CSV" + " string (algorithm #%d)!\n", nb_algorithms) ; + goto error ; + } + current_algo->error = atof(csv_field) ; + + // Area name (optional) + csv_field = strsep(&csv, CSV_DELIMITER) ; + if (csv_field) + current_algo->area = strndup(csv_field, OWL_AREA_STRLEN) ; + + // Insert the current algorithm at the begining of the list + current_algo->next = (*result)->results ; + (*result)->results = current_algo ; + } + while ((csv_field = strsep(&csv, CSV_DELIMITER))) ; + + return ; // Success + + error: + owl_free_result(*result) ; + *result = NULL ; + free(current_algo) ; +} + + +/* + * Prints an owl_result to the given stream. + */ +void owl_fprint_result(FILE *stream, owl_result *result) +{ + char timestamp_str[OWL_TIMESTAMP_STRLEN] ; + owl_timestamp_to_string(timestamp_str, result->mobile_timestamp) ; + + fprintf(stream, + "Mobile MAC: %s\n" + "Request type: %"PRIu8"\n" + "Mobile timestamp: %s\n" + "Results:\n" + , + result->mobile_mac_addr, + result->request_type, + timestamp_str + ) ; + + owl_algorithm_result *algo = result->results ; + while (algo) + { + owl_fprint_algorithm_result(stream, algo) ; + algo = algo->next ; + } +} + + +/* + * Prints an owl_result to the given stream. + */ +void owl_fprint_algorithm_result(FILE *stream, + owl_algorithm_result *algo) +{ + fprintf(stream, + "* Algorithm: %s\n" + " X: %f\n" + " Y: %f\n" + " Z: %f\n" + " Error: %f\n" + " Area: %s\n" + , + algo->algorithm, + algo->x, + algo->y, + algo->z, + algo->error, + algo->area ? algo->area : "" + ) ; +} + + + +/* + * Frees the memory allocated by an owl_result. + * Note that the pointer will not set to NULL. + */ +void owl_free_result(owl_result *result) +{ + if (! result) + return ; + while (result->results) + { + owl_algorithm_result *algo = result->results ; + result->results = algo->next ; + owl_free_algorithm_result(algo) ; + } + free(result->mobile_mac_addr) ; + free(result) ; +} + + +/* + * Frees the memory allocated by a single owl_algorithm_result (*not* + * recursively). + * Note that the pointer will not set to NULL. + */ +void owl_free_algorithm_result(owl_algorithm_result *algo) +{ + if (! algo) + return ; + free(algo->algorithm) ; + free(algo->area) ; + free(algo) ; +} diff --git a/libowlps-resultreader-udp/owlps-resultreader-udp.c b/libowlps-resultreader-udp/owlps-resultreader-udp.c new file mode 100644 index 0000000..5a3bef9 --- /dev/null +++ b/libowlps-resultreader-udp/owlps-resultreader-udp.c @@ -0,0 +1,34 @@ +/* + * This is a simple program to demonstrate the use of + * libowlps-resultreader-udp. + * It must be linked against libowlps and its dependencies + * (see the Makefile). + */ + +#include "owlps-resultreader-udp.h" + +#include + +int main(void) +{ + owl_result *result ; + int sockfd ; + + sockfd = owl_create_udp_listening_socket(MOBILE_DEFAULT_PORT) ; + if (sockfd < 0) + return 1 ; + + while (1) + { + owl_receive_position(sockfd, &result) ; + if (result == NULL) + return 1 ; + owl_print_result(result) ; + owl_free_result(result) ; + printf("--------------\n") ; + } + + close(sockfd) ; + + return 0 ; +} diff --git a/libowlps-resultreader-udp/owlps-resultreader-udp.h b/libowlps-resultreader-udp/owlps-resultreader-udp.h new file mode 100644 index 0000000..60d857a --- /dev/null +++ b/libowlps-resultreader-udp/owlps-resultreader-udp.h @@ -0,0 +1,70 @@ +/* + * This library provides functions to read results sent on UDP by OwlPS + * Positioning. + * See the example program owlps-resultreader-udp.c. + */ + +#ifndef _LIBOWLPS_RESULTREADER_CSV_ +#define _LIBOWLPS_RESULTREADER_CSV_ + +#include + +#include +#include + + +/* Maximum size of a result CSV string sent by OwlPS Positioning. + * Request's information: + * = 18 (MAC) + 2 (type) + 22 (timestamp) = 42 + * Plus, for each algorithm, about 60 characters (say 100, as the length + * of the algorithm and area names can vary). + * Let's keep room for 10 algorithms : + * 10×100 + 42 = 1042 characters + '\0' = 1043. + */ +#define OWL_CSV_RESULT_STRLEN 1043 + + +/* Linked list of algorithms' results. + * Each structure is the result of a single algorithm. + */ +typedef struct _owl_algorithm_result +{ + char *algorithm ; + float x ; + float y ; + float z ; + float error ; + char *area ; + + struct _owl_algorithm_result *next ; +} owl_algorithm_result ; + + +/* Results for a request. Includes the request's data, and the + * list of algorithms' results. + */ +typedef struct _owl_result +{ + char *mobile_mac_addr ; + uint8_t request_type ; + owl_timestamp mobile_timestamp ; + owl_algorithm_result *results ; +} owl_result ; + + +void owl_receive_position(int sockfd, owl_result **result) ; +void owl_fill_result(owl_result **result, char *csv) ; + +void owl_fprint_result(FILE *stream, owl_result *result) ; +void owl_fprint_algorithm_result(FILE *stream, + owl_algorithm_result *algo) ; +#define owl_print_result(RESULT) \ + (owl_fprint_result(stdout, (RESULT))) +#define owl_print_algorithm_result(ALGO) \ + (owl_fprint_algorithm_result(stdout, (ALGO))) + +void owl_free_result(owl_result *result) ; +void owl_free_algorithm_result(owl_algorithm_result *algo) ; + + +#endif // _LIBOWLPS_RESULTREADER_CSV_ diff --git a/libowlps/owlps.h b/libowlps/owlps.h index 7759248..feb3572 100644 --- a/libowlps/owlps.h +++ b/libowlps/owlps.h @@ -160,6 +160,10 @@ typedef struct _owl_autocalibration_order /* Misc. */ // Length of a MAC address in string format (including '\0') #define OWL_ETHER_ADDR_STRLEN 18 +// Maximum length of an algorithm name (including '\0') +#define OWL_ALGORITHM_STRLEN 31 +// Maximum length of an area name (including '\0') +#define OWL_AREA_STRLEN 31 /* Global variables */