#include "main.h"

struct timespec ts;

// management information base in packet header
static u8 pinfo_legacy_rates[] = {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108};
static u8 pinfo_mcs_rates[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
// radiotap header stores data rate as multiples of 500 Kbps by definition (https://www.radiotap.org/fields/Rate.html)
// But the modified ath9k driver does not allow the respective values for 11p. It only accepts valid values for legacy
// wifi and computes internally the real data rate based on channel bandwidth which leads to multiples of 250 Kbps in
// case of 11p. This is only visible in tx direction.
static u8 pinfo_dot11p_rates[] = {12, 18, 24, 36, 48, 72, 96, 108};

// physical data rate in mbps
static double data_rate_legacy_rates[] = {1.0, 2.0, 5.5, 11.0, 6.0, 9.0, 12.0, 18.0, 24.0, 36.0, 48.0, 54.0};
static double data_rate_dot11p_rates[] = {3.0, 4.5, 6.0, 9.0, 12.0, 18.0, 24.0, 27.0};
// todo: data rate of mcs rates depends on settings of bandwidth and guard interval, get access and check
static double data_rate_mcs_rates[] = {6.5,  13.0, 19.5, 26.0, 39.0,  52.0,  58.5,  65.0, 13.0, 26.0, 39.0, 52.0, 78.0, 104.0, 117.0, 130.0};	// 20MHz + 800ns GI


#define BAUDRATE B115200
#define MODEMDEVICE "/dev/ttyACM0" /*UART NAME IN PROCESSOR*/
struct termios oldtp, newtp;

void open_serial_port(struct global *global)
{
	char path[1024];

	global->gps_fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY |O_NDELAY );
	global->gps_available = false;
	if (global->gps_fd < 0) {
		perror(MODEMDEVICE);
		return;
	}

	global->gps_available = true;

	snprintf(path, sizeof(path), "%s/%s-gps_trace_%zd_%zd.dump",
			global->out_path,
			print_addr(global->wlan_addr),
			ts.tv_sec,
			ts.tv_nsec);
	path[sizeof(path) - 1] = 0;

	global->gps_fp = fopen(path, "w");

	// config
	fcntl(global->gps_fd,F_SETFL,0);
	tcgetattr(global->gps_fd,&oldtp); /* save current serial port settings */
	// tcgetattr(fd,&newtp); /* save current serial port settings */
	bzero(&newtp, sizeof(newtp));
	// bzero(&oldtp, sizeof(oldtp));

	newtp.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
	newtp.c_iflag = IGNPAR | ICRNL;
	newtp.c_oflag = 0;
	newtp.c_lflag = ICANON;
	newtp.c_cc[VINTR]	= 0;	 /* Ctrl-c */
	newtp.c_cc[VQUIT]	= 0;	 /* Ctrl-\ */
	newtp.c_cc[VERASE]   = 0;	 /* del */
	newtp.c_cc[VKILL]	= 0;	 /* @ */
	// newtp.c_cc[VEOF]	 = 4;	 /* Ctrl-d */
	newtp.c_cc[VTIME]	= 0;	 /* inter-character timer unused */
	newtp.c_cc[VMIN]	 = 0;	 /* blocking read until 1 character arrives */
	newtp.c_cc[VSWTC]	= 0;	 /* '\0' */
	newtp.c_cc[VSTART]   = 0;	 /* Ctrl-q */
	newtp.c_cc[VSTOP]	= 0;	 /* Ctrl-s */
	newtp.c_cc[VSUSP]	= 0;	 /* Ctrl-z */
	newtp.c_cc[VEOL]	 = 0;	 /* '\0' */
	newtp.c_cc[VREPRINT] = 0;	 /* Ctrl-r */
	newtp.c_cc[VDISCARD] = 0;	 /* Ctrl-u */
	newtp.c_cc[VWERASE]  = 0;	 /* Ctrl-w */
	newtp.c_cc[VLNEXT]   = 0;	 /* Ctrl-v */
	newtp.c_cc[VEOL2]	= 0;	 /* '\0' */


	// default values
	global->gps.dlat = 0;
	global->gps.dlon = 0;
}

