/****************************************************************************/
/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

typedef unsigned char Byte;
typedef short MBool;
typedef short Int16;
typedef unsigned short Nat16;
typedef long Int32;
typedef unsigned long Nat32;
typedef int Errno;

#ifndef FALSE
#define FALSE	0
#endif

#ifndef TRUE
#define	TRUE	~FALSE
#endif

#define OK	 0
#define ERROR	-1
#define MLocal	static

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

/* Original FileHdr definition, but incorrect on x86 due to packing	*/
typedef struct				/********************************/
{					/* Header for all data files	*/
    Nat16	format;			/* Format - type in MSB, version in LSB*/
    Nat16	rcdSize;		/* Size of one data record	*/
    Nat16	rate;			/* Number of records/minute	*/
    time_t	startTime;		/* Time of first record, time_t	*/
    Nat16	startMs;		/* Millisecond offset of startTime*/
} FileHdr;				/********************************/

#define FMT_OFFSET	0
#define RCDS_OFFSET	2
#define RATE_OFFSET	4
#define TIME_OFFSET	6
#define MS_OFFSET	10

/* File IDs		*/
#define DATA_FILE	0
#define IDX_FILE	1
#define CLK_FILE	2
#define AUX_FILE	3


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

MLocal char	*baseFile = "BEDS00";
MLocal char	fname[64];

MLocal double	scale[] =
    { .05, .05, .05, .00333, .00333, .00333, .0005, .0005, .0005, .000806
};


