/****************************************************************************/
/* Summary  : Application to decode BEDS binary files into CSV files for Excel*/
/* Filename : decode.c                                                      */
/* Author   : Robert Herlien (rah)					    */
/* Project  : BEDS (Benthic Event Detection System			    */
/* Revision : 1.0							    */
/* Created  : 11/10/2011						    */
/****************************************************************************/
/* Modification History:						    */
/* 10nov2011 rah - created						    */
/****************************************************************************/

#include "mbariTypes.h"
#include "mbariConst.h"
#include "file.h"

#ifdef CFX				//If Persistor version
#include	<cfxpico.h>		// Persistor PicoDOS Definitions
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#define NumberOf(arr)		((sizeof(arr) / sizeof(arr[0])))

#define VREF			2.50
#define RAW_TO_VOLTS(n)		((float)(n) * VREF / 65536.0)
#define RAW_TO_BATTVOLTS(n)	((float)(n) * 12.11* VREF / 65536.0)
#define VOLTS_TO_BATTVOLTS(f)	(12.11*f)
#define VOLTS_TO_TEMP(f)	(100.0*(f-0.5))
#define VOLTS_TO_RH(f)		(50.0*(f-0.5))


/********************************/
/*	Module Local Data	*/
/********************************/

MLocal MBool	printDataRecs = FALSE;
MLocal MBool	totalsOnly = FALSE;
Global MBool	pressOnly = FALSE;
Global MBool	sysRecOnly = FALSE;
MLocal Nat32	numDataRecs = 0, totDataRecs = 0, duration;

Void printFileHdr(FILE *fp), printSecMarker(FILE *fp), printData(FILE *fp);
Void printPressure(FILE *fp), printIndexRcd(FILE *fp), printSysRcd(FILE *fp);

MLocal Void (*rcdFunc[])(FILE *) =
{
    printFileHdr, printSecMarker, printData, printPressure, printIndexRcd, printSysRcd
};


/************************************************************************/
/* Function    : getMotWord						*/
/* Purpose     : Get 16 bit word in Motorola (big endian) format	*/
/* Inputs      : Ptr to word						*/
/* Outputs     : 16 bit value						*/
/************************************************************************/
MLocal Nat16 getMotWord(Byte *p)
{
    return(((*p << 8) | p[1]) & 0xffff);
}

/************************************************************************/
/* Function    : getMotLong						*/
/* Purpose     : Get 32 bit long in Motorola (big endian) format	*/
/* Inputs      : Ptr to word						*/
/* Outputs     : 32 bit value						*/
/************************************************************************/
MLocal Nat32 getMotLong(Byte *p)
{
    return(((Nat32)getMotWord(p) << 16) | getMotWord(p+2));
}

/************************************************************************/
/* Function    : readWord						*/
/* Purpose     : Get 16 bit word from input stream in big endian format	*/
/* Inputs      : FILE Ptr						*/
/* Outputs     : 16 bit value						*/
/************************************************************************/
MLocal Nat16 readWord(FILE *fp)
{
    Byte	b[2];

    fread(b, 2, 1, fp);
    return(getMotWord(b));
}

/************************************************************************/
/* Function    : readLong						*/
/* Purpose     : Get 32 bit long from input stream in big endian format	*/
/* Inputs      : FILE Ptr						*/
/* Outputs     : 32 bit value						*/
/************************************************************************/
MLocal Nat32 readLong(FILE *fp)
{
    Byte	b[4];

    fread(b, 4, 1, fp);
    return(getMotLong(b));
}

/************************************************************************/
/* Function    : printFileHdr						*/
/* Purpose     : Print contents of file header				*/
/* Inputs      : Ptr to FILE						*/
/* Outputs     : None							*/
/************************************************************************/
Void printFileHdr(FILE *fp)
{
    char	b;
    time_t	startTime;
    struct tm	*tmp;
    Nat16	startMs;

    fread(&b, 1, 1, fp);
    printf("FileHdr ver=%hu ", readWord(fp));
    printf("ID=%u ", readWord(fp));
    printf("rate=%u ", readWord(fp));

    startTime = readLong(fp);
    tmp = gmtime(&startTime);
    startMs = readWord(fp);
    duration = readLong(fp);

    printf("%04d/%02d/%02d %02d:%02d:%02d.%03hd dur=%ld.%03lu\n",
	   tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
	   tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
	   startMs, duration/1000, duration%1000);
}


