/* This opens an SBD message from a MetOcean iBCN Iridium beacon and decodes
	the bits to display the data in a human-readable form
	Written 7Sep14 by Paul McGill
*/
#include	<stdlib.h>
#include    <stdio.h>
#include    <math.h>
#include	<time.h>
#include	"ibcn.h"

#define		SBDMsgLen 195			// max number of bytes in SBD msg
#define		timeOffset 315964800	// add to GPS time to get Unix time
#define		leapSecs	16			// number of leap seconds inserted into UTC
// These byte-swapping macros are necessary because the microprocessor used in the iBCN
//  stores 2- and 4-byte values in big-endian order, but Macs and PCs, where this code
//  runs, are little-endian.
#define 	SWAP_2(x) ( (((x) & 0xff) << 8) | ((unsigned short)(x) >> 8) )
#define 	SWAP_4(x) ( ((x) << 24) | (((x) << 8) & 0x00ff0000) | \
         	(((x) >> 8) & 0x0000ff00) | ((x) >> 24) )


// function prototypes
void printPosRpt(T_POSITION_REPORT*);
void printBattStat(T_EVENT_REPORT*);
void printPowerUp(T_EVENT_REPORT*);
void printTimeSet(T_EVENT_REPORT*);
void printConfigUpdate(T_EVENT_REPORT*);
void printGPSTimeout(T_EVENT_REPORT*);
void printIridiumTimeout(T_EVENT_REPORT*);
void printGPSPosReq(T_EVENT_REPORT*);

void printDateTime(DATE_TIME);

int main(argc,argv)
int argc;
char **argv;
{

	unsigned char buffer[SBDMsgLen]; // buffer for SBD data
	unsigned int i;
	unsigned int rc;
	unsigned int msgSize;			// number of bytes in this SBD msg
	unsigned int numRecs;			// number of records in this SBD msg
	u8 eventCode;
	 
	if (argc != 2) {
		printf("Usage: %s  filename.sbd\n", argv[0]);
		return EXIT_FAILURE;
	} // end if
	
	// Open the file
	FILE *fp = fopen(argv[1], "rb");
	if (fp == NULL) {
		printf("Failed to open file");
		return EXIT_FAILURE;
	} // end if
	
	// Read the whole file and then close it
	for (i = 0; (rc = getc(fp)) != EOF && i < SBDMsgLen; buffer[i++] = rc);
	fclose(fp);
	msgSize = i;
	
	// Display the contents
	if ((msgSize - 3) % 16 == 0) {	// if whole number of 16-byte records

		numRecs = (msgSize - 3) / 16;	// determine how many records in SBD
		if(numRecs > 1) {
			printf("\nSBD contains %u records\n", numRecs);
		} // end if

		printf("The bytes read were:\n");	
		printf("%02X %02X %02X\n", buffer[0], buffer[1], buffer[2]);
		for (i = 3; i < msgSize; i++) {
			printf("%02X ", buffer[i]);
			if ((i-2) % 16 == 0) printf("\n");
		} // end for
		printf("\n");

		// step through each report in the message and print its contents
		for (i = 3; i < msgSize; i += 16) {		
			eventCode = buffer[i+4];	// get event code of next event
			if(eventCode >= 128) {	// it's a position report
				printPosRpt((T_POSITION_REPORT*) &buffer[i]);
			} else {				// it's an event report
				switch(eventCode) {
					case POWER_UP : {
						printPowerUp((T_EVENT_REPORT*) &buffer[i]);
						break;
					} // end case POWER_UP
				
					case BATTERY_STATUS : {
						printBattStat((T_EVENT_REPORT*) &buffer[i]);
						break;
					} // end case BATTERY_STATUS
				
					case CONFIG_UPDATE : {
						printConfigUpdate((T_EVENT_REPORT*) &buffer[i]);
						break;
					} // end case CONFIG_UPDATE
				
					case GPS_TIMEOUT : {
						printGPSTimeout((T_EVENT_REPORT*) &buffer[i]);
						break;
					} // end case GPS_TIMEOUT

					case IRIDIUM_TIMEOUT : {
						printIridiumTimeout((T_EVENT_REPORT*) &buffer[i]); // this event is not defined
						break;
					} // end case IRIDIUM_TIMEOUT

					case TIME_SET : {
						printTimeSet((T_EVENT_REPORT*) &buffer[i]);
						break;
					} // end case TIME_SET

					case GPS_POSITION_REQ : {
						printGPSPosReq((T_EVENT_REPORT*) &buffer[i]);
						break;
					} // end case GPS_POSITION_REQ
					
					default : {
						printf("UNKNOWN event\n\n");
						break;
					} // end default
				} // end switch
			} // end if
		} // end for
	} else {
		printf("There was an error reading the file.\n");
		return EXIT_FAILURE;
	} // end if
	return EXIT_SUCCESS;
} /* end main */


void printPosRpt(T_POSITION_REPORT* pRpt) {
	
	printf("POSITION_REPORT\n");
	printDateTime(SWAP_4(pRpt->timestamp)); // print time of event
	
	printf("Reserved: %s\n", (pRpt->event_code & 0x40 ? "1" : "0"));
	printf("Num Sats: %u\n", ((pRpt->event_code & 0x38) >> 3) + 3);
	printf("HDOP: %.1f\n", (pRpt->event_code & 0x07) * 0.5);
	printf("Location: %.5f, %.5f\n", (SWAP_4(pRpt->latitude) * 0.000001) - 90,
		(SWAP_4(pRpt->longitude) * 0.000001) - 180);
	printf("Speed: %u m/s\n", pRpt->speed);
	printf("GPS Accuracy: %u m\n", pRpt->fix_accuracy);
	printf("Time to Fix: %u s\n\n",  pRpt->time_to_fix);
} // end printPosRpt()


