diff --git a/groundStation/.gitignore b/groundStation/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..508a8f243429591a76f612c87353c847358052e0 --- /dev/null +++ b/groundStation/.gitignore @@ -0,0 +1,44 @@ +# 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 +logs +BackEnd +obj +Cli diff --git a/groundStation/Makefile b/groundStation/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..50c78782e80cb912a6eb2f42906b7c363e774a06 --- /dev/null +++ b/groundStation/Makefile @@ -0,0 +1,76 @@ +# Declaration of variables + +# Generic Variables +GCC=gcc +GXX=g++ +CFLAGS= -Wall -pedantic -Wextra -Werror -std=gnu99 -g -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-unused-but-set-variable +CXXFLAGS= -Wall -pedantic -Wextra -Werror -Wno-reorder -Wno-unused-variable -std=c++0x -g +INCLUDES = $(foreach dir, $(INCDIR), -I$(dir)) +INCDIR=inc src/vrpn src/vrpn/quat src/vrpn/build $(BESRCDIR) $(CLISRCDIR) $(FESRCDIR) +LIBS= -lpthread -lbluetooth -lvrpn -lquat -Lsrc/vrpn/build -Lsrc/vrpn/build/quat +OBJDIR=obj + +# Backend Specific Variables +BEBINARY=BackEnd +BESRCDIR=src/backend +BECSOURCES := $(wildcard $(BESRCDIR)/*.c ) +BECOBJECTS = $(BECSOURCES:$(BESRCDIR)/%.c=$(OBJDIR)/%.o) +BECPPSOURCES := $(wildcard $(BESRCDIR)/*.cpp ) +BECPPOBJECTS = $(BECPPSOURCES:$(BESRCDIR)/%.cpp=$(OBJDIR)/%.o) + +# CLI Specific Variables +CLIBINARY=Cli +CLISRCDIR=src/cli +CLISOURCES := $(wildcard $(CLISRCDIR)/*.c) +CLIOBJECTS = $(CLISOURCES:$(CLISRCDIR)/%.c=$(OBJDIR)/%.o) + +# Frontend-common stuff +FESRCDIR=src/frontend +FECSOURCES := $(wildcard $(FESRCDIR)/*.c ) +FECOBJECTS = $(FECSOURCES:$(FESRCDIR)/%.c=$(OBJDIR)/%.o) + + +OBJECTS= $(CLIOBJECTS) $(BECOBJECTS) $(BECPPOBJECTS) $(FECOBJECTS) + +# Default target +all: logs objdir backend cli + +vrpn: vrpn/build + +cli: $(CLIOBJECTS) $(FECOBJECTS) + $(GCC) $(CFLAGS) $^ -o $(CLIBINARY) $(INCLUDES) $(LIBS) + +$(CLIOBJECTS) : $(OBJDIR)/%.o : $(CLISRCDIR)/%.c + $(GCC) $(CFLAGS) -c $^ -o $@ $(INCLUDES) $(LIBS) + + +backend: $(BECPPOBJECTS) $(BECOBJECTS) + $(GXX) $(CXXFLAGS) $^ -o $(BEBINARY) $(INCLUDES) $(LIBS) + +$(FECOBJECTS) : $(OBJDIR)/%.o : $(FESRCDIR)/%.c + $(GCC) $(CFLAGS) -c $^ -o $@ $(INCLUDES) $(LIBS) + +$(BECOBJECTS) : $(OBJDIR)/%.o : $(BESRCDIR)/%.c + $(GCC) $(CFLAGS) -c $^ -o $@ $(INCLUDES) $(LIBS) + +$(BECPPOBJECTS) : $(OBJDIR)/%.o : $(BESRCDIR)/%.cpp + $(GXX) $(CXXFLAGS) -c $^ -o $@ $(INCLUDES) $(LIBS) + +vrpn/build: + mkdir -p src/vrpn/build + cd src/vrpn/build && cmake .. && make + +logs: + mkdir -p logs + +objdir: + mkdir -p obj + +clean_logs: + rm -f logs/* + +clean: + rm -rf $(OBJDIR)/ $(BEBINARY) $(CLIBINARY) + +debug: + @echo $(OBJECTS) diff --git a/groundStation/README.md b/groundStation/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a54b1f43611542c3792549e51f1509bc187a5428 --- /dev/null +++ b/groundStation/README.md @@ -0,0 +1,63 @@ +# groundStation + +## Make Process + +First, if submodules were not recursevly added through git. Run this command if you have made any attempt to make vrpn manually. +run + 'git submodule update --init --recursive' + +Now that you have all of the files necissary. + +cd into the groundstation folder. + cd groundStation + make vrpn + make + +run the program with sudo privledges + sudo -E ./BackEnd + +If you wish to change the way the backend communicates to the quad and vice versa, look at src/config.h. + This provides a list of environment variables which you can set and use for your computer or time of use. + Because the backend must be ran with sudo privledges, you will need to preserve the env. vars. with sudo rights. + Hence the "-E" flag. + +## Modifying +See MODIFYING for the software architecture/organization and how to add new functionality. + +## Using +First, the backend daemon must be running. Run the backend with ./BackEnd. Note +the environment variables in config.h, especially the backend socket path. The backend +requires root for bluetooth, but can run as a normal user with TCP, as long as the +socket path is writable by the user. + +Once the backend is running, various CLI tools can be used to view the state of the +quad and send commands. Each of these tools is given as the first argument +to the CLI program, for example, to monitor the quad, use `cli monitor`. Note that +the backend socket environment variable must be set to the same value as it +was for the backend. The CLI program also supports busybox-style symbolic links. +For example, if there is a symlink named `monitor` that points to the `cli` binary, +running the `monitor` program will effectively run `cli monitor`. + +The names of the binaries is subject to change. + +### Example +In one terminal or screen, run the backend: + +`UCART_SOCKET=./ucart.socket ./BackEnd` + +This will activate the quad and the backend, and the backend will be available for +connections from the frontend tools. One useful tool is the monitor. In another +terminal window, run the monitor forever: + +`UCART_SOCKET=./ucart.socket ./cli monitor -f` + +This will begin a periodic monitoring that updates 10 times per second. + +Finally, in a third window, export the socket path: + +`export UCART_SOCKET=./ucart.socket` + +and then run any other tools to modify the quad, for example modifying PID constants: + +`./cli setpid --pitch -p 1.000` + diff --git a/groundStation/src/backend/backend.c b/groundStation/src/backend/backend.c new file mode 100644 index 0000000000000000000000000000000000000000..5c4dc9fa6590757f051969825b6969c62bcf8fb5 --- /dev/null +++ b/groundStation/src/backend/backend.c @@ -0,0 +1,773 @@ +/* Author: Kris Burney & Jake Drahos + * + * BlueTooth socket program for passing vrpn data to quad. + */ + +#define _GNU_SOURCE +#define _BSD_SOURCE + +//system includes +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/select.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> +#include <pthread.h> +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <netinet/tcp.h> + +//user created includes +#include "communication.h" +#include "commands.h" +#include "vrpn_tracker.hpp" +#include "type_def.h" +#include "logger.h" +#include "config.h" + +#define QUAD_BT_ADDR "00:06:66:64:61:D6" +#define QUAD_BT_CHANNEL 0x01 +#define CMD_MAX_LENGTH 1024 +#define MAX_HASH_SIZE 50 + +/* Backend-internal command magics */ +#define TD_MAGIC "TRACKERDATA" + +// function prototypes +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(); +int safe_fd_set(int , fd_set* , int* ); +int safe_fd_clr(int , fd_set* , int* ); +static void safe_close_fd(int fd, pthread_mutex_t *mutexLock); + +static void cb(struct ucart_vrpn_TrackerData *); +static int new_client(int fd); +/* Return index of client, or -1 */ +static ssize_t get_client_index(int fd); +/* Returns pointer to client buffer, or -1 */ +static char * get_client_buffer(int fd); +/* Return pointer to client pending responses, or -1*/ +static int * get_client_pend_responses(int fd); +/* Return positive integer if successful, -1 otherwise */ +static int clientAddPendResponses(int fd, unsigned char *packet); +/* Returns -1 on error */ +static int remove_client(int fd); +/* Receive data from client */ +static void client_recv(int fd); +/* Receive data from quad */ +static void quad_recv(const char * buf, size_t len); +/* Checks to see if socket has disconnected. Returns 1 on disconnect, else returns 0 */ +static int wasDisconnected(int fd); + +/* Thread-safe wrappers */ +pthread_mutex_t quadSocketMutex; +static ssize_t writeQuad(const char * buf, size_t count); +static ssize_t readQuad(char * buf, size_t count); + +/* Functions for recording Latencies */ +void findTimeDiff(int respID); +int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y); +//time stamp checking +static unsigned int currMessageID = 0; +struct timeval timeArr[MAX_HASH_SIZE]; + +// global variables +static volatile int keepRunning = 1; +const char *TRACKER_IP = "UAV@192.168.0.120:3883"; +static int zyboSocket; +static int backendSocket; +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"; + +#define MAX_CLIENTS 32 +#define CLIENT_BUFFER_SIZE 1024 +#define CLIENT_MAX_PENDING_RESPONSES 5 +static char client_buffers[MAX_CLIENTS][CLIENT_BUFFER_SIZE]; +static int client_fds[MAX_CLIENTS]; +static int client_pending_responses[MAX_CLIENTS][CLIENT_MAX_PENDING_RESPONSES]; +fd_set rfds_master; +int max_fd = 0; + +pthread_mutex_t quadResponseMutex, cliInputMutex ; +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++; +} + +int main(int argc, char **argv) +{ + int activity; + FD_ZERO(&rfds_master); + + /* + * Create backend listening socket + */ + /* Determine socket path */ + char * backend_socket_path = DEFAULT_SOCKET; + if (getenv(SOCKET_ENV)) { + backend_socket_path = getenv(SOCKET_ENV); + } + /* Unlink if it exists */ + unlink(backend_socket_path); + + /* Create socket */ + backendSocket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (backendSocket < 0) { + err(-1, "socket"); + } + + /* Create sockaddr and bind */ + struct sockaddr_un sa; + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, backend_socket_path, 107); + sa.sun_path[107] = '\0'; + if (bind(backendSocket, (struct sockaddr *) &sa, sizeof(sa))) { + err(-1, "bind"); + } + + /* Listen */ + if (listen(backendSocket, 16)) { + err(-1, "listen"); + } + + /* Add to socket set */ + safe_fd_set(backendSocket, &rfds_master, &max_fd); + + + /* Initialize client buffers */ + for (int i = 0; i < MAX_CLIENTS; i++) { + client_fds[i] = -1; + client_buffers[i][0] = '\n'; + for(int j = 0; j < CLIENT_MAX_PENDING_RESPONSES; j++) { + client_pending_responses[i][j] = -1; + } + } + + if (pthread_mutex_lock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + + if ((zyboSocket = connectToZybo()) < 0) + { + perror("Error connecting to Quad..."); + free(respBuf); + free(commandBuf); + exit(1); + } + + if (pthread_mutex_unlock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } + + // open the log file + if(createLogFile(argc, argv[1])) + { + perror("Error creating log file..."); + exit(1); + } + // writeStringToLog(logHeader); + + // watch for input from stdin (fd 0) to see when it has input + safe_fd_set(fileno(stdin), &rfds_master, &max_fd); + + if (!getenv(NOQUAD_ENV)) { + // watch for input from the zybo socket + safe_fd_set(zyboSocket, &rfds_master, &max_fd); + + } + // Tell the quad we are ready to send it vrpn data + sendStartPacket(); + + if(!getenv(NOVRPN_ENV)){ + // create vrpnTracker instance + tracker = ucart_vrpn_tracker_createInstance(TRACKER_IP); + // this function will be called whenever tracker receives data + ucart_vrpn_tracker_addCallback(tracker, cb); + } + + struct timeval timeout = { + .tv_sec = 1, + .tv_usec = 0 + }; + + respBuf = calloc(CMD_MAX_LENGTH, sizeof(unsigned char)); + sleep(3); + while(keepRunning) + { + fd_set rfds; + rfds = rfds_master; + activity = select(max_fd+1, &rfds, NULL, NULL, NULL); + if(activity == -1) { + perror("select() "); + } else if (activity) { + for(int fd = 0; fd <= max_fd; ++fd) { + if (FD_ISSET(fd, &rfds)) { + if(wasDisconnected(fd)){ + break; + } + + if (fd == fileno(stdin)) { + /** + * Ignore stdin from the backend + */ + } else if (fd == zyboSocket) { + printf("recieving from quad\n"); + + /** + * Read the response from the control loop + */ + int available; + ioctl(fd, FIONREAD, &available); + if (available < 12) { + continue; + } + int respLen = readQuad(respBuf, CMD_MAX_LENGTH); + if(respLen <= 0) { + perror("ERROR reading from quad...\n"); + } + + //int id = getInt((unsigned char *)respBuf, 7); + //findTimeDiff(id); + + quad_recv(respBuf, respLen); + // if(respLen == 11) { + // int id = getInt((unsigned char *)respBuf, 7); + // findTimeDiff(id); + // printf("respLen = %d : id = %d'\n", respLen, id); + // for(int i = 0; i <= respLen -1; ++i) + // printf("%x ", (unsigned char)respBuf[i]); + // printf("'\n"); + // } + memset(respBuf, 0, respLen); + } else if (fd == backendSocket) { + int new_fd = 0; + new_fd = accept(backendSocket, NULL, NULL); + if (new_fd < 0) { + warn("accept"); + } else { + printf("Connection\n"); + if (new_client(new_fd)) { + printf("Added client\n"); + safe_fd_set(new_fd, &rfds_master, &max_fd); + } + } + } else if (get_client_index(fd) > -1) { + /* It is a socket to a frontend */ + client_recv(fd); + } + } + } + } else { + timeout.tv_sec = 1; + timeout.tv_usec = 0; + } + } + + ucart_vrpn_tracker_freeInstance(tracker); + + safe_close_fd(zyboSocket, &quadSocketMutex); + + closeLogFile(); + return 0; +} + +void sendStartPacket() { + unsigned char packet[8] = {0}; + currMessageID++; + metadata_t metadata = + { + (char) 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] = (currMessageID & 0x000000ff); // MSG ID(1) + packet[4] = ((currMessageID >> 8) & 0x000000ff); // 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(struct ucart_vrpn_TrackerData) + 8; + int n; + unsigned char packet[pSize]; + currMessageID++; + 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] = (currMessageID & 0x000000ff); // MSG ID(1) + packet[4] = ((currMessageID >> 8) & 0x000000ff); // MSG ID(2) + packet[5] = (sizeof(struct ucart_vrpn_TrackerData) & 0x000000ff); // DATALEN(1) + packet[6] = ((sizeof(struct ucart_vrpn_TrackerData) >> 8) & 0x00000ff); // DATALEN(2) + memcpy(&packet[7], &info, sizeof(struct ucart_vrpn_TrackerData)); + + char checksum = 0; + int i; + for(i=0; i < pSize - 1; i++) + checksum ^= packet[i]; + + packet[pSize - 1] = checksum; //PACKET_END_BYTE; + + n = writeQuad((char *) packet, pSize); + if(n < 0) { + perror("vrpnhandler: ERROR writing to socket"); + keepRunning = 0; + } + struct timeval tstart; + gettimeofday(&tstart, NULL); + timeArr[currMessageID%MAX_HASH_SIZE] = tstart; +} + +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; + int status = 0; + + if (getenv(NOQUAD_ENV)) { + return 0; + } + + /* Use bluetooth by default */ + if (!getenv(QUAD_WIFI_ENV)) { + printf("Using BT Settings\n"); + struct sockaddr_rc addr; + + // 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 + status = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); + } else { + printf("Using WIFI settings\n"); + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + + /* Quick and Dirty */ + if (getenv(QUAD_IP_ENV)) { + if (!inet_aton(getenv(QUAD_IP_ENV), &addr.sin_addr)) { + printf("Env var %s invalid IP %s\n", + QUAD_IP_ENV, getenv(QUAD_IP_ENV)); + return -1; + } + } else { + if (!inet_aton(QUAD_IP_DEFAULT, &addr.sin_addr)) { + printf("Default IP %s is invalid\n", + QUAD_IP_DEFAULT); + return -1; + } + } + + if (getenv(QUAD_PORT_ENV)) { + /* Quick 'n dirty, oh yeah! */ + addr.sin_port = htons(atoi(getenv(QUAD_PORT_ENV))); + } else { + addr.sin_port = htons(QUAD_PORT_DEFAULT); + } + + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) { + perror("socket"); + return -1; + } + printf("Connecting to Quad @ %s:%u\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + + status = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); + } + + // connection failed + if(status < 0) + { + close(sock); + perror("connect"); + return -1; + } + else + { + // int result = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (int[]){1}, sizeof(int)); + // printf("result = %d\n", result); + printf("connection successful!...\n"); + return sock; + } +} + +/* 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 (getenv(NOQUAD_ENV)) { + return count; + } + if (pthread_mutex_lock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + //setsockopt(zyboSocket, IPPROTO_TCP, TCP_QUICKACK, (int[]){1}, sizeof(int)); + retval = write(zyboSocket, buf, count); + if (pthread_mutex_unlock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } + + return retval; +} + +static ssize_t readQuad(char * buf, size_t count) { + ssize_t retval; + if (pthread_mutex_lock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + retval = read(zyboSocket, buf, count); + //int result = setsockopt(zyboSocket, IPPROTO_TCP, TCP_QUICKACK, (int[]){1}, sizeof(int)); + //printf("result = %d\n", result); + + if (pthread_mutex_unlock(&quadSocketMutex)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } + return retval; +} + +static int new_client(int fd) { + ssize_t new_slot = -1; + for (ssize_t i = 0; i < MAX_CLIENTS; i++) { + if (client_fds[i] < 0) { + new_slot = i; + break; + } + } + if (new_slot == -1) { + warnx("Ran out of room! Consider increasing MAX_CLIENTS!"); + return 0; + } + + client_fds[new_slot] = fd; + client_buffers[new_slot][0] = '\0'; + + return 1; +} + +static ssize_t get_client_index(int fd) { + for (ssize_t i = 0; i < MAX_CLIENTS; i++) { + if (client_fds[i] == fd) { + return i; + } + } + + return -1; +} + +static char * get_client_buffer(int fd) { + ssize_t slot = get_client_index(fd); + if (slot == -1) { + return NULL; + } else { + return client_buffers[slot]; + } +} + +static int * get_client_pend_responses(int fd) { + ssize_t slot = get_client_index(fd); + if (slot == -1) { + return NULL; + } else { + return client_pending_responses[slot]; + } +} + +static int clientAddPendResponses(int fd, unsigned char *packet) { + int *pendingResponses = get_client_pend_responses(fd); + int packetID = (packet[4] << 8) | (packet[3]); + for(int i = 0; i < CLIENT_MAX_PENDING_RESPONSES; i++) { + if(pendingResponses[i] == -1) { + pendingResponses[i] = packetID; + return i; + } + } + return -1; +} + +static int remove_client(int fd) { + ssize_t slot = get_client_index(fd); + if(slot == -1) + return -1; + char *clientBuffer = get_client_buffer(fd); + if(clientBuffer == NULL) + return -1; + clientBuffer[0] = '\0'; + int *pendingResponses = get_client_pend_responses(fd); + if(pendingResponses == NULL) + return -1; + for(int i = 0; i < CLIENT_MAX_PENDING_RESPONSES; i++) { + pendingResponses[i] = -1; + } + client_fds[slot] = -1; + return 0; +} + +static void safe_close_fd(int fd, pthread_mutex_t *mutexLock) { + if (pthread_mutex_lock(mutexLock)) { + err(-2, "pthrtead_mutex_lock (%s:%d):", __FILE__, __LINE__); + } + close(fd); + if (pthread_mutex_unlock(mutexLock)) { + err(-2, "pthrtead_mutex_unlock (%s:%d):", __FILE__, __LINE__); + } +} + +static void client_recv(int fd) { + char * buffer; + ssize_t len_pre; + buffer = get_client_buffer(fd); + len_pre = strlen(buffer); + + char * cursor; + cursor = buffer + len_pre; + + ssize_t r; + r = read(fd, cursor, CLIENT_BUFFER_SIZE - len_pre - 1); + if (r < 0) { + warn("read (fd: %d)", fd); + } + buffer[len_pre + r] = '\0'; + + int index = 0; + /* Parse buffer and handle commands */ + while (1) { + /* not using strtok because reasons */ + size_t len = strlen(buffer); + ssize_t newline = -1; + for (size_t i = 0; i < len; i++) { + if (buffer[i] == '\n') { + newline = i; + break; + } + } + + /* No newline found. End parsing */ + if (newline == -1) { + break; + } + buffer[newline] = '\0'; + + printf("Client(%d) : '%s'\n",fd, buffer); + unsigned char * packet; + if(formatCommand(buffer, &packet) == -1) { + /* buffer was not a quad command, handling internally to + * backend instead of forwarding to quad + */ + if (strncmp(buffer, TD_MAGIC, strlen(TD_MAGIC)) == 0) { + /* Request for tracker data */ + struct ucart_vrpn_TrackerData td; + if (tracker == NULL) { + char * dummy = TD_MAGIC " 1.0 2.0 3.0 4.0 5.0 6.0\n"; + write(fd, dummy, strlen(dummy)); + }else if (ucart_vrpn_tracker_getData(tracker, &td)) { + write(fd, TD_MAGIC " ERROR\n", strlen(TD_MAGIC " ERROR\n")); + } else { + /* more than sufficient buffer */ + char buffer[2048]; + /* Map VRPN XYZ to Height Lat Long (right now it's + * a guess). Format is Height Lat Long P R Y */ + if (snprintf(buffer, + 2048, + TD_MAGIC " %lf %lf %lf %lf %lf %lf\n", + td.z, + td.y, + td.x, + td.pitch, + td.roll, + td.yaw) >= 2048) { + + /* Output longer than buffer */ + warnx("Increase format buffer size, output was too long!"); + write(fd, TD_MAGIC " ERROR\n", strlen(TD_MAGIC " ERROR\n")); + } + write(fd, buffer, strlen(buffer)); + } + } + } else { + if(clientAddPendResponses(fd, packet) == -1) { + warnx("Ran out of room! Consider increasing CLIENT_MAX_PENDING_RESPONSES!\n"); + } else { + int datalen = (packet[6] << 8) | (packet[5]); + printf("sending %lf '", getFloat(packet, 7)); + for(int i = 0; i < datalen + 8; ++i) { + printf(" 0x%x", (signed) packet[i]); + } + printf("'\n"); + writeQuad((char *) packet, datalen +8); + } + } + + char * rest = &buffer[newline] + 1; + size_t restLen = (strlen(rest) == 0) ? 1 : strlen(rest); + /* Delete parsed data and move the rest to the left */ + memmove(buffer, rest, restLen +1); + } +} + +static void quad_recv(const char * buf, size_t len) { + /* Check to see which command we are receiving. If it is one that needs to be passed on + onto the clients, do so. + */ + + char packet[len]; + int validPacket; + unsigned char *data; + metadata_t metadata; + + memcpy(packet, buf, len); + + // Validate the message is correctly formatted + validPacket = parse_packet((unsigned char *) packet, &data, &metadata); + if(validPacket != 0) { + warnx("Could not recognize packet from quad.\n"); + return; + } + + int datalen = (packet[6] << 8) | (packet[5]); + char * cmdText = MessageTypes[(int)metadata.msg_type].subtypes[(int)metadata.msg_subtype].cmdText; + float value = getFloat((unsigned char *)packet, 7); + printf("Quad : %s, %lf\n", cmdText, value); + /* + Assuming the quad sends the correct info.. This hasn't been tested yet due to a lack of + quad software. We can check how to format by the cmdText and pass to every client. + */ + char buffer[1048]; + sprintf(buffer, "%s %lf\n", cmdText, value); + + for(int fd = 0; fd <= max_fd; ++fd) { + if (get_client_index(fd) > -1) { + write(fd, buffer, datalen + 8); + } + } +} + +static int wasDisconnected(int fd) { + char buff; + if(recv(fd, &buff, 1, MSG_PEEK | MSG_DONTWAIT) == 0) + { + remove_client(fd); + safe_fd_clr(fd, &rfds_master, &max_fd); + printf("fd %d has disconnect and was removed\n", fd); + return 1; + } + return 0; +} + +int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y) { + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_usec < y->tv_usec) { + int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + y->tv_usec -= 1000000 * nsec; + y->tv_sec += nsec; + } + if (x->tv_usec - y->tv_usec > 1000000) { + int nsec = (x->tv_usec - y->tv_usec) / 1000000; + y->tv_usec += 1000000 * nsec; + y->tv_sec -= nsec; + } + /* Compute the time remaining to wait. + tv_usec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +void findTimeDiff(int respID) { + struct timeval result, tend; + gettimeofday(&tend, NULL); + timeval_subtract(&result, &tend, &timeArr[respID%MAX_HASH_SIZE]); + printf("elapsed time = %ld ms\n", result.tv_usec/1000); + // char tmp[8]; + // snprintf(tmp, 8, "%ld \tms\n", result.tv_usec/1000); + // writeStringToLog(tmp); +} diff --git a/groundStation/src/backend/commands.c b/groundStation/src/backend/commands.c new file mode 100644 index 0000000000000000000000000000000000000000..a97197b8106334e13b2e652ab93c1228f868ce0b --- /dev/null +++ b/groundStation/src/backend/commands.c @@ -0,0 +1,1150 @@ +#include "commands.h" + +// TAKE THESE OUT WHEN IMPLEMENTING ON THE QUAD SIDE +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; +} +//------------------------------------------------ + +struct MessageType MessageTypes[MAX_TYPE] = +{ + // DEBUG + { + // Message Type ID + 0x00, + + // Debug Subtypes + { + // NONE subtype + { + // ID + 0x00, + // Command text + "debug", + // Type of the command data + stringType, + // Function pointer + &debug + } + } + }, + + // CALIBRATION + { + // Message Type ID + 0x01, + + // Calibration Subtypes (PID coefficients) + { + // yaw p constant subtype + { + // ID + 0x00, + // Command text + "setyawp", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // yaw i constant subtype + { + // ID + 0x01, + // Command text + "setyawi", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // yaw d constant subtype + { + // ID + 0x02, + // Command text + "setyawd", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // roll p constant subtype + { + // ID + 0x03, + // Command text + "setrollp", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // roll i constant subtype + { + // ID + 0x04, + // Command text + "setrolli", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // roll d constant subtype + { + // ID + 0x05, + // Command text + "setrolld", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // pitch p constant subtype + { + // ID + 0x06, + // Command text + "setpitchp", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // pitch i constant subtype + { + // ID + 0x07, + // Command text + "setpitchi", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // pitch d constant subtype + { + // ID + 0x08, + // Command text + "setpitchd", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // yawrate p constant subtype + { + // ID + 0x09, + // Command text + "setyawratep", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // yawrate i constant subtype + { + // ID + 0x0A, + // Command text + "setyawratei", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // yawrate d constant subtype + { + // ID + 0x0B, + // Command text + "setyawrated", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // rollrate p constant subtype + { + // ID + 0x0C, + // Command text + "setrollratep", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // rollrate i constant subtype + { + // ID + 0x0D, + // Command text + "setrollratei", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // rollrate d constant subtype + { + // ID + 0x0E, + // Command text + "setrollrated", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // pitchrate p constant subtype + { + // ID + 0x0F, + // Command text + "setpitchratep", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // pitchrate i constant subtype + { + // ID + 0x10, + // Command text + "setpitchratei", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // pitchrate d constant subtype + { + // ID + 0x11, + // Command text + "setpitchrated", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // height p constant subtype + { + // ID + 0x12, + // Command text + "setheightp", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // height i constant subtype + { + // ID + 0x13, + // Command text + "setheighti", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // height d constant subtype + { + // ID + 0x14, + // Command text + "setheightd", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // lat p constant subtype + { + // ID + 0x15, + // Command text + "setlatp", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // lat i constant subtype + { + // ID + 0x16, + // Command text + "setlati", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // lat d constant subtype + { + // ID + 0x17, + // Command text + "setlatd", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // long p constant subtype + { + // ID + 0x18, + // Command text + "setlongp", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // long i constant subtype + { + // ID + 0x19, + // Command text + "setlongi", + // Type of the command data + floatType, + // Function pointer + NULL + }, + // long d constant subtype + { + // ID + 0x1A, + // Command text + "setlongd", + // Type of the command data + floatType, + // Function pointer + NULL + } + } + }, + + // REQUEST + { + // Message Type ID + 0x02, + + // Request Subtypes + { + // accelerometer subtype + { + // ID + 0x00, + // Command text + "getaccel", + // Type of the command data + floatType, + // Function pointer + &getaccel + }, + // gyroscope subtype + { + // ID + 0x01, + // Command text + "getgyro", + // Type of the command data + floatType, + // Function pointer + &getgyro + }, + // pitch angle subtype + { + // ID + 0x02, + // Command text + "getpitchangle", + // Type of the command data + floatType, + // Function pointer + &getpitchangle + }, + // roll angle subtype + { + // ID + 0x03, + // Command text + "getrollangle", + // Type of the command data + floatType, + // Function pointer + &getrollangle + }, + // get yaw setpoint subtype + { + // ID + 0x04, + // Command text + "getyaw", + // Type of the command data + floatType, + // Function pointer + &setyaw + }, + // get yaw p constant subtype + { + // ID + 0x05, + // Command text + "getyawp", + // Type of the command data + floatType, + // Function pointer + &getyawp + }, + // get yaw d constant subtype + { + // ID + 0x06, + // Command text + "getyawd", + // Type of the command data + floatType, + // Function pointer + &getyawd + }, + // get roll setpoint subtype + { + // ID + 0x07, + // Command text + "getroll", + // Type of the command data + floatType, + // Function pointer + &getroll + }, + // get roll p constant subtype + { + // ID + 0x08, + // Command text + "getrollp", + // Type of the command data + floatType, + // Function pointer + &getrollp + }, + // getroll d constant subtype + { + // ID + 0x09, + // Command text + "getrolld", + // Type of the command data + floatType, + // Function pointer + &getrolld + }, + // get pitch setpoint subtype + { + // ID + 0x0A, + // Command text + "getpitch", + // Type of the command data + floatType, + // Function pointer + &getpitch + }, + // get pitch p constant subtype + { + // ID + 0x0B, + // Command text + "getpitchp", + // Type of the command data + floatType, + // Function pointer + &getpitchp + }, + // get pitch d constant subtype + { + // ID + 0x0C, + // Command text + "getpitchd", + // Type of the command data + floatType, + // Function pointer + &getpitchd + }, + // get throttle setpoint subtype + { + // ID + 0x0D, + // Command text + "getthrottle", + // Type of the command data + floatType, + // Function pointer + &getthrottle + }, + // get throttle p constant subtype + { + // ID + 0x0E, + // Command text + "getthrottlep", + // Type of the command data + floatType, + // Function pointer + &getthrottlep + }, + // get throttle i constant subtype + { + // ID + 0x0F, + // Command text + "getthrottlei", + // Type of the command data + floatType, + // Function pointer + &getthrottlei + }, + // get throttle d constant subtype + { + // ID + 0x10, + // Command text + "getthrottled", + // Type of the command data + floatType, + // Function pointer + &getthrottled + } + } + }, + + // RESPONSE + { + // Message Type ID + 0x03, + + // Response Subtypes + { + // accelerometer subtype + { + // ID + 0x00, + // Command text + "respaccel", + // Type of the command data + floatType, + // Function pointer + &respaccel + }, + // gyroscope subtype + { + // ID + 0x01, + // Command text + "respgyro", + // Type of the command data + floatType, + // Function pointer + &respgyro + }, + // pitch angle subtype + { + // ID + 0x02, + // Command text + "resppitchangle", + // Type of the command data + floatType, + // Function pointer + &resppitchangle + }, + // roll angle subtype + { + // ID + 0x03, + // Command text + "resprollangle", + // Type of the command data + floatType, + // Function pointer + &resprollangle + }, + // resp yaw setpoint subtype + { + // ID + 0x04, + // Command text + "respyaw", + // Type of the command data + floatType, + // Function pointer + &setyaw + }, + // resp yaw p constant subtype + { + // ID + 0x05, + // Command text + "respyawp", + // Type of the command data + floatType, + // Function pointer + &respyawp + }, + // resp yaw d constant subtype + { + // ID + 0x06, + // Command text + "respyawd", + // Type of the command data + floatType, + // Function pointer + &respyawd + }, + // resp roll setpoint subtype + { + // ID + 0x07, + // Command text + "resproll", + // Type of the command data + floatType, + // Function pointer + &resproll + }, + // resp roll p constant subtype + { + // ID + 0x08, + // Command text + "resprollp", + // Type of the command data + floatType, + // Function pointer + &resprollp + }, + // resproll d constant subtype + { + // ID + 0x09, + // Command text + "resprolld", + // Type of the command data + floatType, + // Function pointer + &resprolld + }, + // resp pitch setpoint subtype + { + // ID + 0x0A, + // Command text + "resppitch", + // Type of the command data + floatType, + // Function pointer + &resppitch + }, + // resp pitch p constant subtype + { + // ID + 0x0B, + // Command text + "resppitchp", + // Type of the command data + floatType, + // Function pointer + &resppitchp + }, + // resp pitch d constant subtype + { + // ID + 0x0C, + // Command text + "resppitchd", + // Type of the command data + floatType, + // Function pointer + &resppitchd + }, + // resp throttle setpoint subtype + { + // ID + 0x0D, + // Command text + "respthrottle", + // Type of the command data + floatType, + // Function pointer + &respthrottle + }, + // resp throttle p constant subtype + { + // ID + 0x0E, + // Command text + "respthrottlep", + // Type of the command data + floatType, + // Function pointer + &respthrottlep + }, + // resp throttle i constant subtype + { + // ID + 0x0F, + // Command text + "respthrottlei", + // Type of the command data + floatType, + // Function pointer + &respthrottlei + }, + // resp throttle d constant subtype + { + // ID + 0x10, + // Command text + "respthrottled", + // Type of the command data + floatType, + // Function pointer + &respthrottled + } + } + }, + + // UPDATE + { + // Message Type ID + 0x04, + + // Update Subtypes + { + // NONE subtype + { + // ID + 0x00, + // Command text + "update", + // Type of the command data + stringType, + // Function pointer + &update + } + } + }, + + // LOG + { + // Message Type ID + 0x05, + + // Log Subtypes + { + // NONE subtype + { + // ID + 0x00, + // Command text + "log", + // Type of the command data + stringType, + // Function pointer + &logdata + }, + // Response subtype + { + // ID + 0x01, + // Command text + "response", + // Type of the command data + stringType, + // Function pointer + &response + } + } + }, + +}; + +int debug(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("function for debug\n"); + return 0; +} + +int update(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + unsigned char update[28]; + memcpy(update, ((float *)packet), 28); + + int packetId = getInt(update, 0); + float y_pos = getFloat(update, 4); + float x_pos = getFloat(update, 8); + float alt_pos = getFloat(update, 12); + float roll = getFloat(update, 16); + float pitch = getFloat(update, 20); + float yaw = getFloat(update, 24); + + structs->log_struct.currentQuadPosition.packetId = packetId; + structs->log_struct.currentQuadPosition.y_pos = y_pos; + structs->log_struct.currentQuadPosition.x_pos = x_pos; + structs->log_struct.currentQuadPosition.alt_pos = alt_pos; + structs->log_struct.currentQuadPosition.roll = roll; + structs->log_struct.currentQuadPosition.pitch = pitch; + structs->log_struct.currentQuadPosition.yaw = yaw; + + printf("QUAD: VRPN Packet:"); + printf("Packet ID: %d\n", packetId); + printf("Y Position: %f\n", y_pos); + printf("X Position: %f\n", x_pos); + printf("Altitude Position: %f\n", alt_pos); + printf("Roll: %f\n", roll); + printf("Pitch: %f\n", pitch); + printf("Yaw: %f\n", yaw); + + printf("function for update\n"); + return 0; +} + +// Why is this here? +// This should be on the ground station side +int logdata(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("Logging: %s\n", packet); + return 0; +} + +int response(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("This is the response: %s\n", packet); + + return 0; +} + +// ------------------------------------------------------------------ + +int setyaw(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + + printf("%f\n", value); + + structs->setpoint_struct.desiredQuadPosition.yaw = value; + + printf("function for setyaw: %f\n", structs->setpoint_struct.desiredQuadPosition.yaw); + + return 0; +} + +int setyawp(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->parameter_struct.yaw_angle_pid.Kp = value; + + printf("function for setyawp: %f\n", structs->parameter_struct.yaw_angle_pid.Kp); + + return 0; +} + +int setyawd(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->parameter_struct.yaw_angle_pid.Kd = value; + + printf("function for setyawd: %f\n", structs->parameter_struct.yaw_angle_pid.Kd); + + return 0; +} + +int setroll(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->setpoint_struct.desiredQuadPosition.roll = value; + + printf("function for setroll: %f\n", structs->setpoint_struct.desiredQuadPosition.roll); + + return 0; +} + +int setrollp(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->parameter_struct.local_y_pid.Kp = value; + + printf("function for setrollp: %f\n", structs->parameter_struct.local_y_pid.Kp); + + return 0; +} + +int setrolld(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->parameter_struct.local_y_pid.Kd = value; + + printf("function for setrolld: %f\n", structs->parameter_struct.local_y_pid.Kd); + + return 0; +} + +int setpitch(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->setpoint_struct.desiredQuadPosition.pitch = value; + + printf("function for setpitch: %f\n", structs->setpoint_struct.desiredQuadPosition.pitch); + + return 0; +} + +int setpitchp(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->parameter_struct.local_x_pid.Kp = value; + + printf("function for setpitchp: %f\n", structs->parameter_struct.local_x_pid.Kp); + + return 0; +} + +int setpitchd(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->parameter_struct.local_x_pid.Kd = value; + + printf("function for setpitchd: %f\n", structs->parameter_struct.local_x_pid.Kd); + + return 0; +} + +// ------------------------------------------------------------ +// These should be renamed to altitude! +int setthrottle(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->setpoint_struct.desiredQuadPosition.alt_pos = value; + + printf("function for setthrottle: %f\n", structs->setpoint_struct.desiredQuadPosition.alt_pos); + + return 0; +} + +int setthrottlep(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->parameter_struct.alt_pid.Kp = value; + + printf("function for setthrottlep: %f\n", structs->parameter_struct.alt_pid.Kp); + + return 0; +} + +int setthrottlei(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->parameter_struct.alt_pid.Ki = value; + + printf("function for setthrottlei: %f\n", structs->parameter_struct.alt_pid.Ki); + + return 0; +} + +int setthrottled(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + float value; + + memcpy(&value, ((float *)packet), dataLen); + structs->parameter_struct.alt_pid.Kd = value; + + printf("function for setthrottled: %f\n", structs->parameter_struct.alt_pid.Kd); + + return 0; +} +int getyaw(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getyawp(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getyawd(unsigned char *packet, int dataLen, modular_structs_t *structs) { + return 0; +} +int getroll(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getrollp(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getrolld(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getpitch(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getpitchp(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getpitchd(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getthrottle(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getthrottlep(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getthrottlei(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int getthrottled(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} + +// These should be renamed to altitude! +// ------------------------------------------------------------ + + + + +int getgyro(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("function for getgyro\n"); + return 0; +} + +int getpitchangle(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("function for getpitchangle\n"); + return 0; +} + +int getrollangle(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("function for getrollangle\n"); + return 0; +} + + +int getaccel(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("function for getaccel\n"); + return 0; +} + + +int respgyro(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("function for respgyro\n"); + return 0; +} + +int resppitchangle(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("function for resppitchangle\n"); + return 0; +} + +int resprollangle(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("function for resprollangle\n"); + return 0; +} + + +int respaccel(unsigned char *packet, int dataLen, modular_structs_t *structs) +{ + printf("function for respaccel\n"); + return 0; +} + +int respyaw(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int respyawp(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int respyawd(unsigned char *packet, int dataLen, modular_structs_t *structs) { + return 0; +} +int resproll(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int resprollp(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int resprolld(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int resppitch(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int resppitchp(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int resppitchd(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int respthrottle(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int respthrottlep(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int respthrottlei(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} +int respthrottled(unsigned char *packet, int dataLen, modular_structs_t *structs){ + return 0; +} diff --git a/groundStation/src/backend/commands.h b/groundStation/src/backend/commands.h new file mode 100644 index 0000000000000000000000000000000000000000..b9c9956ea0150c81291f93d3a3c665d07be67295 --- /dev/null +++ b/groundStation/src/backend/commands.h @@ -0,0 +1,105 @@ +#ifndef _COMMANDS_H +#define _COMMANDS_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "type_def.h" + + +// ---------------------- +// Helper stuff + +#define MAX_TYPE 6 +#define MAX_SUBTYPE 100 + +enum Message{ + BEGIN_CHAR = 0xBE, + END_CHAR = 0xED +}; + +// This should also have double to avoid confusion with float values. +enum DataType +{ + floatType, + intType, + stringType +}; + +// MESSAGE SUBTYPES +struct MessageSubtype{ + char ID; + char cmdText[100]; + char cmdDataType; + int (*functionPtr)(unsigned char *command, int dataLen, modular_structs_t *structs); +}; + +// MESSAGE TYPES +struct MessageType{ + char ID; + struct MessageSubtype subtypes[MAX_SUBTYPE]; +}; + +int debug(unsigned char *c, int dataLen, modular_structs_t *structs); +int update(unsigned char *c, int dataLen, modular_structs_t *structs); +int logdata(unsigned char *c, int dataLen, modular_structs_t *structs); +int response(unsigned char *packet, int dataLen, modular_structs_t *structs); +int setyaw(unsigned char *c, int dataLen, modular_structs_t *structs); +int setyawp(unsigned char *c, int dataLen, modular_structs_t *structs); +int setyawd(unsigned char *c, int dataLen, modular_structs_t *structs); +int setroll(unsigned char *c, int dataLen, modular_structs_t *structs); +int setrollp(unsigned char *c, int dataLen, modular_structs_t *structs); +int setrolld(unsigned char *c, int dataLen, modular_structs_t *structs); +int setpitch(unsigned char *c, int dataLen, modular_structs_t *structs); +int setpitchp(unsigned char *c, int dataLen, modular_structs_t *structs); +int setpitchd(unsigned char *c, int dataLen, modular_structs_t *structs); +int setthrottle(unsigned char *c, int dataLen, modular_structs_t *structs); +int setthrottlep(unsigned char *c, int dataLen, modular_structs_t *structs); +int setthrottlei(unsigned char *c, int dataLen, modular_structs_t *structs); +int setthrottled(unsigned char *c, int dataLen, modular_structs_t *structs); +int getyaw(unsigned char *c, int dataLen, modular_structs_t *structs); +int getyawp(unsigned char *c, int dataLen, modular_structs_t *structs); +int getyawd(unsigned char *c, int dataLen, modular_structs_t *structs); +int getroll(unsigned char *c, int dataLen, modular_structs_t *structs); +int getrollp(unsigned char *c, int dataLen, modular_structs_t *structs); +int getrolld(unsigned char *c, int dataLen, modular_structs_t *structs); +int getpitch(unsigned char *c, int dataLen, modular_structs_t *structs); +int getpitchp(unsigned char *c, int dataLen, modular_structs_t *structs); +int getpitchd(unsigned char *c, int dataLen, modular_structs_t *structs); +int getthrottle(unsigned char *c, int dataLen, modular_structs_t *structs); +int getthrottlep(unsigned char *c, int dataLen, modular_structs_t *structs); +int getthrottlei(unsigned char *c, int dataLen, modular_structs_t *structs); +int getthrottled(unsigned char *c, int dataLen, modular_structs_t *structs); +int getaccel(unsigned char *c, int dataLen, modular_structs_t *structs); +int getgyro(unsigned char *c, int dataLen, modular_structs_t *structs); +int getpitchangle(unsigned char *c, int dataLen, modular_structs_t *structs); +int getrollangle(unsigned char *c, int dataLen, modular_structs_t *structs); +int respaccel(unsigned char *c, int dataLen, modular_structs_t *structs); +int respgyro(unsigned char *c, int dataLen, modular_structs_t *structs); +int resppitchangle(unsigned char *c, int dataLen, modular_structs_t *structs); +int resprollangle(unsigned char *c, int dataLen, modular_structs_t *structs); +int respyaw(unsigned char *c, int dataLen, modular_structs_t *structs); +int respyawp(unsigned char *c, int dataLen, modular_structs_t *structs); +int respyawd(unsigned char *c, int dataLen, modular_structs_t *structs); +int resproll(unsigned char *c, int dataLen, modular_structs_t *structs); +int resprollp(unsigned char *c, int dataLen, modular_structs_t *structs); +int resprolld(unsigned char *c, int dataLen, modular_structs_t *structs); +int resppitch(unsigned char *c, int dataLen, modular_structs_t *structs); +int resppitchp(unsigned char *c, int dataLen, modular_structs_t *structs); +int resppitchd(unsigned char *c, int dataLen, modular_structs_t *structs); +int respthrottle(unsigned char *c, int dataLen, modular_structs_t *structs); +int respthrottlep(unsigned char *c, int dataLen, modular_structs_t *structs); +int respthrottlei(unsigned char *c, int dataLen, modular_structs_t *structs); +int respthrottled(unsigned char *c, int dataLen, modular_structs_t *structs); +int respaccel(unsigned char *c, int dataLen, modular_structs_t *structs); +int respgyro(unsigned char *c, int dataLen, modular_structs_t *structs); +int resppitchangle(unsigned char *c, int dataLen, modular_structs_t *structs); +int resprollangle(unsigned char *c, int dataLen, modular_structs_t *structs); + +float getFloat(unsigned char* str, int pos); +int getInt(unsigned char* str, int pos); + +// TODO add in string to be read from the command line when sending a subtype of message +extern struct MessageType MessageTypes[MAX_TYPE]; + +#endif \ No newline at end of file diff --git a/groundStation/src/backend/communication.c b/groundStation/src/backend/communication.c new file mode 100644 index 0000000000000000000000000000000000000000..2dacd03f90afb8ed35b99a56f34ca785225e3350 --- /dev/null +++ b/groundStation/src/backend/communication.c @@ -0,0 +1,271 @@ +#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(char *command, unsigned char **formattedCommand) { + //fprintf(stderr, "length = %li , received '%s'\n", strlen(command), command); + char cmd[strlen(command)]; + strncpy(cmd, command, strlen(command)); + cmd[strlen(command)] = '\0'; + + tokenList_t tokens = tokenize(cmd); + float floatValue = 0.0; + int intValue = 0; + int valid; + metadata_t metadata; + + // ---------------------------------------------- + if(tokens.numTokens > 0) { + for(int type = 0; type < MAX_TYPE; type++) + { + for(int subtype = 0; subtype < MAX_SUBTYPE; subtype++) + { + if(strcmp(tokens.tokens[0], MessageTypes[type].subtypes[subtype].cmdText) == 0) + { + printf("Sending\n\ttype: %d, \n\tsubtype: %d\n\tcommand: %s\n", type, subtype, MessageTypes[type].subtypes[subtype].cmdText); + + // Make sure the second token is the right type + switch (MessageTypes[type].subtypes[subtype].cmdDataType) + { + // Validate the float input + case floatType: + metadata.begin_char = (char) BEGIN_CHAR; + metadata.msg_type = MessageTypes[type].ID; + metadata.msg_subtype = MessageTypes[type].subtypes[subtype].ID; + + if(MessageTypes[type].ID == 0x01) { + valid = checkFloat(tokens.tokens[1], &floatValue); + if(!valid) { + return -1; + } + metadata.data_len = sizeof(floatValue); + } else { + metadata.data_len = 0; + } + + metadata.msg_id = msgNum++; + formatPacket(&metadata, &floatValue, formattedCommand); + break; + + // Validate the integer input + case intType: + metadata.begin_char = (char) BEGIN_CHAR; + metadata.msg_type = MessageTypes[type].ID; + metadata.msg_subtype = MessageTypes[type].subtypes[subtype].ID; + + if(MessageTypes[type].ID == 0x01) { + valid = checkInt(tokens.tokens[1], &intValue); + if(!valid) { + return -1; + } + metadata.data_len = sizeof(intValue); + } else { + metadata.data_len = 0; + } + + metadata.msg_id = msgNum++; + formatPacket(&metadata, &intValue, formattedCommand); + + break; + + // Validate the string input (doesn't need to happen) + case stringType: + metadata.begin_char = (char) BEGIN_CHAR; + metadata.msg_type = MessageTypes[type].ID; + metadata.msg_subtype = MessageTypes[type].subtypes[subtype].ID; + 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 & 0x000000ff); + (*formattedCommand)[4] = ((metadata->msg_id >> 8) & 0x000000ff); + + // 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, modular_structs_t *structs) { + 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) { + /* Null check*/ + if (MessageTypes[(unsigned char)metadata.msg_type].subtypes[ + (unsigned char) metadata.msg_subtype].functionPtr) { + // Call the appropriate subtype function + (* (MessageTypes[(unsigned char)metadata.msg_type].subtypes[(unsigned char)metadata.msg_subtype].functionPtr))(data, metadata.data_len, structs); + } + + return 0; + } + + // Only gets here if there is an error + return -1; +} diff --git a/groundStation/src/backend/communication.h b/groundStation/src/backend/communication.h new file mode 100644 index 0000000000000000000000000000000000000000..746b9f5d45ce9199588ff45b58f5bdaa5d669ad3 --- /dev/null +++ b/groundStation/src/backend/communication.h @@ -0,0 +1,19 @@ +#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(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, modular_structs_t *structs); +int logData(unsigned char *log_msg, unsigned char *formattedCommand); + +#endif \ No newline at end of file diff --git a/groundStation/src/backend/config.h b/groundStation/src/backend/config.h new file mode 100644 index 0000000000000000000000000000000000000000..bf88e6e2b7cf09db273dba3ecc5bcf3cee7d2211 --- /dev/null +++ b/groundStation/src/backend/config.h @@ -0,0 +1,20 @@ +#ifndef __CONFIG_H +#define __CONFIG_H + + +#define DEFAULT_SOCKET "/var/run/ucart.socket" +#define SOCKET_ENV "UCART_SOCKET" +#define NOQUAD_ENV "UCART_NO_QUAD" +#define NOVRPN_ENV "UCART_NO_VRPN" + + +// If you are planning on using any of these env vars and you have +// exported them with normal user rights. You will need to run the +// backend with sudo elevation and with the --preserve-env flag or -E +#define QUAD_WIFI_ENV "UCART_USE_WIFI" +#define QUAD_IP_ENV "UCART_QUAD_IP" +#define QUAD_IP_DEFAULT "192.168.4.1" +#define QUAD_PORT_ENV "UCART_QUAD_PORT" +#define QUAD_PORT_DEFAULT 8080 + +#endif diff --git a/groundStation/src/backend/logger.c b/groundStation/src/backend/logger.c new file mode 100644 index 0000000000000000000000000000000000000000..028cfeac16c173e59154a1c9c8321eeba29d0344 --- /dev/null +++ b/groundStation/src/backend/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/backend/logger.h b/groundStation/src/backend/logger.h new file mode 100644 index 0000000000000000000000000000000000000000..edfb365ffce6606e7b5bd8d1f4305c5f697180a9 --- /dev/null +++ b/groundStation/src/backend/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/backend/old_main.cold b/groundStation/src/backend/old_main.cold new file mode 100644 index 0000000000000000000000000000000000000000..5234bcc567a6e574b30eea671b3f977a7211f478 --- /dev/null +++ b/groundStation/src/backend/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/backend/type_def.h b/groundStation/src/backend/type_def.h new file mode 100644 index 0000000000000000000000000000000000000000..55dca9e8fb27c26397b00961b7dbec8bbe0d4136 --- /dev/null +++ b/groundStation/src/backend/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/backend/vrpn_tracker.cpp b/groundStation/src/backend/vrpn_tracker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f9b56a9909ac1512d6d2164d313214200645e22 --- /dev/null +++ b/groundStation/src/backend/vrpn_tracker.cpp @@ -0,0 +1,178 @@ +#include <iostream> +#include <algorithm> +#include <functional> + +#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); + + TrackerData::TrackerData() : + x(0.0), y(0.0), z(0.0), + pitch(0.0), roll(0.0), yaw(0.0), + fps(0.0), timestamp() + { + } + + Tracker::Tracker(std::string server) : + remote(server.c_str()), + stop_flag(0), + trackerData() + { + } + + Tracker::Tracker(const char * server) : + remote(server), + stop_flag(0), + trackerData() + { + 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; + for(auto i = cb_vector.begin(); i != cb_vector.end(); ++i) { + (*i)(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; +}; + +void cb_wrapper(void (*cb)(struct ucart_vrpn_TrackerData *), + 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; + data.fps = td.fps; + (*cb)(&data); +} + +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); + return inst; + } 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 { + auto new_cb = bind(cb_wrapper, cb, std::placeholders::_1); + inst->t->addCallback(new_cb); + } 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/backend/vrpn_tracker.hpp b/groundStation/src/backend/vrpn_tracker.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d10ad6e79a0bd1a3db42d0c18b4b6b286146538a --- /dev/null +++ b/groundStation/src/backend/vrpn_tracker.hpp @@ -0,0 +1,100 @@ +/* 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 { + public: + double x; + double y; + double z; + + double pitch; + double roll; + double yaw; + + double fps; + timeval timestamp; + + TrackerData(); + }; + + 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 diff --git a/groundStation/src/cli/cli.c b/groundStation/src/cli/cli.c new file mode 100644 index 0000000000000000000000000000000000000000..58fe14b8c9ac71026860ae4c7687ae31fa38ea26 --- /dev/null +++ b/groundStation/src/cli/cli.c @@ -0,0 +1,53 @@ +#include <stdio.h> +#include <string.h> +#include <err.h> +#include <libgen.h> + +#include "cli.h" + +int main(int argc, char **argv) +{ + int cmdID = -1; + char * command; + int i , useSymlink = 0; + struct backend_conn *conn; + + command = basename(argv[0]); + for(i = 0; i < MAX_COMMANDS; ++i) { + if (strncmp(command, commandNames[i], strlen(commandNames[i])) == 0) + { + cmdID = i; + useSymlink = 1; + } + } + + if(cmdID == -1) { + command = argv[1]; + for(i = 0; i < MAX_COMMANDS; ++i) { + if (strncmp(command, commandNames[i], strlen(commandNames[i])) == 0) + { + cmdID = i; + } + } + } + + if(cmdID == -1){ + printf("Could not match input with a command. Please try again...\n"); + return -1; + } + + printf("Parsed Command : %s\n", commandNames[cmdID]); + conn = ucart_backendConnect(); + if(conn == NULL) { + return -1; + } + + if(useSymlink) { + (*cli_functions[cmdID]) (conn, argc, &argv[0]); + }else { + (*cli_functions[cmdID]) (conn, argc-1, &argv[1]); + } + + ucart_backendDisconnect(conn); + return 0; +} diff --git a/groundStation/src/cli/cli.h b/groundStation/src/cli/cli.h new file mode 100644 index 0000000000000000000000000000000000000000..f4c2fe3f4783b9b4a4dd1e3482cdf3034cbaf269 --- /dev/null +++ b/groundStation/src/cli/cli.h @@ -0,0 +1,32 @@ +#ifndef __CLI_H +#define __CLI_H + +#include "cli_monitor.h" +#include "cli_setpid.h" +#include "cli_getpid.h" +// #include "cli_getimu.h" +enum CommandNameIds{ + CMD_MONITOR, + CMD_GETPID, + CMD_SETPID, + CMD_GETIMU, + MAX_COMMANDS +}; + +typedef int (*cli_function_ptr)(struct backend_conn *, int, char **); +static cli_function_ptr cli_functions[] = { + &cli_monitor, + &cli_getpid, + &cli_setpid + //&cli_getimu +}; + +static char* commandNames[MAX_COMMANDS] = { + "monitor", + "getpid", + "setpid", + "getImu" +}; + + +#endif diff --git a/groundStation/src/cli/cli_getimu.h b/groundStation/src/cli/cli_getimu.h new file mode 100644 index 0000000000000000000000000000000000000000..3f69e4b154b9678845b7604a7d91c6b83bf72ee5 --- /dev/null +++ b/groundStation/src/cli/cli_getimu.h @@ -0,0 +1,9 @@ +#ifndef CLI_GETIMU_H +#define CLI_GETIMU_H +#include "frontend_getimu.h" + +int cli_getimu( + struct backend_conn * conn, + struct frontend_pid_data * pid_data); + +#endif \ No newline at end of file diff --git a/groundStation/src/cli/cli_getpid.c b/groundStation/src/cli/cli_getpid.c new file mode 100644 index 0000000000000000000000000000000000000000..bb887ca24242fb05f4a22710becf248cdb6f8f62 --- /dev/null +++ b/groundStation/src/cli/cli_getpid.c @@ -0,0 +1,105 @@ +#include <stdio.h> +#include <unistd.h> +#include <getopt.h> + +#include "cli_getpid.h" + +int cli_getpid(struct backend_conn * conn, int argc, char **argv) { + int c; + static int getRoll = 0, getPitch = 0, getYaw = 0, getAll = 0; + static int getRollV = 0, getPitchV = 0, getYawV = 0; + static int getHeight = 0, getLat = 0, getLong = 0; + struct frontend_pid_data pid_data; + static struct option long_options[] = { + /* These options don’t set a flag. We distinguish them by their indices. */ + {"roll", no_argument, &getRoll, 1}, + {"pitch", no_argument, &getPitch, 1}, + {"yaw", no_argument, &getYaw, 1}, + {"rollv", no_argument, &getYawV, 1}, + {"pitchv", no_argument, &getYawV, 1}, + {"yawv", no_argument, &getYawV, 1}, + {"height", no_argument, &getHeight, 1}, + {"lat", no_argument, &getLat, 1}, + {"long", no_argument, &getLong, 1}, + {0, 0, 0, 0} + }; + + while (1) + { + /* getopt_long stores the option index here. */ + int option_index = 0; + + c = getopt_long(argc, argv, "a", long_options, &option_index); + + /* Detect the end of the options. */ + if (c == -1) + break; + + if (c == 'a') { + getAll = 1; + } + } + + int result; + if(getAll) { + pid_data.controller = PID_ROLL; + if ((result = getValues(conn, &pid_data))) { + return result; + } + pid_data.controller = PID_PITCH; + if ((result = getValues(conn, &pid_data))) { + return result; + } + pid_data.controller = PID_YAW; + if ((result = getValues(conn, &pid_data))) { + return result; + } + } else { + if(getPitch) { + pid_data.controller = PID_PITCH; + if ((result = getValues(conn, &pid_data))) { + return result; + } + } + if(getRoll) { + pid_data.controller = PID_ROLL; + if ((result = getValues(conn, &pid_data))) { + return result; + } + } + if(getYaw) { + pid_data.controller = PID_YAW; + if ((result = getValues(conn, &pid_data))) { + return result; + } + } + } + + return 0; +} + +int getValues(struct backend_conn * conn, struct frontend_pid_data * pid_data) { + if(frontend_getpid(conn, pid_data)) { + return 1; + } + + return 0; + + switch(pid_data->controller) { + case PID_PITCH : + printf("Pitch Constants: P = %f\tI = %f\tD = %f\n", + pid_data->p, pid_data->i, pid_data->d); + break; + case PID_ROLL : + printf("Roll Constants: P = %f\tI = %f\tD = %f\n", + pid_data->p, pid_data->i, pid_data->d); + break; + case PID_YAW : + printf("Yaw Constants: P = %f\tI = %f\tD = %f\n", + pid_data->p, pid_data->i, pid_data->d); + break; + default : + break; + } + return 0; +} diff --git a/groundStation/src/cli/cli_getpid.h b/groundStation/src/cli/cli_getpid.h new file mode 100644 index 0000000000000000000000000000000000000000..f5c0b15e4a2f2c7c5ad605a389b0c1b52a1344a6 --- /dev/null +++ b/groundStation/src/cli/cli_getpid.h @@ -0,0 +1,9 @@ +#ifndef CLI_GETPID_H +#define CLI_GETPID_H + +#include "frontend_getpid.h" + +int getValues(struct backend_conn *, struct frontend_pid_data *); +int cli_getpid(struct backend_conn * conn, int argc, char ** argv); + +#endif \ No newline at end of file diff --git a/groundStation/src/cli/cli_monitor.c b/groundStation/src/cli/cli_monitor.c new file mode 100644 index 0000000000000000000000000000000000000000..4494e65b08320fb4831a733ff83d2bc53e803ae0 --- /dev/null +++ b/groundStation/src/cli/cli_monitor.c @@ -0,0 +1,102 @@ +#define _GNU_SOURCE + +#include <stdio.h> +#include <unistd.h> +#include <getopt.h> +#include <time.h> +#include <unistd.h> +#include <err.h> + +#include "cli_monitor.h" +#include "frontend_tracker.h" + +int cli_monitor(struct backend_conn * conn, int argc, char **argv) { + int c, result; + int countFlag = 0; + + int count, rate = 10; + int forever = 0; + + + while ((c = getopt(argc, argv, "fc:r:")) != -1) { + switch(c) { + case 'c' : + count = atoi(optarg); + countFlag = 1; + break; + case 'r' : + rate = atoi(optarg) + 1; + break; + case 'f' : + forever = 1; + break; + default : + break; + } + } + + if (forever) { + for (;;) { + struct timespec req; + if (rate == 1) { + req.tv_sec = 1; + req.tv_nsec = 0; + } else { + req.tv_sec = 0; + req.tv_nsec = 1000000000 / rate; + } + nanosleep(&req, NULL); + monitor(conn); + } + } else if (countFlag) { + for (int i = 0; i < count; i++) { + result = monitor(conn); + + struct timespec req; + if (rate == 1) { + req.tv_sec = 1; + req.tv_nsec = 0; + } else { + req.tv_sec = 0; + req.tv_nsec = 1000000000 / rate; + } + nanosleep(&req, NULL); + } + } else { + return monitor(conn); + } + + return result; +} + +int monitor(struct backend_conn * conn) { + /* Get tracker data */ + struct frontend_tracker_data td; + if (frontend_track(conn, &td)) { + errx(1, "Error reading tracker data"); + } + + /* TODO: Get PID constants and status */ + /* It might be a good idea to only read the pid constants + * every few seconds, so count iterations and only do it if + * this is every (rate * 2 or 3) pass through the loop + */ + + /* Print stuff on screen */ + /* Assuming a tab width of 8 columns */ + printf("\033[2J"); + printf("STATUS: NA\n"); + printf("CTRLR :\tP\tR\tY\tP_V\tR_V\tY_V\tH\tLAT\tLON\n"); + printf(" P :\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\n", + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + printf(" I :\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\n", + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + printf(" D :\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\n", + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + printf("PosAtt:\tH\tLAT\tLON\tP\tR\tY\n"); + printf(" :\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\t%6.3lf\n", + td.height, td.lateral, td.longitudinal, + td.pitch, td.roll, td.yaw); + + return 0; +} diff --git a/groundStation/src/cli/cli_monitor.h b/groundStation/src/cli/cli_monitor.h new file mode 100644 index 0000000000000000000000000000000000000000..308721bb89f177da345269971a8d63b32d9dc39c --- /dev/null +++ b/groundStation/src/cli/cli_monitor.h @@ -0,0 +1,14 @@ +#ifndef CLI_MONITOR_H +#define CLI_MONITOR_H + +#include <time.h> + +#include "frontend_getpid.h" + +#define SECOND_IN_NANO 1000000000 + +int cli_monitor(struct backend_conn * conn, int argc, char **argv); + +// Return 0 on success and 1 otherwise +int monitor(struct backend_conn * conn); +#endif \ No newline at end of file diff --git a/groundStation/src/cli/cli_setpid.c b/groundStation/src/cli/cli_setpid.c new file mode 100644 index 0000000000000000000000000000000000000000..2fdc16fdb43e6e42ea5eea16153d98eeb5978302 --- /dev/null +++ b/groundStation/src/cli/cli_setpid.c @@ -0,0 +1,81 @@ +#include <stdio.h> +#include <unistd.h> +#include <getopt.h> + +#include "cli_setpid.h" +#include "frontend_setpid.h" + +int cli_setpid(struct backend_conn * conn, int argc, char **argv) { + int c; + static int setRoll = 0, setPitch = 0, setYaw = 0, setAll = 0; + static int setRollV = 0, setPitchV = 0, setYawV = 0; + static int setHeight = 0, setLat = 0, setLong = 0; + struct frontend_pid_data pid_data; + static int mask; + static float pval = 0, ival = 0, dval = 0; + static struct option long_options[] = { + /* These options don’t set a flag. We distinguish them by their indices. */ + {"roll", no_argument, &setRoll, 1}, + {"pitch", no_argument, &setPitch, 1}, + {"yaw", no_argument, &setYaw, 1}, + {"rollv", no_argument, &setRollV, 1}, + {"pitchv", no_argument, &setPitchV, 1}, + {"yawv", no_argument, &setYawV, 1}, + {"height", no_argument, &setHeight, 1}, + {"lat", no_argument, &setLat, 1}, + {"long", no_argument, &setLong, 1}, + {0, 0, 0, 0} + }; + + while (1) + { + /* getopt_long stores the option index here. */ + int option_index = 0; + + c = getopt_long(argc, argv, "p:i:d:", long_options, &option_index); + + /* Detect the end of the options. */ + if (c == -1) + break; + + switch(c) { + case 'p' : + pid_data.p = atof(optarg); + mask |= SET_P; + break; + case 'i' : + pid_data.i = atof(optarg); + mask |= SET_I; + break; + case 'd' : + pid_data.d = atof(optarg); + mask |= SET_D; + break; + default : + break; + } + } + + if(setRoll) { + pid_data.controller = PID_ROLL; + } else if (setYaw) { + pid_data.controller = PID_YAW; + } else if (setPitch) { + pid_data.controller = PID_PITCH; + } else if (setRollV) { + pid_data.controller = PID_ROLL_RATE; + } else if (setPitchV) { + pid_data.controller = PID_PITCH_RATE; + } else if (setYawV) { + pid_data.controller = PID_YAW_RATE; + } else if (setHeight) { + pid_data.controller = PID_HEIGHT; + } else if (setLong) { + pid_data.controller = PID_LAT; + } else if (setLat) { + pid_data.controller = PID_LONG; + } + + frontend_setpid(conn, &pid_data, mask); + return 0; +} diff --git a/groundStation/src/cli/cli_setpid.h b/groundStation/src/cli/cli_setpid.h new file mode 100644 index 0000000000000000000000000000000000000000..87fb0a4f40e91ed704972bd17e4859991d91641b --- /dev/null +++ b/groundStation/src/cli/cli_setpid.h @@ -0,0 +1,8 @@ +#ifndef _CLI_SETPID_H +#define _CLI_SETPID_H + +#include "frontend_setpid.h" + +int cli_setpid(struct backend_conn * conn, int argc, char ** argv); + +#endif diff --git a/groundStation/src/frontend/frontend_common.c b/groundStation/src/frontend/frontend_common.c new file mode 100644 index 0000000000000000000000000000000000000000..cbedbe42a6dcbed1622f2109d833a75a1ce6d7ba --- /dev/null +++ b/groundStation/src/frontend/frontend_common.c @@ -0,0 +1,93 @@ +#define _GNU_SOURCE + +#include "frontend_common.h" +#include "config.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <err.h> + +struct backend_conn { + FILE * socket; + + size_t len; + char * buf; +}; + +struct backend_conn * ucart_backendConnect() +{ + int s; + struct sockaddr_un remote; + char str[100]; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + perror("socket"); + exit(1); + } + + struct backend_conn * conn = NULL; + + printf("Trying to connect...\n"); + + remote.sun_family = AF_UNIX; + char * sock_env = getenv(SOCKET_ENV); + strcpy(remote.sun_path, sock_env ? sock_env : DEFAULT_SOCKET); + if (connect(s, (struct sockaddr *)&remote, sizeof(remote)) == -1) { + perror("connect"); + goto fail_final; + } + + conn = malloc(sizeof(struct backend_conn)); + if (conn == NULL) { + perror("malloc"); + goto fail_sock; + } + + conn->len = 0; + conn->buf = NULL; + conn->socket = fdopen(s, "r+"); + if (conn->socket == NULL) { + perror("fdopen"); + goto fail_malloc_conn; + } + if (setvbuf(conn->socket, NULL, _IONBF, 0)) { + warn("setvbuf"); + } + + /* success */ + goto fail_final; + +fail_malloc_conn: + free(conn); + conn = NULL; +fail_sock: + close(s); +fail_final: + return conn; +} + +void ucart_backendDisconnect(struct backend_conn * conn) +{ + fclose(conn->socket); + if (conn->buf) { + free(conn->buf); + } + free(conn); +} + +char * ucart_backendGetline(struct backend_conn *conn) +{ + getline(&conn->buf, &conn->len, conn->socket); + return conn->buf; +} + +int ucart_backendWrite(struct backend_conn *conn, const char * line) +{ + return fputs(line, conn->socket); +} diff --git a/groundStation/src/frontend/frontend_common.h b/groundStation/src/frontend/frontend_common.h new file mode 100644 index 0000000000000000000000000000000000000000..c8ea7877190c29ff41f02f9aa92a3987c80e2de6 --- /dev/null +++ b/groundStation/src/frontend/frontend_common.h @@ -0,0 +1,22 @@ +#ifndef FRONTEND_COMMON_H +#define FRONTEND_COMMON_H +#include <stdlib.h> + +struct backend_conn; + +/* Start connection to quad */ +struct backend_conn * ucart_backendConnect(void); + +/* Stop connection to quad */ +void ucart_backendDisconnect(struct backend_conn * conn); + +/* Get a line from the backend. + * + * The line will remain valid until the next call to ucart_backendGetline. + */ +char * ucart_backendGetline(struct backend_conn * conn); + +/* Write a line to the backend */ +int ucart_backendWrite(struct backend_conn * backend, const char * line); + +#endif diff --git a/groundStation/src/frontend/frontend_getpid.c b/groundStation/src/frontend/frontend_getpid.c new file mode 100644 index 0000000000000000000000000000000000000000..fbde309c3169e4d2429f5c3cd06c7674709491a8 --- /dev/null +++ b/groundStation/src/frontend/frontend_getpid.c @@ -0,0 +1,46 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "frontend_getpid.h" +#include "pid_common.h" + +/* Get a specified PID. + * + * Example: + * + * struct frontend_pid_data pid_data; + * pid_data.pid = PID_PITCH; + * if (frontend_getpid(conn, &pid_data)) { + * error + * } else { + * pid_data.p, pid_data.i, and pid_data.d are filled + * } + * + * Returns 0 on success, 1 on error + */ +int frontend_getpid( + struct backend_conn * conn, struct frontend_pid_data * pid_data) { + + char line[100] = ""; + switch (pid_data->controller) { + case PID_PITCH : + strncpy(line, "getpitchp\ngetpitchd\n", 20); + break; + case PID_ROLL : + strncpy(line, "getrollp\ngetrolld\n", 18); + break; + case PID_YAW : + strncpy(line, "getyawp\ngetyawd\n", 17); + break; + default : + return 1; + } + + int size; + if((size = ucart_backendWrite(conn, line)) < 0 ) { + return 1; + } + + return 0; +} diff --git a/groundStation/src/frontend/frontend_getpid.h b/groundStation/src/frontend/frontend_getpid.h new file mode 100644 index 0000000000000000000000000000000000000000..977ae97f314a3c5b3891f5d60e56929aa03d6db9 --- /dev/null +++ b/groundStation/src/frontend/frontend_getpid.h @@ -0,0 +1,26 @@ +#ifndef FRONTEND_GETPID_H +#define FRONTEND_GETPID_H + +#include "frontend_common.h" +#include "pid_common.h" + +/* Get a specified PID. + * + * Example: + * + * struct frontend_pid_data pid_data; + * pid_data.pid = PITCH; + * if (frontend_getpid(conn, &pid_data)) { + * error + * } else { + * pid_data.p, pid_data.i, and pid_data.d are filled + * } + * + * Returns 0 on success, 1 on error + */ +int frontend_getpid( + struct backend_conn * conn, + struct frontend_pid_data * pid_data); + + +#endif diff --git a/groundStation/src/frontend/frontend_setpid.c b/groundStation/src/frontend/frontend_setpid.c new file mode 100644 index 0000000000000000000000000000000000000000..58bf4109cd1c50d22486e464c101c9f8dd3c367e --- /dev/null +++ b/groundStation/src/frontend/frontend_setpid.c @@ -0,0 +1,82 @@ +#include <err.h> +#include <stdio.h> + +#include "frontend_setpid.h" +#include "pid_common.h" +#include "frontend_common.h" + +int frontend_setpid( + struct backend_conn * conn, + struct frontend_pid_data * pid_data, + int mask) +{ + if (conn == NULL) { + return -1; + } + + char * controller; + switch (pid_data->controller) { + case PID_PITCH: + controller = "pitch"; + break; + case PID_ROLL: + controller = "roll"; + break; + case PID_YAW: + controller = "yaw"; + break; + case PID_PITCH_RATE: + controller = "pitchrate"; + break; + case PID_ROLL_RATE: + controller = "rollrate"; + break; + case PID_YAW_RATE: + controller = "yawrate"; + break; + case PID_HEIGHT: + controller = "height"; + break; + case PID_LAT: + controller = "lat"; + break; + case PID_LONG: + controller = "long"; + break; + default: + warnx("Unsupported PID constant"); + return -1; + } + + char buffer[2048]; + /* Set the P, I, and D */ + if (mask & SET_P) { + if (snprintf(buffer, 2048, + "set%sp %f\n", + controller, + pid_data->p) >= 2048) { + errx(0, "Buffer to small to format!"); + } + ucart_backendWrite(conn, buffer); + } + if (mask & SET_I) { + if (snprintf(buffer, 2048, + "set%si %f\n", + controller, + pid_data->i) >= 2048) { + errx(0, "Buffer to small to format!"); + } + ucart_backendWrite(conn, buffer); + } + if (mask & SET_D) { + if (snprintf(buffer, 2048, + "set%sd %f\n", + controller, + pid_data->d) >= 2048) { + errx(0, "Buffer to small to format!"); + } + ucart_backendWrite(conn, buffer); + } + + return 0; +} diff --git a/groundStation/src/frontend/frontend_setpid.h b/groundStation/src/frontend/frontend_setpid.h new file mode 100644 index 0000000000000000000000000000000000000000..f1deb5d3d91568939d5bd44ffab07f082a290233 --- /dev/null +++ b/groundStation/src/frontend/frontend_setpid.h @@ -0,0 +1,17 @@ +#ifndef _FRONTEND_SETPID_H +#define _FRONTEND_SETPID_H + +#include "pid_common.h" +#include "frontend_common.h" + +int frontend_setpid( + struct backend_conn * conn, + struct frontend_pid_data * pid_data, + int mask); + +#define SET_P 0x01 +#define SET_I 0x02 +#define SET_D 0x04 +#define SET_ALL (SET_P | SET_I | SET_D) + +#endif diff --git a/groundStation/src/frontend/frontend_tracker.c b/groundStation/src/frontend/frontend_tracker.c new file mode 100644 index 0000000000000000000000000000000000000000..94597b63c66d96c4d5197dd94edf138b38a956a2 --- /dev/null +++ b/groundStation/src/frontend/frontend_tracker.c @@ -0,0 +1,42 @@ +#include <err.h> +#include <string.h> +#include <stdio.h> + +#include "frontend_tracker.h" + +#define MAGIC "TRACKERDATA" + +int frontend_track( + struct backend_conn * conn, + struct frontend_tracker_data * data) +{ + ucart_backendWrite(conn, MAGIC "\n"); + + char * line; + for (;;) { + line = ucart_backendGetline(conn); + if (line == NULL) { + warnx("Line not returned from backend"); + return 1; + } + + if (strncmp(line, MAGIC, strlen(MAGIC)) == 0) { + break; + } + } + + if (strncmp(line, MAGIC " ERROR", strlen(MAGIC " ERROR")) == 0) { + warnx("Backend returned an error: %s", strstr(line, "ERROR")); + return 1; + } + + /* Line format: Height Lat Long Pitch Roll Yaw */ + sscanf(line, MAGIC " %lf %lf %lf %lf %lf %lf ", + &data->height, + &data->lateral, + &data->longitudinal, + &data->pitch, + &data->roll, + &data->yaw); + return 0; +} diff --git a/groundStation/src/frontend/frontend_tracker.h b/groundStation/src/frontend/frontend_tracker.h new file mode 100644 index 0000000000000000000000000000000000000000..5fa82487cafb8650a4f990ef9fed0c4ea4154405 --- /dev/null +++ b/groundStation/src/frontend/frontend_tracker.h @@ -0,0 +1,27 @@ +#ifndef _FRONTEND_TRACKER_H +#define _FRONTEND_TRACKER_H + +#include "frontend_common.h" + +/* Struct containing pos/att data */ +struct frontend_tracker_data { + double height; + double lateral; + double longitudinal; + double pitch; + double roll; + double yaw; +}; + +/* Get pos/att data from the tracking system + * + * conn: IN Connection to quad + * data: OUT Data is written to this struct + * + * Returns 0 on success, nonzero on error + * + */ +int frontend_track(struct backend_conn * conn, + struct frontend_tracker_data *data); + +#endif diff --git a/groundStation/src/frontend/pid_common.h b/groundStation/src/frontend/pid_common.h new file mode 100644 index 0000000000000000000000000000000000000000..c40c4fb42fb19bf7caf22a03effe091fb7621ece --- /dev/null +++ b/groundStation/src/frontend/pid_common.h @@ -0,0 +1,26 @@ +#ifndef PID_COMMON_H +#define PID_COMMON_H + +enum pid_controller { + PID_PITCH, + PID_ROLL, + PID_YAW, + PID_PITCH_RATE, + PID_ROLL_RATE, + PID_YAW_RATE, + PID_HEIGHT, /* Z */ + PID_LAT, /* X */ + PID_LONG, /* Y */ + PID_NUM_PIDS +}; + +struct frontend_pid_data { + enum pid_controller controller; + + float p; + float i; + float d; +}; + + +#endif diff --git a/groundStation/src/vrpn b/groundStation/src/vrpn new file mode 160000 index 0000000000000000000000000000000000000000..99c54dbefe04897cc7c146101a126f62c0e65f97 --- /dev/null +++ b/groundStation/src/vrpn @@ -0,0 +1 @@ +Subproject commit 99c54dbefe04897cc7c146101a126f62c0e65f97