/* convert the content of a binary file, alternating respectively left channel and right channel per line */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <inttypes.h>
#include <sys/types.h>
#include <math.h>

//--------------------------------------------------------------------------------
static int open_file(char * file_path)
{
	int fd = open(file_path, O_RDONLY ); 
	if ( -1 == fd )
	{
		perror("open");
		exit(1);
	}
	
	return fd;
}
//--------------------------------------------------------------------------------

struct chunk_t
{
	uint32_t header;
	size_t count; /* size in unit of uint32_t */
	uint32_t * data;  
};

//--------------------------------------------------------------------------------
static struct chunk_t * new_chunk(void)
{
	struct chunk_t * new = malloc(sizeof(struct chunk_t));
	if(!new)
	{
		perror("malloc");
		exit(1);		
	}
	return new;
}
//--------------------------------------------------------------------------------
void free_chunk(struct chunk_t * chunk)
{
	free(chunk->data);
	free(chunk);
}
//--------------------------------------------------------------------------------

typedef enum {GAIN_1=1,GAIN_10=10} gain_t;

void convert_and_output_data(gain_t gain, uint32_t value )
{
	double voltage = 0;
	switch(gain)
	{
		case 1:
		case 10:
			voltage = (double)( ((double)(value & 0x00FFFFFF) * 20.0)/((double) gain * pow(2,24)) ) - (10 / gain);
			/* left channel */
			printf("%hu ", (value >> 25) & 0x1);
			/* overflow flag */
			printf("%hu ", (value >> 24) & 0x1 );
			/* digital value */
			printf("0x%hu ", value & 0x00FFFFFF );
			/* voltage */
			printf("%lf", voltage );
			break;
		default:
			fprintf(stderr,"unknow gain %d\n",gain);
			exit(1);
	}
	printf("\n");
}

//--------------------------------------------------------------------------------
void output_chunk(gain_t gain, unsigned long count, struct chunk_t * chunk)
{
	/* header info. is output on stderr, to allow filtering */
	fprintf(stderr, "chunk %lu ",count);
	fprintf(stderr, "source: ");
	switch(chunk->header & 0x3 )
	{
		case 0x0:
			fprintf(stderr, "DAC");
			break;
		case 0x1:
			fprintf(stderr, "Chronometer");
			break;
		default:
			fprintf(stderr, "UNKNOWN");
	}
	fprintf(stderr, "/%i", 0x3 & (chunk->header >> 2) );
	fprintf(stderr, " count %u\n", chunk->count );
	
	/* data are output on stdout */
	{
		size_t i = 0;
		for(i=0; i<chunk->count; i++)
		{
			convert_and_output_data(gain, chunk->data[i] );
		}
		
	}
	
}

//--------------------------------------------------------------------------------
struct chunk_t * read_chunk(int data_file)
{
	uint32_t header = 0;
	int ret = read(data_file,&header,sizeof(header) );
	struct chunk_t * chunk= NULL;
	
	if ( 0 == ret ) 
		return NULL; /* nothing more in file */
  
	if ( sizeof(header) != ret )
	{
		perror("read");
		exit(1);
	}

	chunk = new_chunk();
	chunk->header = header;
	
	/* data are provided in "packets" preceded by a 4-bytes header describing the content
	 * bits 31-4 Number of values in 4 bytes units
	 * bit 3-2: Index of the source ( 0 .. 3 )
	 * bit 1-0: Source (0: ADC ,1: Chronometer)
	 */
	 
	/* read rest of chunk */
	{	
		chunk->count = (header>>4);
		size_t count8 = sizeof(uint32_t)*chunk->count;
		uint32_t * data = malloc(count8);
		if ( !data )
		{
			perror("malloc");
			exit(1);
		}
		if ( read(data_file,data,count8 ) != count8 )
		{
			perror("read");
			exit(1);
		}
		
		chunk->data = data;
	}
	
	return chunk;
}

//--------------------------------------------------------------------------------
void help(void)
{
	printf("synopsis: data2txt PATH/TO/FILE gain\n");
	printf("gain: 1|10\n");
	printf("data2txt: convert the content of a binary file,\n");
	printf("alternating respectively left channel and right channel once per line\n");
	printf("for each value, output the following fields, separated by space:\n");
	printf("\tchannel: 0 for left channel, 1 for right\n");
	printf("\toverflow flag: 1 if an overflow has happened\n");
	printf("\tdigital value (hex)\n");
	printf("\tvoltage value (double)\n");
	
}

//--------------------------------------------------------------------------------
int main(int argc, char** argv)
{
	int data_file;
	gain_t gain;
	
	/* must have one arg: file to read data from */ 
	if ( argc != 3 )
	{
		printf("bad number of arguments\n");
		help();
		exit(1);
	}
	
	data_file = open_file(argv[1]);
	
	gain = atoi(argv[2]);
	switch(gain)
	{
		case 1:
		case 10:
			break;
		default:
			fprintf(stderr, "incorrect gain parameter\n");
			help();
			exit(1);
	}
	
	unsigned long count = 0;
	for(;;)
	{
		struct chunk_t * chunk = read_chunk(data_file);
		if ( !chunk)
			break;
		count++;
		output_chunk(gain, count, chunk);
		free_chunk(chunk);
	}
	return 0;
}