void ev_handler_gps_read(int fd, short ev __unused, void *arg){
	ssize_t length;
	struct global *global = arg;
	char log[1024];
	char buffer[100];
	char temp_buffer[100];
	char delimiter[] = ",";
	char *ptr;

	clock_gettime(CLOCK_REALTIME, &global->gps.ts_sys);
	length  = read(fd, &buffer, sizeof(buffer));

	if (length < 0) {
		printf("Error reading from serial port\n");
		return;
	}

	if (length == 0) {
		printf("No more data\n");
		return;
	}

	buffer[length] = '\0';
	strcpy(temp_buffer, buffer);
	ptr = strtok(temp_buffer, delimiter);
	if(strcmp(ptr, "$GPGGA") != 0)
		return;

	printf("%s", buffer);

	if (global->rate_chosen < 0) {
		if (global->dot11p_mode) {
			snprintf(log, sizeof(log), "{\"ts\": %zd.%zd, \"round\": %d, \"legacy\": %f, \"mcs\": %d, \"gps\": %.*s},\n",
					 global->gps.ts_sys.tv_sec, global->gps.ts_sys.tv_nsec,global->output_round,  data_rate_dot11p_rates[-(global->rate_chosen + 1)], global->bitrate_mcs, (int) length-1, buffer);
		} else {
			snprintf(log, sizeof(log), "{\"ts\": %zd.%zd, \"round\": %d, \"legacy\": %f, \"mcs\": %d, \"gps\": %.*s},\n",
					 global->gps.ts_sys.tv_sec, global->gps.ts_sys.tv_nsec,global->output_round,  data_rate_legacy_rates[-(global->rate_chosen + 1)], global->bitrate_mcs, (int) length-1, buffer);
		}
	} else {
		snprintf(log, sizeof(log), "{\"ts\": %zd.%zd, \"round\": %d, \"legacy\": %d, \"mcs\": %d, \"gps\": %.*s},\n",
				 global->gps.ts_sys.tv_sec, global->gps.ts_sys.tv_nsec,global->output_round,  global->bitrate_legacy, global->bitrate_mcs, (int) length-1, buffer);
	}


	fwrite(log, strlen(log), 1, global->gps_fp);
	fflush(global->gps_fp);

	//strcpy(global->gps_info.gps_msg, buffer);
	//global->gps_info.length = (uint32_t) length;

	int cnt = 0;
	while(ptr != NULL) {
		//printf("%d\t%s\n", cnt, ptr);
		ptr = strtok(NULL, delimiter);
		cnt++;

		switch (cnt) {
			case UTC:
				global->gps.utc = atof(ptr);
				break;
			case LAT:
				global->gps.lat = atof(ptr);
				break;
			case DLAT:
				global->gps.dlat = *ptr;
				break;
			case LON:
				global->gps.lon = atof(ptr);
				break;
			case DLON:
				global->gps.dlon = *ptr;
				break;
			case QUAL:
				global->gps.quality = (u8) atoi(ptr);
				break;
			case NUM_SAT:
				global->gps.num_sat = (u8) atoi(ptr);
				break;
			case ALT:
				global->gps.alt = atof(ptr);
				break;
			default:
				continue;
		}
	}
}