void printBattStat(T_EVENT_REPORT* pRpt) {
	T_EVENT_DATA_BATTERY_STATUS* pBattStat;

	printf("BATTERY_STATUS event\n");
	printDateTime(SWAP_4(pRpt->timestamp)); // print time of event
	
	pBattStat = (T_EVENT_DATA_BATTERY_STATUS*) &(pRpt->event_data);	// get pointer to event data
	printf("Battery: %.1f V\n", SWAP_2(pBattStat->voltage) / 10.0);
	printf("Temperature: %d degC\n\n", pBattStat->temperature);
} // end printBattStat()	


void printPowerUp(T_EVENT_REPORT* pRpt) {
	T_EVENT_DATA_POWER_UP* pPwrUp;
	static const char* pReasons[] = {
		"none", "software", "jtag", "watchdog", "brownout", "external", "power on", "other"};

	printf("POWER_UP event\n");
	printDateTime(SWAP_4(pRpt->timestamp)); // print time of event
	
	pPwrUp = (T_EVENT_DATA_POWER_UP*) &(pRpt->event_data);	// get pointer to event data
	printf("Reset Reason: %s\n\n", pReasons[pPwrUp->reset_reason]);
} // end printPowerUp()	


void printTimeSet(T_EVENT_REPORT* pRpt) {
	T_EVENT_DATA_TIME_SET* pTimeSet;

	printf("TIME_SET event\n");
	printDateTime(SWAP_4(pRpt->timestamp)); // print time of event
	
	pTimeSet = (T_EVENT_DATA_TIME_SET*) &(pRpt->event_data);	// get pointer to event data
	printf("Source: %s\n", pTimeSet->source ? "user" : "GPS");
	// Note: a bug in the Metocean firmware is causing the offset to be sent little endian,
	//  so a swap is not necessary until the bug is fixed.
	printf("Offset: %u s\n\n", pTimeSet->offset);
	//printf("Offset: %u\n\n", SWAP_4(pTimeSet->offset));
} // end printTimeSet()


void printConfigUpdate(T_EVENT_REPORT* pRpt) {
	T_EVENT_DATA_CONFIG_UPDATE* pConfigUpdate;
	static const char* pSources[] = {"internal", "Iridium OTA", "reserved", "Bluetooth"};

	printf("CONFIG_UPDATE event\n");
	printDateTime(SWAP_4(pRpt->timestamp)); // print time of event
	
	pConfigUpdate = (T_EVENT_DATA_CONFIG_UPDATE*) &(pRpt->event_data);	// get pointer to event data
	printf("Source: %s\n\n", pSources[pConfigUpdate->source]);
} // end printConfigUpdate()


void printGPSTimeout(T_EVENT_REPORT* pRpt) {
	T_EVENT_DATA_GPS_TIMEOUT* pGPSTimeout;
	
	printf("GPS_TIMEOUT event\n");
	printDateTime(SWAP_4(pRpt->timestamp)); // print time of event
	
	pGPSTimeout = (T_EVENT_DATA_GPS_TIMEOUT*) &(pRpt->event_data);	// get pointer to event data
	printf("Location: %.5f, %.5f\n", (SWAP_4(pGPSTimeout->latitude) * 0.000001) - 90,
	(SWAP_4(pGPSTimeout->longitude) * 0.000001) - 180);
	printf("Speed: %u m/s\n", pGPSTimeout->speed);
	printf("GPS Accuracy: %u m\n", pGPSTimeout->fix_accuracy);
	printf("Num Sats: %u\n\n", pGPSTimeout->num_satellites);
} // end printGPSTimeout()


void printIridiumTimeout(T_EVENT_REPORT* pRpt) {
	T_EVENT_DATA_IRIDIUM_TIMEOUT* pIridiumTimeout;
	static const char* pReasons[] = {"send msg timeout", "send msg max retries", "send msg error",
	"get msg timeout", "get msg max retries", "get msg error", "update timeout"};
	
	printf("IRIDIUM_TIMEOUT event\n");
	printDateTime(SWAP_4(pRpt->timestamp)); // print time of event
	
	pIridiumTimeout = (T_EVENT_DATA_IRIDIUM_TIMEOUT*) &(pRpt->event_data);	// get pointer to event data
	printf("Reason: %s\n\n", pReasons[pIridiumTimeout->reason]);
} // end printIridiumTimeout()


void printGPSPosReq(T_EVENT_REPORT* pRpt) {
	T_EVENT_DATA_GPS_POSITION_REQUEST* pGPSPosReq;

	printf("GPS_POSITION_REQUEST event\n");
	printDateTime(SWAP_4(pRpt->timestamp)); // print time of event

	pGPSPosReq = (T_EVENT_DATA_GPS_POSITION_REQUEST*) &(pRpt->event_data);	// get pointer to event data
	printf("Source: %s\n\n", pGPSPosReq->source ? "reserved" : "command");
} // end printGPSPosReq()


void printDateTime(DATE_TIME gpsTime) {
	time_t	 unixTime;
	struct tm* timeStruct;

	unixTime = gpsTime + timeOffset - leapSecs;
	timeStruct = gmtime(&unixTime);
	printf("%s", asctime(timeStruct));	
	//printf("%s", ctime(&unixTime));	
} // end printDateTime()