/************************************************************************/
/* Function    : getMotWord						*/
/* Purpose     : Get 16 bit word in Motorola (big endian) format	*/
/* Inputs      : Ptr to word						*/
/* Outputs     : 16 bit value						*/
/************************************************************************/
Nat16 getMotWord(void *inp)
{
    Byte *p = (Byte *)inp;
    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						*/
/************************************************************************/
Nat32 getMotLong(void *inp)
{
    return(((Nat32)getMotWord(inp) << 16) | getMotWord(inp+2));
}

/************************************************************************/
/* Function    : openFile						*/
/* Purpose     : Open an input file					*/
/* Inputs      : File name						*/
/* Outputs     : FILE ptr or NULL					*/
/************************************************************************/
FILE	*openFile(char *ftype)
{
    FILE	*fp;

    sprintf(fname, "%s.%s", baseFile, ftype);
    fp = fopen(fname, "r");
    if (fp == NULL)
	printf("Could not open %s for reading\n", fname);
    return(fp);
}

/************************************************************************/
/* Function    : createFile						*/
/* Purpose     : Create an output file					*/
/* Inputs      : File name						*/
/* Outputs     : FILE ptr or NULL					*/
/************************************************************************/
FILE	*createFile(char *ftype)
{
    FILE	*fp;

    sprintf(fname, "%s.%s.OUT", baseFile, ftype);
    fp = fopen(fname, "w");
    if (fp == NULL)
	printf("Could not create %s\n", fname);
    return(fp);
}

/************************************************************************/
/* Function    : readHeader						*/
/* Purpose     : Read data header from FILE				*/
/* Inputs      : Ptr to FILE, ptr to FileHdr				*/
/* Outputs     : None							*/
/************************************************************************/
Errno readHeader(FILE *fp, FileHdr *hdrp)
{
    Byte	buf[12];

    if (fread(buf, 12, 1, fp) < 1)
    {
	printf("Could not read file header\n");
	return(ERROR);
    }

    hdrp->format = getMotWord(buf + FMT_OFFSET);
    hdrp->rcdSize = getMotWord(buf + RCDS_OFFSET);
    hdrp->rate = getMotWord(buf + RATE_OFFSET);
    hdrp->startTime = (time_t)getMotLong(buf + TIME_OFFSET);
    hdrp->startMs = getMotWord(buf + MS_OFFSET);

    return(OK);
}

/************************************************************************/
/* Function    : showHeader						*/
/* Purpose     : Print contents of data header				*/
/* Inputs      : Ptr to FileHdr						*/
/* Outputs     : None							*/
/************************************************************************/
Errno showHeader(FileHdr *hdrp)
{
    struct tm	stm;

    gmtime_r(&hdrp->startTime, &stm);

    printf("Fmt = %#hx Rcdsize = %hu Rate = %hu Starttime = %u/%u/%4u %02u:%02u:%02u.%03u\n",
	   hdrp->format, hdrp->rcdSize, hdrp->rate, 
	   stm.tm_mon+1, stm.tm_mday, stm.tm_year+1900, stm.tm_hour, stm.tm_min,
	   stm.tm_sec, hdrp->startMs);

    return(OK);
}

/************************************************************************/
/* Function    : fprintHeader						*/
/* Purpose     : Print contents of data header to file			*/
/* Inputs      : Ptr to FILE						*/
/* Outputs     : None							*/
/************************************************************************/
Errno fprintHeader(FILE *fp, FileHdr *hdrp)
{
    struct tm	stm;

    gmtime_r(&hdrp->startTime, &stm);

    fprintf(fp, "%#hx, %hu, %hu, %ld, %u/%u/%4u %02u:%02u:%02u.%03u\n",
	    hdrp->format, hdrp->rcdSize, hdrp->rate, hdrp->startTime,
	    stm.tm_mon+1, stm.tm_mday, stm.tm_year+1900, stm.tm_hour, stm.tm_min,
	    stm.tm_sec, hdrp->startMs);

    return(OK);
}

/************************************************************************/
/* Function    : main							*/
/* Purpose     : Main entry point					*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/* Comments    : Never returns						*/
/************************************************************************/
int main(int argc, char **argv)
{
    FILE	*ifp, *ofp;
    FileHdr	hdr;
    Int32	ldat[2];
    Int16	navDat[10], i, rcvdat;
    time_t	smplTime;
    Nat16	smplOffset = 0;

    if (argc > 1)
	baseFile = argv[1];

    printf("Converting DAT file:\n");
    if ((ifp = openFile("DAT")) == NULL)
	exit(1);

    if ((ofp = createFile("DAT")) == NULL)
	exit(1);

    if (readHeader(ifp, &hdr) == OK)
    {
	showHeader(&hdr);
	fprintHeader(ofp, &hdr);
	smplTime = hdr.startTime;
    }

    while (fread(navDat, sizeof(Int16), 10, ifp) > 0)
    {
	fprintf(ofp, "%lu.%1hu ", smplTime, smplOffset++);
	if (smplOffset >= 10)
	{
	    smplTime++;
	    smplOffset = 0;
	}

	for (i = 0; i < 9; i++)
	{
	    rcvdat = getMotWord(navDat+i) & 0x3fff;
	    if (rcvdat & 0x2000)
		rcvdat |= 0xe000;
	    fprintf(ofp, "%8.4f, ", rcvdat * scale[i]);
	}

	fprintf(ofp, "%8.4f\n", scale[9] * (navDat[9] & 0xfff));
    }

    fclose(ifp);
    fclose(ofp);

    printf("Converting IDX file:\n");
    if ((ifp = openFile("IDX")) != NULL)
    {
	if (readHeader(ifp, &hdr) == OK)
	    showHeader(&hdr);

	if ((ofp = createFile("IDX")) != NULL)
	{
	    fprintHeader(ofp, &hdr);
	    while (fread(ldat, sizeof(Int32), 2, ifp) > 0)
	    {
		if (fprintf(ofp, "%ld,%ld\n", getMotLong(ldat), getMotLong(ldat+1)) < 0)
		    break;
	    }
	}

	fclose(ifp);
	fclose(ofp);
    }

    printf("Converting CLK file:\n");
    if ((ifp = openFile("CLK")) != NULL)
    {
	if (readHeader(ifp, &hdr) == OK)
	    showHeader(&hdr);

	if ((ofp = createFile("CLK")) != NULL)
	{
	    fprintHeader(ofp, &hdr);
	    while (fread(ldat, sizeof(Int32), 1, ifp) > 0)
	    {
		if (fprintf(ofp, "%ld\n", getMotLong(ldat)) < 0)
		    break;
	    }
	}

	fclose(ifp);
	fclose(ofp);
    }

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