char *print_addr(u8 *addr) {
	static char mac_str[16][20];
	static int index = 0;

	index = (index + 1) % 16;

	snprintf(mac_str[index], sizeof(mac_str[index]) - 1, "%02x:%02x:%02x:%02x:%02x:%02x",
		 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
	mac_str[index][sizeof(mac_str[0]) - 1] = 0;

	return mac_str[index];
}

int monitor_tx_meshmerize_packet(struct global *global, struct sk_buff *skb, struct packet_info *pinfo) {
	/**
	 * monitor_tx_meshmerize_packet - transport a packet with meshmerize header on radiotap
	 * @global: global structures
	 * @skb: packet information and data
	 * @pinfo: auxiliary information on the packet
	 *
	 * Returns 0 on success, < 0 otherwise.
	 * */
	int ret;

	if (build_ieee80211(skb, pinfo) < 0)
		return -1;

	if (build_radiotap(skb, pinfo) < 0)
		return -1;

	ret = write(global->mon_fd, skb->data, skb->len);
	if (ret < 0)
		fprintf(stderr, "transmit failed\n");

	return 0;
}

#define MAX_SEQNO		0xffff

int rate_rx_packet(struct global *global, struct sk_buff *skb, struct packet_info *pinfo) {
	struct station_info *sta_info = NULL, *sta_temp;
	int32_t seqno;
	u32 round;

	list_for_each_entry(sta_temp, &global->sta_info_list, list) {
		if (memcmp(sta_temp->mac, pinfo->mac_src, ETH_ALEN) != 0)
			continue;

		sta_info = sta_temp;
		break;
	}

	if (!sta_info) {
		char path[1024];
		char *init_string = "[\n";

		sta_info = malloc(sizeof(*sta_info));
		memset(sta_info, 0, sizeof(*sta_info));

		memcpy(sta_info->mac, pinfo->mac_src, ETH_ALEN);
		INIT_LIST_HEAD(&sta_info->list);

		snprintf(path, sizeof(path), "%s/%s/rate_analysis_%s_%ld.dump",
			global->out_path,
			print_addr(global->wlan_addr),
			print_addr(sta_info->mac),
			time(NULL));
		path[sizeof(path) - 1] = 0;

		sta_info->fp = fopen(path, "w");
		if (!sta_info->fp) {
			free(sta_info);
			return -1;
		}

		fwrite(init_string, strlen(init_string), 1, sta_info->fp);

		list_add(&sta_info->list, &global->sta_info_list);
	}

	if (skb->len < sizeof(uint32_t))
		return -1;

	seqno = ntohl(*((uint32_t *) skb->data));
	round = ntohl(*(((uint32_t *) skb->data) + 1));

	if (seqno > MAX_SEQNO)
		seqno = MAX_SEQNO;

	/* restart if mcs/bitrate has changed or seqno is lower */
	if (sta_info->mcs != pinfo->mcs ||
		sta_info->bitrate != pinfo->bitrate ||
		seqno < sta_info->seqno ||
		sta_info->round != round) {
		char open_string[1024];
		char gps_string[512];

		if (sta_info->json_started) {
			char *close_string = "\" },\n";

			while (++sta_info->seqno < MAX_SENDS)
				fwrite("0", 1, 1, sta_info->fp);

			fwrite(close_string, strlen(close_string), 1, sta_info->fp);
		}

		sta_info->seqno = -1;
		sta_info->round = round;
		sta_info->mcs = pinfo->mcs;
		sta_info->bitrate = pinfo->bitrate;
		//printf("opening for bitrate %d, mcs %d\n", sta_info->bitrate, sta_info->mcs);


		if (global->gps_available) {
			snprintf(gps_string, sizeof(gps_string),
							 "\"gps\": {\"loc\": {\"utc\": %6.3f, \"lat\": %4.4f, \"dlat\": \"%c\", \"lon\": %5.4f, \"dlon\": \"%c\", \"qual\": %d, \"num\": %d, \"alt\": %3.1f}}, ",
				 global->gps.utc,
				 global->gps.lat,
				 global->gps.dlat,
				 global->gps.lon,
				 global->gps.dlon,
				 global->gps.quality,
				 global->gps.num_sat,
				 global->gps.alt
							 );
		} else {
			gps_string[0] = 0;
		}

		if (sta_info->bitrate)
			snprintf(open_string, sizeof(open_string),
				 "{ \"round\": %d, \"systime\": %ld, \"legacy\": % 3.1f, %s\"bitmask\": \"", round, time(NULL),
				 ((float) sta_info->bitrate) / 2, gps_string);
		else
			snprintf(open_string, sizeof(open_string), "{ \"round\": %d, \"systime\": %ld, \"mcs\": %d, %s\"bitmask\": \"",
				 round, time(NULL),
				 sta_info->mcs, gps_string);

		open_string[sizeof(open_string) - 1] = 0;
		fwrite(open_string, strlen(open_string), 1, sta_info->fp);
		sta_info->json_started = 1;
	}

	while (sta_info->seqno + 1 < seqno) {
		fwrite("0", 1, 1, sta_info->fp);
		sta_info->seqno++;
	}

	fwrite("1", 1, 1, sta_info->fp);
	sta_info->seqno = seqno;

	fflush(sta_info->fp);

	return 0;
}

int monitor_rx_packet(struct global *global, struct sk_buff *skb) {
	struct packet_info pinfo;

	memset(&pinfo, 0, sizeof(pinfo));

	if (parse_radiotap(skb, &pinfo) < 0)
		return -1;

	if (parse_ieee80211(skb, &pinfo) < 0)
		return -1;

	rate_rx_packet(global, skb, &pinfo);

	return 0;
}

void ev_handler_mon_read(int fd, short ev __unused, void *arg) {
	struct global *global = arg;
	struct sk_buff skb;
	static u8 buf[2048];
	ssize_t len;

	memset(buf, 0, sizeof(buf));
	skb.head = &buf[0];
	skb.data = skb.head;
	len = read(fd, skb.data, sizeof(buf));
	if (len < 0) {
		log_printf(global, LOG_INTERFACE, "failed to read from monitor\n");
		exit(-1);
	}
	skb.len = (size_t) len;

	monitor_rx_packet(global, &skb);
}

void increment_rate(struct global *global) {
	if (global->rate_sent == global->num_rates - 1) {
		global->output_round++;
		if (global->max_rounds) {
			if (global->output_round >= global->max_rounds) {
				safe_exit(global);
			}
		}
		global->rate_sent = -1;
	}

	if (global->random_send_interval){
		if (global->rate_sent < 0){
			shuffle(global->rate_order, global->num_rates);
		}
	}

	global->rate_sent++;
	global->rate_chosen = global->rate_order[global->rate_sent];

	if (global->rate_chosen < 0) {
		if (global->dot11p_mode) {
			global->bitrate_legacy = pinfo_dot11p_rates[-(global->rate_chosen + 1)];
		} else {
			global->bitrate_legacy = pinfo_legacy_rates[-(global->rate_chosen + 1)];
		}
		global->bitrate_mcs = -1;
	} else {
		global->bitrate_legacy = -1;
		global->bitrate_mcs = pinfo_mcs_rates[global->rate_chosen];
	}
}


void shuffle(s8 *array, size_t n) {
	/**
	 * Arrange the N elements of ARRAY in random order.
	 * Only effective if N is much smaller than RAND_MAX;
	 * if this may not be the case, use a better random
	 * number generator.
	 * */
	if (n > 1)
	{
		size_t i;
		for (i = 0; i < n - 1; i++)
		{
		  size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
		  s8 t = array[j];
		  array[j] = array[i];
		  array[i] = t;
		}
	}
}


double random_expo(double beta){
	// TODO: Verify that this function operates as expected.
	double u;
	u = rand() / (RAND_MAX + 1.0);
	return -log(1- u) * beta;
}

static void ms2tv(struct timeval *tv, unsigned long ms)
{
	tv->tv_sec = ms / 1000;
	tv->tv_usec = (ms % 1000) * 1000;
}

double max_element(double *array, size_t len) {
	double max=0;
	for (u32 i = 0; i < len; i++) {
		if (array[i] > max)
			max = array[i];
	}
	return max;
}

void compute_send_interval(struct global *global){
	if (global->random_send_interval){
		double actual_airtime[sizeof(global->num_rates)];   // in ms
		double beta;
		double delay;
		double rate_mbps;
		double max_airtime = 1.5 * (global->max_neighbours / 3.0 + 1.0 / data_rate_dot11p_rates[-(global->rate_chosen + 1)]) / 1000.0 * 8.0 * (PAYLOAD_LENGTH + 50) * MAX_SENDS;

		printf("Max airtime is: %.3f\n", max_airtime);
		delay = max_airtime;	// in ms
		ms2tv(&global->output_timer_interval, (unsigned long)delay);
	}
	printf("Next Tx in %ld.%06lds \n", global->output_timer_interval.tv_sec, global->output_timer_interval.tv_usec);

}

void ev_handler_output_timer(int fd __unused, short ev __unused, void *arg) {
	struct global *global = arg;
	struct packet_info pinfo;
	u8 buf[2048];
	int i;
	struct sk_buff skb;


	//usleep(rand() % (SEND_INTERVAL * 1000000));
	increment_rate(global);

	printf("sending %d packets on ", MAX_SENDS);
	if (global->rate_chosen < 0) {
		if (global->dot11p_mode) {
			printf("bitrate %f / MCS %d\n", data_rate_dot11p_rates[-(global->rate_chosen + 1)], global->bitrate_mcs);
		} else {
			printf("bitrate %f / MCS %d\n", data_rate_legacy_rates[-(global->rate_chosen + 1)], global->bitrate_mcs);
		}
	} else {
		printf("bitrate %d / MCS %d (%f Mbps) \n", global->bitrate_legacy, global->bitrate_mcs, data_rate_mcs_rates[global->rate_chosen]);
	}


	for (i = 0; i < MAX_SENDS; i++) {
		memset(&pinfo, 0, sizeof(pinfo));
		memset(buf, 0, sizeof(buf));
		skb.head = &buf[0];
		skb.data = skb.head + 128;

		skb.len = PAYLOAD_LENGTH;
		*((u32 *) skb.data) = htonl(global->output_seqno);
		*((u32 *) (skb.data + 4)) = htonl(global->output_round);
		global->output_seqno++;

		memcpy(&pinfo.mac_dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN);
		memcpy(&pinfo.mac_src, global->wlan_addr, ETH_ALEN);
		memcpy(&pinfo.ether_dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN);
		memcpy(&pinfo.ether_src, global->wlan_addr, ETH_ALEN);

		if (global->rate_chosen < 0)
			pinfo.bitrate = (u8) global->bitrate_legacy;
		else
			pinfo.mcs = (u8) global->bitrate_mcs;

		monitor_tx_meshmerize_packet(global, &skb, &pinfo);

		if (global->duplicates_on) {
		        sendto(global->sock_fd, &skb, skb.len,MSG_CONFIRM,
		                (const struct sockaddr *) &global->serv_addr, sizeof(global->serv_addr));
		}
	}
	global->output_seqno = 0;

	compute_send_interval(global);
	event_add(global->output_timer, &global->output_timer_interval);
}

void safe_exit(struct global *global){
	struct station_info *sta_info;

	list_for_each_entry(sta_info, &global->sta_info_list, list) {
		if (sta_info->json_started) {
			char *close_string = "\" }\n]\n";

			while (++sta_info->seqno < MAX_SENDS)
				fwrite("0", 1, 1, sta_info->fp);

			fwrite(close_string, strlen(close_string), 1, sta_info->fp);
		} else {
			char *close_string = "]\n";
			fwrite(close_string, strlen(close_string), 1, sta_info->fp);
		}
		fclose(sta_info->fp);

		/* TODO: free'ing the stuff would be cleaner, but we exit afterwards anyway */

		break;
	}

        fclose(global->gps_fp);

	if (global->duplicates_on) {
	        close(global->sock_fd);
	}

	event_base_loopbreak(global->ev_base);
	exit(0);
}

void ev_handler_sigterm(int fd __unused, short ev __unused, void *arg) {
	struct global *global = arg;

	printf("sigterm\n");

	safe_exit(global);
}

static void usage(const char *argv0) {
	fprintf(stderr, "Usage: %s [-p phy][-i ibss] [-r send/recv/duplex] [-m -l -v] [-d -g max_neighbours] [-s path] [-q ip addr]\n", argv0);
	fprintf(stderr, "\tOptions:"
		"\n\t\t -p \tphy interface to use;					Default: Phy0"
		"\n\t\t -i \tibss interface to use;					Default: wlan0"
		"\n\t\t -r \tRole of the node;						Default: recv"
		"\n\t\t -t \tinterval between sending each batch;   			Default: 1s"
		"\n\t\t -n \tnumber of rounds to send;					Default: unlimited"
		"\n\t\t -d \tSend with random intervals;"
		"\n\t\t -g \tMaximum number of neighbour nodes;	 			Default: 8"
		"\n\t\t -s \tSave directory;						Default: /tmp"
                "\n\t\t -q \tSend duplicates to specified IP"
		"\n\t\t -m \tSend only in mcs rates"
		"\n\t\t -l \tSend only in legacy rates"
		"\n\t\t -v \tRun on IEEE802.11p radio. Automatically disables mcs and legacy rates."
		"\n"
	);
	exit(EXIT_FAILURE);
}

int main(int argc, char *argv[]) {
	struct global global;
	struct event *ev_mon, *ev_gps;
	struct event *ev_sigterm, *ev_sighup, *ev_sigint;
	char cmd[1024];
	char *phy = "phy0";
	char *wlan = "wlan0";
	char *role = "recv";
	struct stat st = {0};
	int opt;

	clock_gettime(CLOCK_REALTIME, &ts);

	//initialisation of global
	memset(&global, 0, sizeof(global));
	// global.max_rounds = DEFAULT_MAX_ROUNDS;
	global.output_timer_interval = (struct timeval){DEFAULT_SEND_INTERVAL, 0};
	global.max_neighbours = DEFAULT_EXPECTED_MAX_NEIGHBOURS;
	//set default save directory to /tmp
	snprintf(global.out_path, sizeof(global.out_path), "/tmp");
	global.out_path[sizeof(global.out_path) - 1] = 0;


	while ((opt = getopt(argc, argv, "p:i:r:t:n:m::l::v::d::g:s:q:")) != -1) {
		switch (opt) {
			case 'p':
				phy = optarg;
				break;
			case 'i':
				wlan = optarg;
				break;
			case 'r':
				role = optarg;
				break;
			case 't':
				global.output_timer_interval.tv_sec = atoi(optarg);
				global.output_timer_interval.tv_usec = (int)((atof(optarg) - atoi(optarg)) * 1000000);
				break;
			case 'n':
			        global.max_rounds = (u32)atoi(optarg);
				break;
			case 'm':
				global.disable_legacy = true;
				break;
			case 'l':
				global.disable_mcs = true;
				break;
			case 'v':
				global.dot11p_mode = true;
				global.disable_mcs = true;
				global.disable_legacy = true;
				break;
			case 'd':
				global.random_send_interval = true;
				break;
			case 'g':
				/*if (!global.random_send_interval || !global.disable_legacy){
					fprintf(stderr, "Available only if -d and -m flag is set\n");
					return -1;
				}*/
				global.max_neighbours = (u32)atoi(optarg);
				break;
			case 's':
				strcpy(global.out_path, optarg);
				global.out_path[sizeof(global.out_path) - 1] = 0;
				break;
                        case 'q' :
                                global.duplicates_on = true;
                                strcpy(global.duplicates_ipaddr, optarg);
                                printf("send duplicates to %s\n", global.duplicates_ipaddr);

                                // Creating socket file descriptor
                                if ( (global.sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
                                        perror("socket creation failed");
                                        exit(EXIT_FAILURE);
                                }

                                memset(&global.serv_addr, 0, sizeof(global.serv_addr));

                                // Filling server information
                                global.serv_addr.sin_family = AF_INET;
                                global.serv_addr.sin_port = htons(8888);

                                // Convert IPv4 and IPv6 addresses from text to binary form
                                if(inet_pton(AF_INET, global.duplicates_ipaddr, &global.serv_addr.sin_addr)<=0) {
                                        printf("\nInvalid address/ Address not supported \n");
                                }
                                break;
                        default:
                                usage(argv[0]);
		}
	}
	global.bitrate_legacy = -1;
	global.bitrate_mcs = -1;
	global.rate_sent = -1;

	// checking flags
	if (global.disable_legacy && global.disable_mcs && !global.dot11p_mode) {
		fprintf(stderr, "Only one option of [mcs rates -m] and [legacy rates -l] makes sense (if not sending in 11p mode)\n");
		return -1;
	}

	// set rates to be tested
	// legacy rates : -1, -2, ... -x
	// mcs rates	:  0,  1, ...  y
	if (global.dot11p_mode) {
		global.num_rates = (int) sizeof(data_rate_dot11p_rates) / (int) sizeof(double);
		for (int i = 0; i < global.num_rates; i++) {
			global.rate_order[i] = -(i + 1);
		}
	} else if (global.disable_legacy) {
		global.num_rates = (int) sizeof(data_rate_mcs_rates) / (int) sizeof(double);
		for (int i = 0; i < global.num_rates; i++) {
			global.rate_order[i] = i;
		}
	} else if (global.disable_mcs) {
		global.num_rates = (int) sizeof(data_rate_legacy_rates) / (int) sizeof(double);
		for(int i = 0; i < global.num_rates ; i++) {
			global.rate_order[i] = -(i+1);
		}
	} else {
		global.num_rates = ((int) sizeof(data_rate_legacy_rates) + (int) sizeof(data_rate_mcs_rates)) / (int) sizeof(double);
		for(int i = 0; i < (int) sizeof(data_rate_legacy_rates) / (int) sizeof(double) ; i++) {
			global.rate_order[i] = -(i+1);
		}
		for(int i = 0; i < (int) sizeof(data_rate_mcs_rates) / (int) sizeof(double) ; i++) {
			global.rate_order[i + (int) sizeof(data_rate_legacy_rates) / (int) sizeof(double)] = i;
		}
	}

	if (get_device_mac(wlan, global.wlan_addr) < 0) {
		fprintf(stderr, "Couldn't acquire wlan address\n");
		return -1;
	}

	// set random seed
	unsigned int ran_seed = (unsigned int) ts.tv_nsec;
	srand(ran_seed);

	INIT_LIST_HEAD(&global.sta_info_list);

	snprintf(cmd, sizeof(cmd), "iw phy %s interface add %s_mon type monitor", phy, phy);
	cmd[sizeof(cmd) - 1] = 0;
	system(cmd);
	snprintf(cmd, sizeof(cmd), "ifconfig %s_mon up", phy);
	cmd[sizeof(cmd) - 1] = 0;
	system(cmd);
	snprintf(cmd, sizeof(cmd), "%s_mon", phy);
	cmd[sizeof(cmd) - 1] = 0;

	global.mon_fd = rawsock_create(cmd);

	if (global.mon_fd < 0) {
		fprintf(stderr, "Can't create monitor interface - are you root?\n");
		return -1;
	}

	global.ev_base = event_base_new();
	if (!global.ev_base) {
		fprintf(stderr, "Couldn't set up event base\n");
		return -1;
	}

	char ratechecker_path[1024];
	//if global.out_path doesn't exist, first create this directory
	if (stat(global.out_path, &st) == -1) {
		mkdir(global.out_path, 0755);
	}
	sprintf(ratechecker_path, "%s/%s", global.out_path, print_addr(global.wlan_addr));
	ratechecker_path[sizeof(ratechecker_path) - 1] = 0;
	printf("out path: %s\n", ratechecker_path);
	if (stat(ratechecker_path, &st) == -1) {
	mkdir(ratechecker_path, 0755);
	}

	ev_sigterm = event_new(global.ev_base, SIGTERM, EV_SIGNAL | EV_PERSIST, ev_handler_sigterm, &global);
	event_add(ev_sigterm, NULL);
	ev_sighup = event_new(global.ev_base, SIGHUP, EV_SIGNAL | EV_PERSIST, ev_handler_sigterm, &global);
	event_add(ev_sighup, NULL);
	ev_sigint = event_new(global.ev_base, SIGINT, EV_SIGNAL | EV_PERSIST, ev_handler_sigterm, &global);
	event_add(ev_sigint, NULL);

	if (!strcmp(role, "recv") || !strcmp(role, "duplex")) {
		ev_mon = event_new(global.ev_base, global.mon_fd, EV_READ | EV_PERSIST, ev_handler_mon_read, &global);
		event_add(ev_mon, NULL);
	}

	compute_send_interval(&global);
	if (!strcmp(role, "send") || !strcmp(role, "duplex")) {
		global.output_timer = event_new(global.ev_base, -1, 0, ev_handler_output_timer, &global);
		event_add(global.output_timer, &global.output_timer_interval);
	}

	open_serial_port(&global);
	if (global.gps_available) {
		ev_gps = event_new(global.ev_base, global.gps_fd, EV_READ | EV_PERSIST, ev_handler_gps_read, &global);
		event_add(ev_gps, NULL);
	}

	event_base_loop(global.ev_base, 0);


	return 0;
}
