diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..367bf3b3faf0304e135baa9082caab2f6c0ea3cd --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su + + +# vrpn/build files +src/vrpn/build* +src/vrpn/pc_linux64/* + +#Exacutables +BlueTooth +logs diff --git a/groundStation/Makefile b/groundStation/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f09d3d4d90866ebe6bad0d63c825073e11c6bd66 --- /dev/null +++ b/groundStation/Makefile @@ -0,0 +1,55 @@ +# Declaration of variables +GCC=gcc +GXX=g++ +CFLAGS= -Wall -std=c99 -g +CPPFLAGS= -Wall -std=c++11 -g +INCLUDES = $(foreach dir, $(INCDIR), -I$(dir)) + +# Directories +SRCDIR=src +INCDIR=inc src src/vrpn src/vrpn/quat src/vrpn/build +OBJDIR=obj + +# Final exacutable name +EXE=BlueTooth + +# File names +CSOURCES := $(foreach dir, $(SRCDIR), $(wildcard $(dir)/*.c )) +CPPSOURCES := $(foreach dir, $(SRCDIR), $(wildcard $(dir)/*.cpp )) +COBJECTS = $(CSOURCES:.c=.o) +CPPOBJECTS = $(CPPSOURCES:.cpp=.o) +OBJECTS = $(CPPOBJECTS) $(COBJECTS) + +LIBS= -lpthread -lbluetooth -lvrpn -lquat -Lsrc/vrpn/build -Lsrc/vrpn/build/quat + +# Default target +all: logs $(EXE) + +vrpn: vrpn/build + +# Main target +$(EXE): $(OBJECTS) + $(GXX) $(CPPFLAGS) $^ -o $@ $(INCLUDES) $(LIBS) + +$(COBJECTS) : %.o : %.c + $(GCC) $(CFLAGS) -c $< -o $@ $(INCLUDES) $(LIBS) + +$(CPPOBJECTS) : %.o : %.cpp + $(GCC) $(CPPFLAGS) -c $< -o $@ $(INCLUDES) $(LIBS) + +vrpn/build: + mkdir -p src/vrpn/build + cd src/vrpn/build && cmake .. && make + +logs: + mkdir -p logs + +clean_logs: + rm -f logs/* + +clean: + rm -f $(EXE) $(OBJECTS) + +debug: + @echo $(COBJECTS) + @echo $(CPPOBJECTS) diff --git a/groundStation/README.md b/groundStation/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f89fed232d356c457ad4729994eecfe3ee4cbd41 --- /dev/null +++ b/groundStation/README.md @@ -0,0 +1 @@ +# groundStation \ No newline at end of file diff --git a/groundStation/src/commands.c b/groundStation/src/commands.c new file mode 100644 index 0000000000000000000000000000000000000000..dc4749ee334f38da4383ca7d8200bbafa429a2b4 --- /dev/null +++ b/groundStation/src/commands.c @@ -0,0 +1,109 @@ +#include "commands.h" + +static command_t registeredCommands[NUM_COMMANDS] = { + { 0x00, 0x00, stringType, "debug", &debug }, //DEBUG + { 0x01, 0x00, stringType, "setyaw", &setyaw }, //CALIBRATION + { 0x01, 0x01, stringType, "setyawp", &setyawp }, + { 0x01, 0x02, stringType, "setyawd", &setyawd }, + { 0x01, 0x03, stringType, "setroll", &setroll }, + { 0x01, 0x04, stringType, "setrollp", &setrollp }, + { 0x01, 0x05, stringType, "setrolld", &setrolld }, + { 0x01, 0x06, stringType, "setpitch", &setpitch }, + { 0x01, 0x07, stringType, "setpitchp", &setpitchp }, + { 0x01, 0x08, stringType, "setpitchd", &setpitchd }, + { 0x01, 0x09, stringType, "setthrottle", &setthrottle }, + { 0x01, 0x0A, stringType, "setthrottlep", &setthrottlep }, + { 0x01, 0x0B, stringType, "setthrottlei", &setthrottlei }, + { 0x01, 0x0C, stringType, "setthrottled", &setthrottled }, + { 0x02, 0x00, stringType, "getaccel", &getaccel }, //REQUEST + { 0x02, 0x01, stringType, "getgyro", &getgyro }, + { 0x02, 0x02, stringType, "getpitchangle", &getpitchangle }, + { 0x02, 0x03, stringType, "getrollangle", &getrollangle }, + { 0x03, 0x00, stringType, "accelresp", accelresp }, //RESPONSE + { 0x03, 0x01, stringType, "gyroresp", &gyroresp }, + { 0x03, 0x02, stringType, "pitchangleresp", &pitchangleresp }, + { 0x03, 0x03, stringType, "rollangleresp", &rollangleresp }, + { 0x04, 0x00, stringType, "update", &update }, //UPDATE + { 0x04, 0x01, stringType, "beginupdate", &beginupdate }, + { 0x05, 0x00, stringType, "log", &logdata }, //LOG + { 0x05, 0x01, stringType, "response", &response }, +}; + +int debug(unsigned char *c, int dataLen){ + return 0; +} +int setyaw(unsigned char *c, int dataLen){ + return 0; +} +int setyawp(unsigned char *c, int dataLen){ + return 0; +} +int setyawd(unsigned char *c, int dataLen){ + return 0; +} +int setroll(unsigned char *c, int dataLen){ + return 0; +} +int setrollp(unsigned char *c, int dataLen){ + return 0; +} +int setrolld(unsigned char *c, int dataLen){ + return 0; +} +int setpitch(unsigned char *c, int dataLen){ + return 0; +} +int setpitchp(unsigned char *c, int dataLen){ + return 0; +} +int setpitchd(unsigned char *c, int dataLen){ + return 0; +} +int setthrottle(unsigned char *c, int dataLen){ + return 0; +} +int setthrottlep(unsigned char *c, int dataLen){ + return 0; +} +int setthrottlei(unsigned char *c, int dataLen){ + return 0; +} +int setthrottled(unsigned char *c, int dataLen){ + return 0; +} +int getaccel(unsigned char *c, int dataLen){ + return 0; +} +int getgyro(unsigned char *c, int dataLen){ + return 0; +} +int getpitchangle(unsigned char *c, int dataLen){ + return 0; +} +int getrollangle(unsigned char *c, int dataLen){ + return 0; +} +int accelresp(unsigned char *c, int dataLen){ + return 0; +} +int gyroresp(unsigned char *c, int dataLen){ + return 0; +} +int pitchangleresp(unsigned char *c, int dataLen){ + return 0; +} +int rollangleresp(unsigned char *c, int dataLen){ + return 0; +} +int update(unsigned char *c, int dataLen){ + return 0; +} +int beginupdate(unsigned char *c, int dataLen){ + return 0; +} +int logdata(unsigned char *c, int dataLen){ + return 0; +} +int response(unsigned char *packet, int dataLen){ + return 0; +} \ No newline at end of file diff --git a/groundStation/src/commands.h b/groundStation/src/commands.h new file mode 100644 index 0000000000000000000000000000000000000000..050990de386d1132419c230744316033805f33af --- /dev/null +++ b/groundStation/src/commands.h @@ -0,0 +1,69 @@ +#ifndef _COMMANDS_H +#define _COMMANDS_H + +#define NUM_COMMANDS 26 + +//TODO handle with enums +#define MAX_TYPE 6 +#define MAX_SUBTYPE 100 + +int debug(unsigned char *c, int dataLen); +int setyaw(unsigned char *c, int dataLen); +int setyawp(unsigned char *c, int dataLen); +int setyawd(unsigned char *c, int dataLen); +int setroll(unsigned char *c, int dataLen); +int setrollp(unsigned char *c, int dataLen); +int setrolld(unsigned char *c, int dataLen); +int setpitch(unsigned char *c, int dataLen); +int setpitchp(unsigned char *c, int dataLen); +int setpitchd(unsigned char *c, int dataLen); +int setthrottle(unsigned char *c, int dataLen); +int setthrottlep(unsigned char *c, int dataLen); +int setthrottlei(unsigned char *c, int dataLen); +int setthrottled(unsigned char *c, int dataLen); +int getaccel(unsigned char *c, int dataLen); +int getgyro(unsigned char *c, int dataLen); +int getpitchangle(unsigned char *c, int dataLen); +int getrollangle(unsigned char *c, int dataLen); +int accelresp(unsigned char *c, int dataLen); +int gyroresp(unsigned char *c, int dataLen); +int pitchangleresp(unsigned char *c, int dataLen); +int rollangleresp(unsigned char *c, int dataLen); +int update(unsigned char *c, int dataLen); +int beginupdate(unsigned char *c, int dataLen); +int logdata(unsigned char *c, int dataLen); +int response(unsigned char *packet, int dataLen); + +enum Message{ + BEGIN_CHAR = 0xBE, + END_CHAR = 0xED +}; + +enum DataType +{ + floatType, + intType, + stringType +}; + +typedef struct command { + char ID; + char subID; + char dataType; + char commandText[100]; + int (*functionPtr)(unsigned char *command, int dataLen); +}command_t; + +enum CommandIDs{ + DEBUG, + CALIBRATION, + REQUEST, + RESPONSE, + UPDATE, + LOG, + MAX_COMMAND_IDS +}; + +static command_t registeredCommands[NUM_COMMANDS]; + +#endif \ No newline at end of file diff --git a/groundStation/src/communication.c b/groundStation/src/communication.c new file mode 100644 index 0000000000000000000000000000000000000000..305a40598d8304f6da94e556c4391eda951ceb19 --- /dev/null +++ b/groundStation/src/communication.c @@ -0,0 +1,268 @@ +#include "communication.h" +#include "commands.h" +#include <string.h> +#include <ctype.h> + +static int msgNum = 0; + +tokenList_t tokenize(char* cmd) { + int maxTokens = 16; + tokenList_t ret; + ret.numTokens = 0; + ret.tokens = malloc(sizeof(char*) * maxTokens); + ret.tokens[0] = NULL; + + int i = 0; + char* token = strtok(cmd, " "); + while (token != NULL && i < maxTokens - 1) { + ret.tokens[i++] = token; + ret.tokens[i] = NULL; + ret.numTokens++; + token = strtok(NULL, " "); + } + + return ret; +} + +int checkFloat(char *floatString, float *value) { + char *tmp; + *value = strtod(floatString, &tmp); + if(!(isspace(*tmp) || *tmp == 0)) { + fprintf(stderr, "%s is not a valid floating-point number\n", floatString); + return 0; + } + return 1; +} + +int checkInt(char *intString, int *value) { + char *tmp; + long temp_long; + temp_long = strtol(intString, &tmp, 0); // base 10 number inputted + if(temp_long < INT_MIN || temp_long > INT_MAX || !(isspace(*tmp) || *tmp == 0)) { + fprintf(stderr, "%s is not a valid integer number\n", intString); + return 0; + } + printf("temp: %ld\n\n", temp_long); + *value = (int) temp_long; + return 1; +} + +//-------------------------------- +// Ground Station +//-------------------------------- + +// Formatting commands from ground station CLI +int formatCommand(unsigned char *command, unsigned char **formattedCommand) { + //command[strlen((char *)command) - 1] = 0; + + tokenList_t tokens = tokenize((char *)command); + float floatValue = 0.0; + int intValue = 0; + int valid; + metadata_t metadata = {}; + + // ---------------------------------------------- + if(tokens.numTokens > 1) { + for(int cmdIndex = 0; cmdIndex < NUM_COMMANDS; ++cmdIndex) + { + if(strcmp(tokens.tokens[0], registeredCommands[cmdIndex].commandText) == 0) + { + switch (registeredCommands[cmdIndex].dataType) + { + // Validate the float input + case floatType: + valid = checkFloat(tokens.tokens[1], &floatValue); + if(!valid) { + return -1; + } + + printf("%f, %s\n", floatValue, tokens.tokens[1]); + + metadata.begin_char = BEGIN_CHAR; + metadata.msg_type = registeredCommands[cmdIndex].ID; + metadata.msg_subtype = registeredCommands[cmdIndex].subID; + metadata.msg_id = msgNum++; + metadata.data_len = sizeof(floatValue); + + formatPacket(&metadata, &floatValue, formattedCommand); + + break; + + // Validate the integer input + case intType: + valid = checkInt(tokens.tokens[1], &intValue); + if(!valid) { + return -1; + } + + metadata.begin_char = BEGIN_CHAR; + metadata.msg_type = registeredCommands[cmdIndex].ID; + metadata.msg_subtype = registeredCommands[cmdIndex].subID; + metadata.msg_id = msgNum++; + metadata.data_len = sizeof(intValue); + + formatPacket(&metadata, &intValue, formattedCommand); + + break; + + // Validate the string input (doesn't need to happen) + case stringType: + metadata.begin_char = BEGIN_CHAR; + metadata.msg_type = registeredCommands[cmdIndex].ID; + metadata.msg_subtype = registeredCommands[cmdIndex].subID; + metadata.msg_id = msgNum++; + metadata.data_len = strlen(tokens.tokens[1]); + + formatPacket(&metadata, &tokens.tokens[1], formattedCommand); + + break; + default: + return -1; + } + + return 0; + } + } + } + + // Only gets here if the command does not exist + return -1; +} + +// QUAD & Ground Station +// Format the log data from log_message +//int formatData(unsigned char *log_msg, unsigned char *formattedCommand) +int formatPacket(metadata_t *metadata, void *data, unsigned char **formattedCommand) +{ + *formattedCommand = malloc(sizeof(unsigned char) * metadata->data_len + 8); + //---------------------------------------------------------------------------------------------- + // index|| 0 | 1 | 2 | 3 & 4 | 5 & 6 | 7+ | end | + //---------------------------------------------------------------------------------------------| + // msg param|| beg char | msg type | msg subtype | msg id | data len (bytes) | data | checksum | + //-------------------------------------------------------------------------------------------- | + // bytes|| 1 | 1 | 1 | 2 | 2 | var | 1 | + //---------------------------------------------------------------------------------------------- + + // Begin Char: + (*formattedCommand)[0] = metadata->begin_char; + + // Msg type: + (*formattedCommand)[1] = metadata->msg_type; + + // Msg subtype + (*formattedCommand)[2] = metadata->msg_subtype; + + //Msg id (msgNum is 2 bytes) + (*formattedCommand)[3] = metadata->msg_id; + + // Data length and data - bytes 5&6 for len, 7+ for data + (*formattedCommand)[5] = metadata->data_len & 0x000000ff; + (*formattedCommand)[6] = (metadata->data_len >> 8) & 0x000000ff; + + memcpy(&((*formattedCommand)[7]), data, metadata->data_len); + + // Checksum + // receive data and calculate checksum + int i; + char data_checksum; + for(i = 0; i < 7 + metadata->data_len; i++) + { + data_checksum ^= (*formattedCommand)[i]; + } + + (*formattedCommand)[7 + metadata->data_len] = data_checksum; + + return 0; +} + +// returns the length of the data in bytes (datalen from packet) and fills data +// and metadata with the packet information +// use as follows: +// +// packet is the entire packet message (formatted) +// data is an unallocated (char *) (pass it to this function as &data) +// meta_data is a pointer to an instance of metadata_t +// +int parse_packet(unsigned char * packet, unsigned char ** data, metadata_t * meta_data) +{ + //---------------------------------------------------------------------------------------------- + // index|| 0 | 1 | 2 | 3 & 4 | 5 & 6 | 7+ | end | + //---------------------------------------------------------------------------------------------| + // msg param|| beg char | msg type | msg subtype | msg id | data len (bytes) | data | checksum | + //-------------------------------------------------------------------------------------------- | + // bytes|| 1 | 1 | 1 | 2 | 2 | var | 1 | + //---------------------------------------------------------------------------------------------- + + // first byte must be the begin char + if(packet[0] != 0xBE) + return -1; + + // receive metadata + meta_data->begin_char = packet[0]; + meta_data->msg_type = packet[1]; + meta_data->msg_subtype = packet[2]; + meta_data->msg_id = (packet[4] << 8) | (packet[3]); + meta_data->data_len = (packet[6] << 8) | (packet[5]); + unsigned char packet_checksum = packet[7+meta_data->data_len]; + //fprintf(stderr, "datalen: %d\n", meta_data->data_len); + + int i; + + // receive data + *data = malloc(meta_data->data_len); + for(i = 0; i < meta_data->data_len; i++) + { + (*data)[i] = packet[7+i]; + } + + // calculate checksum + unsigned char calculated_checksum = 0; + for(i = 0; i < meta_data->data_len + 7; i++) + { + calculated_checksum ^= packet[i]; + } + + // compare checksum + if(packet_checksum != calculated_checksum) + fprintf(stderr, "Checksums did not match (Parse Packet): 0x%02x\t0x%02x\n", packet_checksum, calculated_checksum); + + return 0; +} + +// QUAD & Ground Station +// Process the command received +int processCommand(unsigned char *packet, unsigned int cmdIndex) { + int validPacket; + unsigned char *data; + metadata_t metadata; + + // Validate the message is correctly formatted + validPacket = parse_packet(packet, &data, &metadata); + if(validPacket != 0) { + return -1; + } + + if(metadata.data_len >= 0) { + // Call the appropriate subtype function + (* (registeredCommands[cmdIndex].functionPtr))(data, metadata.data_len); + + return 0; + } + + // Only gets here if there is an error + return -1; +} + +float getFloat(unsigned char* str, int pos) { + union { + float f; + int i; + } x; + x.i = ((str[pos+3] << 24) | (str[pos+2] << 16) | (str[pos+1] << 8) | (str[pos])); + return x.f; +} + +int getInt(unsigned char* str, int pos) { + int i = ((str[pos+3] << 24) | (str[pos+2] << 16) | (str[pos+1] << 8) | (str[pos])); + return i; +} diff --git a/groundStation/src/communication.h b/groundStation/src/communication.h new file mode 100644 index 0000000000000000000000000000000000000000..94a3c33c5131ebf2a8c1c4d506bb8e7e5cf62655 --- /dev/null +++ b/groundStation/src/communication.h @@ -0,0 +1,21 @@ +#ifndef _COMMUNICATION_H +#define _COMMUNICATION_H + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#include "type_def.h" + +tokenList_t tokenize(char* cmd); +int checkFloat(char *floatString, float *value); +int checkInt(char *intString, int *value); +int formatCommand(unsigned char *command, unsigned char **formattedCommand); +int formatPacket(metadata_t *metadata, void *data, unsigned char **formattedCommand); +int parse_packet(unsigned char * packet, unsigned char ** data, metadata_t * meta_data); +int processCommand(unsigned char *command, unsigned int cmdIndex); +int logData(unsigned char *log_msg, unsigned char *formattedCommand); +float getFloat(unsigned char* str, int pos); +int getInt(unsigned char* str, int pos); + +#endif diff --git a/groundStation/src/logger.c b/groundStation/src/logger.c new file mode 100644 index 0000000000000000000000000000000000000000..028cfeac16c173e59154a1c9c8321eeba29d0344 --- /dev/null +++ b/groundStation/src/logger.c @@ -0,0 +1,108 @@ +/* Author: Kris Burney + * + * Logger file for holding functions pertaining to loging to a file. + */ +#include "logger.h" +#include <stdio.h> +#include <err.h> +#include <pthread.h> + +static FILE * quadlog_file = NULL; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +int createLogFile(int argc, char* argv) +{ + if (quadlog_file != NULL) { + return -1; + } + + if (pthread_mutex_lock(&mutex)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + + char log_file[300]; + strcpy(log_file, "logs/"); + if(argc >= 2) + { + strncat(log_file, argv, 294); + printf("Creating log file '%s'...\n",log_file); + quadlog_file = fopen(log_file, "a"); + } else { + time_t rawtime; + char timestr [30]; + time(&rawtime); + sprintf(timestr,"%s",ctime(&rawtime)); + + // Lets convert space to _ in + char *p = timestr; + size_t i = 0; + while(i < strlen(timestr)) + { + if (*p == ' ') + *p = '_'; + i++; + p++; + } + + // timestr has a weird char at the end of it. + // we will not include it in our file name + strncat(log_file, timestr, strlen(timestr) -1 ); + strcat(log_file, ".txt"); + printf("Creating log file '%s'...\n",log_file); + quadlog_file = fopen(log_file, "a"); + } + if (pthread_mutex_unlock(&mutex)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } + return 0; +} + +int updateLogFile(const struct ucart_vrpn_TrackerData * td) +{ + int retval; + + if (pthread_mutex_lock(&mutex)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + + retval = fprintf(quadlog_file, + "FPS: %lf Pos (xyz): (%lf %lf %lf) Att (pry): (%lf %lf %lf)\n", + td->fps, td->x, td->y, td->z, td->pitch, td->roll, td->yaw); + + if (pthread_mutex_unlock(&mutex)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } + + return retval; +} + +int writeStringToLog(const char * string) +{ + int retval; + + if (pthread_mutex_lock(&mutex)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + + retval = fprintf(quadlog_file, "%s", string); + + if (pthread_mutex_unlock(&mutex)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } + + return retval; +} + +void closeLogFile(void) +{ + if (pthread_mutex_lock(&mutex)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + + fclose(quadlog_file); + + if (pthread_mutex_unlock(&mutex)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } + +} diff --git a/groundStation/src/logger.h b/groundStation/src/logger.h new file mode 100644 index 0000000000000000000000000000000000000000..edfb365ffce6606e7b5bd8d1f4305c5f697180a9 --- /dev/null +++ b/groundStation/src/logger.h @@ -0,0 +1,21 @@ +/* Author: Kris Burney + * + * Logger header file for holding info pertaining to loging to a file. + */ +#ifndef _LOGGER_H +#define _LOGGER_H + +#include <fcntl.h> +#include <time.h> +#include <stdio.h> +#include <string.h> + +#include "vrpn_tracker.hpp" + +int createLogFile(int, char*); +int writeStringToLog(const char*); +int updateLogFile(const struct ucart_vrpn_TrackerData* ); +void closeLogFile(); + + +#endif diff --git a/groundStation/src/microcart_cli.c b/groundStation/src/microcart_cli.c new file mode 100644 index 0000000000000000000000000000000000000000..bf2fbc9567ebbf6507a7280e133ec2779b438bd6 --- /dev/null +++ b/groundStation/src/microcart_cli.c @@ -0,0 +1,365 @@ +/* Author: Kris Burney + * + * BlueTooth socket program for passing vrpn data to quad. + */ +//system includes +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> +#include <pthread.h> +#include <assert.h> + +//user created includes +#include "communication.h" +#include "commands.h" +#include "vrpn_tracker.hpp" +#include "type_def.h" +#include "logger.h" + +#define QUAD_BT_ADDR "00:06:66:64:61:D6" +#define QUAD_BT_CHANNEL 0x01 +#define CMD_MAX_LENGTH 1024 + +// function prototypes +void killHandler(int); +void readAndPrint(void); +void sendVrpnPacket(struct ucart_vrpn_TrackerData *); +void sendStartPacket(void); +void getVRPNPacket(struct ucart_vrpn_TrackerData *); +void printVrpnData(struct ucart_vrpn_TrackerData *); +int connectToZybo(); +// void *handleQuadResponse(); +// void *handleCliInput(); +// int atomic_check(int*, pthread_mutex_t*); +void performCommand(char *, char *); +int startsWith(const char *, const char *); +int safe_fd_set(int , fd_set* , int* ); +int safe_fd_clr(int , fd_set* , int* ); + +static void cb(struct ucart_vrpn_TrackerData *); + +/* Thread-safe wrappers */ +pthread_mutex_t quadSocketMutex; +static ssize_t writeQuad(const char * buf, size_t count); + +// global variables +static volatile int keepRunning = 1; +const char *TRACKER_IP = "UAV@192.168.0.120:3883"; +static int zyboSocket; +struct ucart_vrpn_tracker * tracker = NULL; +const char *logHeader = "";//"#\n#\tDefault log header\n#\tEverything after '#'`s will be printed as is in the processed logs.\n#\n\0"; + +pthread_mutex_t quadResponseMutex, cliInputMutex ; +unsigned char *respBuf, *commandBuf; +int newQuadResponse = 0, newCliInput = 0; + +// Structures to be used throughout +modular_structs_t structs = {}; + +// Callback to be ran whenever the tracker receives data. +// Currently doing much more than it should. It will be slimmed down +// in the future. + +static void cb(struct ucart_vrpn_TrackerData * td) +{ + static int count = 0; + + if(!(count % 10)) { + sendVrpnPacket(td); + updateLogFile(td); + } + + count++; + // This will print the vrpn data to the terminal if necissary. + // Commented out because the callback will cover quad log data + // at the end of flight. + /**if(!(count % 100)) { + printVrpnData(td); + printf("[Info] Received %d tracker updates.\n", count); + }**/ +} + + +int main(int argc, char **argv) +{ + // pthread_t quadResponse, cliInput; + fd_set rfds; + int activity; + int max_fd = 0; + + signal(SIGINT, killHandler); + + if (pthread_mutex_lock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + + // if ((zyboSocket = connectToZybo()) < 0) + // { + // perror("Error connecting to Zybo..."); + // free(respBuf); + // free(commandBuf); + // exit(1); + // } + + if (pthread_mutex_unlock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } + + // create vrpnTracker instance + //tracker = ucart_vrpn_tracker_createInstance(TRACKER_IP); + + // open the log file + if(createLogFile(argc, argv[1])) + { + perror("Error creating log file..."); + exit(1); + } +// writeStringToLog(logHeader); + + + // clear the fd set + FD_ZERO(&rfds); + // watch for input from stdin (fd 0) to see when it has input + safe_fd_set(fileno(stdin), &rfds, &max_fd); + // watch for input from the zybo socket + //safe_fd_set(zyboSocket, &rfds, &max_fd); + + //printf("zyboSocket = %d, max_fd = %d\n", zyboSocket, max_fd); + + //tell the quad we are ready to send it vrpn data + printf("sending Start Packet...\n"); + //sendStartPacket(); + + + // this function will be called whenever tracker receives data + // ucart_vrpn_tracker_addCallback(tracker, cb); + + // start the prompt + fprintf(stdout, "$microcart> "); + while(keepRunning) + { + activity = select(max_fd+1, &rfds, NULL, NULL, NULL); + if(activity == -1) { + perror("select() "); + } else if (activity) { + for(unsigned int fd = 0; fd <= max_fd; ++fd) { + if (FD_ISSET(fd, &rfds)) { + if (fd == fileno(stdin)) { + unsigned char userCommand[CMD_MAX_LENGTH] = {}; + read(fileno(stdin), (char *)userCommand, sizeof(userCommand)); + unsigned int cmdLen = strlen((char*) userCommand); + // if the user simply hit enter then let them try again + if((userCommand[0] != '\n') && (userCommand[0] != '\r')) + { + // remove newline and return line chars + if((userCommand[cmdLen - 1] == '\n') || (userCommand[cmdLen - 1] == '\r')) + userCommand[cmdLen - 1] = '\0'; + + unsigned char *packet; + formatCommand(userCommand, &packet); + + printf("received input from cli: '%s'\n", userCommand); + + fprintf(stdout, "CLI sees as: %f\n", getFloat(packet, 7)); + + // Write the command to the control_loop socket + // int n = writeQuad(packet, ((packet[6] << 8) | packet[5]) + 8); + // if(n < 0) { + // fprintf(stdout, "CLI: ERROR writing to socket\n"); + // } + } + + + fprintf(stdout, "$microcart> "); + memset(userCommand, 0, cmdLen); + } else if (fd == zyboSocket) { + + } + } + } + } + } + + // ucart_vrpn_tracker_freeInstance(tracker); + + if (pthread_mutex_lock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + close(zyboSocket); + if (pthread_mutex_unlock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } + + closeLogFile(); + return 0; +} + +// signal handler to exit while loop of main function +void killHandler(int dummy) { + keepRunning = 0; + printf("\nleaving Bluetooth module\n"); +} + +void sendStartPacket() { + unsigned char packet[8] = {0}; + + metadata_t metadata = + { + BEGIN_CHAR, + 0x04, + 0x01, + 0x01, + 0 + }; + + packet[0] = metadata.begin_char; // BEGIN //PACKET_START_BYTE; + packet[1] = metadata.msg_type; // UPDATE //'U'; // U for vrpn camera update, C for command + packet[2] = metadata.msg_subtype; // BEGIN UPDATE + packet[3] = 1; // MSG ID(1) + packet[4] = 0; // MSG ID(2) + packet[5] = 0; // DATALEN(1) + packet[6] = 0; // DATALEN(2) + + char checksum = 0; + int i; + for(i=0; i < metadata.data_len + 7; i++) + checksum ^= packet[i]; + + packet[metadata.data_len + 7] = checksum; //PACKET_END_BYTE; + int status = writeQuad((char * ) packet, metadata.data_len + 8); + if (status != 8) + { + perror("Error sending start packet...\n"); + keepRunning = 0; + }else + { + printf("Start packet successfuly sent...\n"); + } +} + +void sendVrpnPacket(struct ucart_vrpn_TrackerData *info) { + int pSize = sizeof(info) + 8; + int n; + char packet[pSize]; + packet[0] = 0xBE; // BEGIN //PACKET_START_BYTE; + packet[1] = 0x04; // UPDATE //'U'; // U for vrpn camera update, C for command + packet[2] = 0x00; // N/A + //TODO Figure out Packet ID with this new ucar_vrpn_TrackerData struct + packet[3] = (0x00 & 0x000000ff); // MSG ID(1) + packet[4] = ((0x00 >> 8) & 0x000000ff); // MSG ID(2) + packet[5] = (sizeof(info) & 0x000000ff); // DATALEN(1) + packet[6] = ((sizeof(info) >> 8) & 0x00000ff); // DATALEN(2) + memcpy(&packet[7], &info, sizeof(info)); + + char checksum = 0; + int i; + for(i=0; i < pSize - 1; i++) + checksum ^= packet[i]; + + packet[pSize - 1] = checksum; //PACKET_END_BYTE; + + n = writeQuad(packet, pSize); + if(n < 0) { + perror("vrpnhandler: ERROR writing to socket"); + keepRunning = 0; + } +} + +void getVRPNPacket(struct ucart_vrpn_TrackerData *td) { + int status; + if((status = ucart_vrpn_tracker_getData(tracker, td)) < 0) + { + perror("Error receiving VRPN data from tracker..."); + keepRunning = 0; + } +} + +void printVrpnData(struct ucart_vrpn_TrackerData * td) { + printf("FPS: %lf Pos (xyz): (%lf %lf %lf) Att (pry): (%lf %lf %lf)\n", + td->fps, td->x, td->y, td->z, td->pitch, td->roll, td->yaw); +} + +int connectToZybo() { + int sock; + struct sockaddr_rc addr = { -1 }; + + // allocate a socket + sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + + //set the connection params ie. who to connect to + addr.rc_family = AF_BLUETOOTH; + addr.rc_channel = (uint8_t) QUAD_BT_CHANNEL; + str2ba( QUAD_BT_ADDR, &addr.rc_bdaddr ); + + printf("Attempting to connect to zybo. Please be patient...\n"); + // blocking call to connect to socket sock ie. zybo board + int status = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); + + // connection failed + if(status < 0) + { + close(sock); + printf("Connection failed!...\n"); + return -1; + } + else + { + printf("connection successful!...\n"); + return sock; + } +} + +void performCommand(char *cmdName, char * command) { + for(int i = 0; i < NUM_COMMANDS; ++i) + { + if(startsWith(registeredCommands[i].commandText, command)); + fprintf(stdout, "\r\n You used cmd '%s'\n",registeredCommands[i].commandText); + } +} + +int startsWith(const char *pre, const char *str) { + size_t lenpre = strlen(pre), + lenstr = strlen(str); + return lenstr < lenpre ? 0 : (strncmp(pre, str, lenpre) == 0); +} + +/* add a fd to fd_set, and update max_fd */ +int safe_fd_set(int fd, fd_set* fds, int* max_fd) { + assert(max_fd != NULL); + + FD_SET(fd, fds); + if (fd > *max_fd) { + *max_fd = fd; + } + return 0; +} + +/* clear fd from fds, update max fd if needed */ +int safe_fd_clr(int fd, fd_set* fds, int* max_fd) { + assert(max_fd != NULL); + + FD_CLR(fd, fds); + if (fd == *max_fd) { + (*max_fd)--; + } + return 0; +} + +static ssize_t writeQuad(const char * buf, size_t count) { + ssize_t retval; + if (pthread_mutex_lock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + retval = writeQuad(buf, count); + if (pthread_mutex_unlock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } + + return retval; +} diff --git a/groundStation/src/old_main.cold b/groundStation/src/old_main.cold new file mode 100644 index 0000000000000000000000000000000000000000..5234bcc567a6e574b30eea671b3f977a7211f478 --- /dev/null +++ b/groundStation/src/old_main.cold @@ -0,0 +1,73 @@ +#include <libcli.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <string.h> +#include <strings.h> +#include <signal.h> +#include <unistd.h> +#include <arpa/inet.h> + + +#define CLITEST_PORT 8000 + +int fn_help(struct cli_def *cli, const char *command, char *argv[], int argc); + +struct cli_def * cli; +int on = 1; + +int main(int argc, char **argv) +{ + int s, x; + struct sockaddr_in addr; + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + perror("socket"); + return 1; + } + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(CLITEST_PORT); + + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + perror("bind"); + return 1; + } + + if (listen(s, 50) < 0) + { + perror("listen"); + return 1; + } + + printf("Listening on port %d\n", CLITEST_PORT); + + cli = cli_init(); + cli_set_banner(cli, "Welcome to my test cli"); + + cli_register_command(cli, NULL, "helpme", fn_help, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "displays help info for a command"); + + + + + while((x = accept(s, NULL, NULL))) + { + cli_loop(cli, x); + close(x); + } + + + cli_done(cli); +} + + +int fn_help(struct cli_def *cli, const char *command, char *argv[], int argc) { + printf("you ran the help function!!\n"); + return 0; +} + diff --git a/groundStation/src/type_def.h b/groundStation/src/type_def.h new file mode 100644 index 0000000000000000000000000000000000000000..55dca9e8fb27c26397b00961b7dbec8bbe0d4136 --- /dev/null +++ b/groundStation/src/type_def.h @@ -0,0 +1,361 @@ +/* + * struct_def.h + * + * Created on: Mar 2, 2016 + * Author: ucart + */ + +#ifndef TYPE_DEF_H_ +#define TYPE_DEF_H_ + +/** + * @brief + * The modes for autonomous and manual flight. + * + */ +enum flight_mode{ + AUTO_FLIGHT_MODE, + MANUAL_FLIGHT_MODE +}; + +//---------------------------------------------------------------------------------------------- +// index|| 0 | 1 | 2 | 3 & 4 | 5 & 6 | 7+ | end | +//---------------------------------------------------------------------------------------------| +// msg param|| beg char | msg type | msg subtype | msg id | data len (bytes) | data | checksum | +//-------------------------------------------------------------------------------------------- | +// bytes|| 1 | 1 | 1 | 2 | 2 | var | 1 | +//---------------------------------------------------------------------------------------------- +typedef struct { + char begin_char; + char msg_type; + char msg_subtype; + int msg_id; + int data_len; +} metadata_t; + + +// String builder data type +typedef struct stringBuilder_s { + char* buf; + int length; + int capacity; + int maxCapacity; + + // Methods + int (*addStr)(struct stringBuilder_s*, char*); + int (*addStrAt)(struct stringBuilder_s*, char*, int); + int (*addChar)(struct stringBuilder_s*, char); + int (*addCharAt)(struct stringBuilder_s*, char, int); + int (*removeCharAt)(struct stringBuilder_s*, int); + void (*clear)(struct stringBuilder_s*); +} stringBuilder_t; + +typedef struct { + char** tokens; + int numTokens; +} tokenList_t; + +typedef struct commands{ + int pitch, roll, yaw, throttle; +}commands; + +typedef struct raw{ + int x,y,z; +}raw; +typedef struct PID_Consts{ + float P, I, D; +}PID_Consts; + +//Camera system info +typedef struct { + int packetId; + + double y_pos; + double x_pos; + double alt_pos; + + double yaw; + double roll; + double pitch; +} quadPosition_t; + +typedef struct { + float yaw; + float roll; + float pitch; + float throttle; +} quadTrims_t; + +//Gyro, accelerometer, and magnetometer data structure +//Used for reading an instance of the sensor data +typedef struct { + + // GYRO + //Xint16 raw_gyro_x, raw_gyro_y, raw_gyro_z; + + float gyro_xVel_p; // In degrees per second + float gyro_yVel_q; + float gyro_zVel_r; + + // ACCELEROMETER + //Xint16 raw_accel_x, raw_accel_y, raw_accel_z; + + float accel_x; //In g + float accel_y; + float accel_z; + + float accel_roll; + float accel_pitch; + + + // MAG + //Xint16 raw_mag_x, raw_mag_y, raw_mag_z; + + float heading; // In degrees + + float mag_x; //Magnetic north: ~50 uT + float mag_y; + float mag_z; + + + +}gam_t; + +typedef struct PID_t { + double current_point; // Current value of the system + double setpoint; // Desired value of the system + float Kp; // Proportional constant + float Ki; // Integral constant + float Kd; // Derivative constant + double prev_error; // Previous error + double acc_error; // Accumulated error + double pid_correction; // Correction factor computed by the PID + float dt; // sample period +} PID_t; + +typedef struct PID_values{ + float P; // The P component contribution to the correction output + float I; // The I component contribution to the correction output + float D; // The D component contribution to the correction output + float error; // the error of this PID calculation + float change_in_error; // error change from the previous calc. to this one + float pid_correction; // the correction output (P + I + D) +} PID_values; + +///////// MAIN MODULAR STRUCTS +/** + * @brief + * Holds the data inputed by the user + * + */ +typedef struct user_input_t { + int rc_commands[6]; // Commands from the RC transmitter + + +// float cam_x_pos; // Current x position from the camera system +// float cam_y_pos; // Current y position from the camera system +// float cam_z_pos; // Current z position from the camera system +// float cam_roll; // Current roll angle from the camera system +// float cam_pitch; // Current pitch angle from the camera system +// float cam_yaw; // Current yaw angle from the camera system + + float yaw_manual_setpoint; + float roll_angle_manual_setpoint; + float pitch_angle_manual_setpoint; + + int hasPacket; + stringBuilder_t * sb; +} user_input_t; + +/** + * @brief + * Holds the log data to be sent to the ground station. It may hold the + * timestamp of when a sensor's data was obtained. + * + */ +typedef struct log_t { + // Time + float time_stamp; + float time_slice; + + // Id + int packetId; + + gam_t gam; // Raw and calculated gyro, accel, and mag values are all in gam_t + float phi_dot, theta_dot, psi_dot; // gimbal equation values + + quadPosition_t currentQuadPosition; + + float roll_angle_filtered, pitch_angle_filtered; + float lidar_altitude; + + float pid_P_component, pid_I_component, pid_D_component; // use these generically for any PID that you are testing + + // PID coefficients and errors + PID_t local_x_PID, local_y_PID, altitude_PID; + PID_t angle_yaw_PID, angle_roll_PID, angle_pitch_PID; + PID_t ang_vel_yaw_PID, ang_vel_roll_PID, ang_vel_pitch_PID; + + PID_values local_x_PID_values, local_y_PID_values, altitude_PID_values; + PID_values angle_yaw_PID_values, angle_roll_PID_values, angle_pitch_PID_values; + PID_values ang_vel_yaw_PID_values, ang_vel_roll_PID_values, ang_vel_pitch_PID_values; + + // RC commands + commands commands; + + //trimmed values + quadTrims_t trims; + + int motors[4]; + +} log_t; + +/** + * @brief + * Holds the raw data from the sensors and the timestamp if available + * + */ +typedef struct raw_sensor { + int acc_x; // accelerometer x data + int acc_x_t; // time of accelerometer x data + + int acc_y; // accelerometer y data + int acc_y_t; // time of accelerometer y data + + int acc_z; // accelerometer z data + int acc_z_t; // time of accelerometer z data + + + int gyr_x; // gyroscope x data + int gyr_x_t; // time of gyroscope x data + + int gyr_y; // gyroscope y data + int gyr_y_t; // time of gyroscope y data + + int gyr_z; // gyroscope z data + int gyr_z_t; // time of gyroscope z data + + int ldr_z; //lidar z data (altitude) + int ldr_z_t; //time of lidar z data + + gam_t gam; + + // Structures to hold the current quad position & orientation + quadPosition_t currentQuadPosition; + +} raw_sensor_t; + +/** + * @brief + * Holds the processed data from the sensors and the timestamp if available + * + */ +typedef struct sensor { + int acc_x; // accelerometer x data + int acc_x_t; // time of accelerometer x data + + int acc_y; // accelerometer y data + int acc_y_t; // time of accelerometer y data + + int acc_z; // accelerometer z data + int acc_z_t; // time of accelerometer z data + + + int gyr_x; // gyroscope x data + int gyr_x_t; // time of gyroscope x data + + int gyr_y; // gyroscope y data + int gyr_y_t; // time of gyroscope y data + + int gyr_z; // gyroscope z data + int gyr_z_t; // time of gyroscope z data + + int ldr_z; //lidar z data (altitude) + int ldr_z_t; //time of lidar z data + + float pitch_angle_filtered; + float roll_angle_filtered; + float lidar_altitude; + + float phi_dot, theta_dot, psi_dot; + + // Structures to hold the current quad position & orientation + quadPosition_t currentQuadPosition; + quadTrims_t trims; + +} sensor_t; + +/** + * @brief + * Holds the setpoints to be used in the controller + * + */ +typedef struct setpoint_t { + quadPosition_t desiredQuadPosition; +} setpoint_t; + +/** + * @brief + * Holds the parameters that are specific to whatever type of + * control algorithm is being used + * + */ +typedef struct parameter_t { + PID_t roll_angle_pid, roll_ang_vel_pid; + PID_t pitch_angle_pid, pitch_ang_vel_pid; + PID_t yaw_ang_vel_pid; + PID_t local_x_pid; + PID_t local_y_pid; + PID_t yaw_angle_pid; + PID_t alt_pid; +} parameter_t; + +/** + * @brief + * Holds user defined data for the controller + * + */ +typedef struct user_defined_t { + int flight_mode; + int engaging_auto; +} user_defined_t; + +/** + * @brief + * Holds the raw actuator values before processing + * + */ +typedef struct raw_actuator_t { + + int controller_corrected_motor_commands[6]; + +} raw_actuator_t; + +/** + * @brief + * Holds processed commands to go to the actuators + * + */ +typedef struct actuator_command_t { + int pwms[4]; +} actuator_command_t; + +/** + * @brief + * Structures to be used throughout + */ +typedef struct { + user_input_t user_input_struct; + log_t log_struct; + raw_sensor_t raw_sensor_struct; + sensor_t sensor_struct; + setpoint_t setpoint_struct; + parameter_t parameter_struct; + user_defined_t user_defined_struct; + raw_actuator_t raw_actuator_struct; + actuator_command_t actuator_command_struct; +}modular_structs_t; + +//////// END MAIN MODULAR STRUCTS + +#endif /* TYPE_DEF_H_ */ diff --git a/groundStation/src/vrpn_tracker.cpp b/groundStation/src/vrpn_tracker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..49cbaed12e4599a22cecf95ad6351d51de9c6bf5 --- /dev/null +++ b/groundStation/src/vrpn_tracker.cpp @@ -0,0 +1,174 @@ +#include <iostream> +#include <algorithm> + +#include "vrpn_Tracker.h" +#include "quat.h" + +#include "vrpn_tracker.hpp" + +namespace microcart +{ + static void VRPN_CALLBACK vrpn_cb(void * param, const vrpn_TRACKERCB t); + + Tracker::Tracker(std::string server) : Tracker(server.c_str()) + { + } + + Tracker::Tracker(const char * server) : + remote(server), + stop_flag(0), + trackerData({ + .x = 0.0, + .y = 0.0, + .z = 0.0, + .pitch = 0.0, + .roll = 0.0, + .yaw = 0.0, + .fps = 0.0, + .timestamp = { + .tv_sec = 0, + .tv_usec = 0 + } + }) + + { + remote.register_change_handler(this, vrpn_cb); + + stop_flag = 0; + vrpn_thread = std::thread(&Tracker::vrpn_loop, this); + } + + Tracker::~Tracker() + { + { + std::lock_guard<std::mutex> guard(vrpn_mutex); + stop_flag = 1; + } + vrpn_thread.join(); + } + + const struct TrackerData Tracker::getData(void) + { + std::lock_guard<std::mutex> guard(vrpn_mutex); + return trackerData; + } + + void Tracker::vrpn_loop(void) + { + while (1) { + remote.mainloop(); + { + std::lock_guard<std::mutex> guard(vrpn_mutex); + + if (stop_flag) { + return; + } + } + } + } + + void Tracker::callback(const vrpn_TRACKERCB t) + { + std::lock_guard<std::mutex> guard(vrpn_mutex); + + q_vec_type euler; + q_to_euler(euler, t.quat); + + trackerData.x = (double) t.pos[0]; + trackerData.y = (double) t.pos[1]; + trackerData.z = (double) t.pos[2]; + + trackerData.roll = (double) euler[2]; + trackerData.pitch = (double) euler[1]; + trackerData.yaw = (double) euler[0]; + + timeval elapsed_time; + timersub(&t.msg_time, &trackerData.timestamp, &elapsed_time); + trackerData.timestamp = t.msg_time; + + double elapsed_time_usec = (double) elapsed_time.tv_usec + + ((1000000.0) * (double) elapsed_time.tv_sec); + + trackerData.fps = 1.0 / elapsed_time_usec; + + auto td = trackerData; + std::for_each(cb_vector.begin(), cb_vector.end(), + [td](std::function<void(const TrackerData &)> &fn){ + fn(td); + }); + } + + void Tracker::addCallback(std::function<void(const TrackerData&)> cb) + { + cb_vector.push_back(cb); + } + + static void VRPN_CALLBACK vrpn_cb(void * param, const vrpn_TRACKERCB t) + { + Tracker * inst = (Tracker *)(param); + inst->callback(t); + } +} + +/* C Interface stuff */ +struct ucart_vrpn_tracker { + microcart::Tracker * t; +}; + +extern "C" +{ + struct ucart_vrpn_tracker * ucart_vrpn_tracker_createInstance( + const char * server) + { + try { + auto inst = new struct ucart_vrpn_tracker; + inst->t = new microcart::Tracker(server); + } catch(...) { + return NULL; + } + } + + void ucart_vrpn_tracker_freeInstance(struct ucart_vrpn_tracker * inst) + { + delete inst->t; + delete inst; + } + + int ucart_vrpn_tracker_addCallback(struct ucart_vrpn_tracker * inst, + void (*cb)(struct ucart_vrpn_TrackerData *)) + { + try { + inst->t->addCallback([cb](const microcart::TrackerData & td) { + struct ucart_vrpn_TrackerData data; + data.x = td.x; + data.y = td.y; + data.z = td.z; + data.pitch = td.pitch; + data.roll = td.roll; + data.yaw = td.yaw; + (*cb)(&data); + }); + } catch(...) { + return -1; + } + return 0; + } + + int ucart_vrpn_tracker_getData(struct ucart_vrpn_tracker *inst, + struct ucart_vrpn_TrackerData *td) + { + try { + auto data = inst->t->getData(); + td->x = data.x; + td->y = data.y; + td->z = data.z; + td->pitch = data.pitch; + td->roll = data.roll; + td->yaw = data.yaw; + + return 0; + } catch(...) { + return -1; + } + } +} diff --git a/groundStation/src/vrpn_tracker.hpp b/groundStation/src/vrpn_tracker.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ebd217551dab4fa6d26faf8ab1bd14a193aac317 --- /dev/null +++ b/groundStation/src/vrpn_tracker.hpp @@ -0,0 +1,97 @@ +/* Author: Jake Drahos + * + * VPRN tracker module header file. + */ + +#ifndef _MICROCART_VRPN_TRACKER_HPP +#define _MICROCART_VRPN_TRACKER_HPP + +#ifdef __cplusplus +#include <mutex> +#include <thread> +#include <functional> +#include <vector> + +#include "vrpn_Tracker.h" +#endif + +#include <sys/time.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + struct ucart_vrpn_tracker; + struct ucart_vrpn_TrackerData { + double x; + double y; + double z; + + double pitch; + double roll; + double yaw; + + double fps; + struct timeval timestamp; + }; + + struct ucart_vrpn_tracker * ucart_vrpn_tracker_createInstance( + const char * server); + + void ucart_vrpn_tracker_freeInstance(struct ucart_vrpn_tracker * inst); + + int ucart_vrpn_tracker_addCallback(struct ucart_vrpn_tracker * inst, + void (*cb)(struct ucart_vrpn_TrackerData *)); + + int ucart_vrpn_tracker_getData(struct ucart_vrpn_tracker * inst, + struct ucart_vrpn_TrackerData * td); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +namespace microcart +{ + struct TrackerData { + double x; + double y; + double z; + + double pitch; + double roll; + double yaw; + + double fps; + timeval timestamp; + }; + + class Tracker { + public: + const struct TrackerData getData(void); + void callback(const vrpn_TRACKERCB t); + + Tracker(std::string server); + Tracker(const char * server); + ~Tracker(); + + void addCallback(std::function<void(const TrackerData &)> cb); + private: + int stop_flag; + TrackerData trackerData; + std::thread vrpn_thread; + std::mutex vrpn_mutex; + + vrpn_Tracker_Remote remote; + + void vrpn_loop(void); + + std::vector<std::function<void(const TrackerData &)>> cb_vector; + }; + + +} +/* __cplusplus */ +#endif + +#endif