/************************************************************************/
/* Function    : checkDataRecs						*/
/* Purpose     : See if we need to print number of data recs found	*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/************************************************************************/
MLocal Void checkDataRecs(Void)
{
    if (!printDataRecs && !totalsOnly && !pressOnly && !sysRecOnly && numDataRecs)
    {
	printf("%lu Data Records\n", numDataRecs);
	numDataRecs = 0;
    }
}

/************************************************************************/
/* Function    : printSecMarker						*/
/* Purpose     : Print second marker record				*/
/* Inputs      : Ptr to FILE						*/
/* Outputs     : None							*/
/************************************************************************/
Void printSecMarker(FILE *fp)
{
    time_t	secTime;
    struct tm	*tmp;
    char	b;

    checkDataRecs();
    fread(&b, 1, 1, fp);
    secTime = readLong(fp);
    tmp = gmtime(&secTime);

    if (!totalsOnly && !pressOnly && !sysRecOnly)
	printf("Second Marker %d %04d/%02d/%02d %02d:%02d:%02d\n", secTime,
	       tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
	       tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
}


/************************************************************************/
/* Function    : printData						*/
/* Purpose     : Print data record					*/
/* Inputs      : Ptr to FILE						*/
/* Outputs     : None							*/
/************************************************************************/
Void printData(FILE *fp)
{
    Byte	b[22], *p;
    long	lval, i;
    float	accel[3], quat[4];

    fread(b, 4*QUAT_SIZE + 9, 1, fp);
    totDataRecs++;

    if (!printDataRecs || totalsOnly)
	numDataRecs++;
    else
    {
	for (i = 0, p = b; i < 3; i++)
	{
	    lval = (long)(((unsigned long)p[0] << 16) +
			  ((unsigned long)p[1] << 8) +
			  ((unsigned long)p[2]));
	    if (lval & 0x800000)
		lval |= 0xff000000;
	    accel[i] = (float)lval / 65536.0f;
	    p += 3;
	}

	for (i = 0; i < 4; i++)
	{
#if QUAT_SIZE == 2
	    Int16 ival = (Int16)(((unsigned long)p[0] << 8) +
				 (unsigned long)p[1]);
	    quat[i] = (float)ival / 16384.0f;
	    p += 2;
#else
	    lval = (long)(((unsigned long)p[0] << 24) +
			  ((unsigned long)p[1] << 16) +
			  ((unsigned long)p[2] << 8));
	    quat[i] = (float)lval / 1073741824.0f;
	    p += 3;
#endif
	}

	printf("Inertial Data  acc: %8.5f %8.5f %8.5f  quat: %8.5f %8.5f %8.5f %8.5f\n",
	       accel[0], accel[1], accel[2], quat[0], quat[1], quat[2], quat[3]);
    }
}


/************************************************************************/
/* Function    : printPressure						*/
/* Purpose     : Print pressure record					*/
/* Inputs      : Ptr to FILE						*/
/* Outputs     : None							*/
/************************************************************************/
Void printPressure(FILE *fp)
{
    Byte	b;
    Nat16	press;

    checkDataRecs();
    fread(&b, 1, 1, fp);
    press = readWord(fp);

    if (!totalsOnly && !sysRecOnly)
	printf("Ext Pressure %hu counts %5.2f volts\n", press, RAW_TO_VOLTS(press));
}


/************************************************************************/
/* Function    : printIndexRcd						*/
/* Purpose     : Print index record					*/
/* Inputs      : Ptr to FILE						*/
/* Outputs     : None							*/
/************************************************************************/
Void printIndexRcd(FILE *fp)
{
    time_t	startTime;
    struct tm	*tmp;
    Nat16	startMs;
    Nat32	idxDuration;
    Flt32	maxVal;
    Byte	b[16];

    checkDataRecs();
    fread(b, 1, 1, fp);

    startTime = readLong(fp);
    tmp = gmtime(&startTime);
    startMs = readWord(fp);
    idxDuration = readLong(fp);
    maxVal = (float)readLong(fp) / 65536.0f;

    if (!totalsOnly)
	printf("Index %04d/%02d/%02d %02d:%02d:%02d.%03hd dur=%ld.%03lu maxVal=%.4f ",
	       tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
	       tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
	       startMs, idxDuration/1000, idxDuration%1000, maxVal);

    fread(b, 16, 1, fp);
    b[15]='\0';

    if (!totalsOnly)
	printf("file=%s\n", b);
}

/************************************************************************/
/* Function    : printSysRcd						*/
/* Purpose     : Print system data record				*/
/* Inputs      : Ptr to FILE						*/
/* Outputs     : None							*/
/************************************************************************/
Void printSysRcd(FILE *fp)
{
    Byte	b[4];
    time_t	srTime;
    struct tm	*tmp;
    Nat16	batt, vmodem, epress, ipress, temp, hum;

    checkDataRecs();
    fread(b, 1, 1, fp);
    srTime = readLong(fp);
    tmp = gmtime(&srTime);
    batt = readWord(fp);
    vmodem = readWord(fp);
    epress = readWord(fp);
    ipress = readWord(fp);
    temp = readWord(fp);
    hum = readWord(fp);

    if (!totalsOnly && !pressOnly)
    {
	printf("Sys Rcd %04d/%02d/%02d %02d:%02d:%02d Batt %5.2fV "
	       "Vmodem %5.2fV ExtPress %5.2fV IntPress %5.2fV Temp %4.1f degC Hum %4.1f%%\n",
	       tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
	       tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
	       RAW_TO_BATTVOLTS(batt), RAW_TO_BATTVOLTS(vmodem),
	       RAW_TO_VOLTS(epress), RAW_TO_VOLTS(ipress), 
	       VOLTS_TO_TEMP(RAW_TO_VOLTS(temp)), VOLTS_TO_RH(RAW_TO_VOLTS(hum)));
    }
}


/************************************************************************/
/* Function    : main							*/
/* Purpose     : Main entry point					*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/* Comments    : Never returns						*/
/************************************************************************/
int main(int argc, char **argv)
{
    int		opt, index, recType;
    FILE	*fp;

#ifdef CFX
    int		optind = 1;

    // Identify the progam and build
    printf("\nProgram: %s: %s %s \n", __FILE__, __DATE__, __TIME__);

    for (opt = 1; opt==1; )
    {
	if (strcasecmp(argv[optind], "-D") == 0)
	{
	    printDataRecs = TRUE;
	    optind++;
	    opt = 1;
	}
	else if (strcasecmp(argv[optind], "-T") == 0)
	{
	    totalsOnly = TRUE;
	    optind++;
	    opt = 1;
	}
	else if (strcasecmp(argv[optind], "-H") == 0)
	{
	    printf("\nDECODE [-D] [-T] [-S] [-P] <filename>/tDecode a BEDS binary file\n"
		   "\t [-D] Print full data records (else prints summaries each second)\n"
		   "\t [-T] Print only totals, not individual records (except header)\n"
		   "\t [-S] Print only System Records\n"
		   "\t [-P] Print only Pressure Records\n");
	    exit(0);
	}
	else
	    opt = 0;
    }
#else

    printf("Decode version 2/6/13\n");

    while ((opt = getopt(argc, argv, "dtsph")) >= 0)
	switch(opt)
	{
	  case 'd':
	    printDataRecs = TRUE;
	    break;

	  case 't':
	    totalsOnly = TRUE;
	    break;

	  case 'p':
	    pressOnly = TRUE;
	    break;

	  case 's':
	    sysRecOnly = TRUE;
	    break;

	  case 'h':
	      printf("\ndecode [-d] [-t] [-s] [-p] <filename>/tDecode a BEDS binary file\n"
		     "\t [-d] Print full data records (else prints summaries each second)\n"
		     "\t [-t] Print only totals, not individual records (except header)\n"
		     "\t [-s] Print only System Records\n"
		     "\t [-p] Print only Pressure Records\n");
	      exit(0);
	}
#endif

    if (optind >= argc)
    {
	fprintf(stderr, "No input file specified\n");
	exit(0);
    }

    for(index = optind; index < argc; index++)
    {
	if ((fp = fopen(argv[index], "r")) == NULL)
	{
	    fprintf(stderr, "Could not open file %s\n", argv[index]);
	    continue;
	}

	printf("Decoding file %s:\n", argv[index]);
	totDataRecs = 0;

	while((recType = fgetc(fp)) != EOF)
	{
	    if (recType < NumberOf(rcdFunc))
		(*rcdFunc[recType])(fp);
	    else
		printf("Unknown Record type: %d\n", recType);
#ifdef CFX
	    if (kbhit())
		exit(0);
#endif
	}

	checkDataRecs();
	fclose(fp);
	printf("File %s: %lu data records in %ld.%03lu seconds\n",
	       argv[index], totDataRecs, duration/1000, duration%1000);
    }

    return(0);
			      
} /* main() */
