// Copyright Doug Lewis Sept 2017 

#include <stdio.h>
#include <stdlib.h>
#include <prussdrv.h>
#include <pruss_intc_mapping.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>


#include </home/erps/daq2/pruSrc/pruadc.h>

 
unsigned int adc_pru_data0[ADC_PRU_DATA_SIZE];
unsigned int adc_pru_data1[ADC_PRU_DATA_SIZE];

typedef struct adc_conf_t {
   unsigned int cycles_per_clock;
   unsigned int down_sample;
   unsigned int control_string;
   unsigned int num_blocks;  // File size in number of 128k blocks to record
   unsigned int block_size;
   unsigned int mode;
   unsigned int addr;
   unsigned int size;
   } adc_conf;
   


unsigned int readFileValue(char filename[]){
   FILE* fp;
   unsigned int value = 0;
   fp = fopen(filename, "rt");
   fscanf(fp, "%x", &value);
   fclose(fp);
   return value;
}


unsigned int adc_conf_set_address(adc_conf *conf) {
    //conf->addr = ADC_PRU_DATA_SIZE + readFileValue(MMAP1_LOC "addr");
    conf->addr = readFileValue(MMAP1_LOC "addr");
}

unsigned int adc_conf_get_address(adc_conf *conf) {
    return (conf->addr);
}

unsigned int adc_conf_set_size(adc_conf *conf) {
    conf->size = readFileValue(MMAP1_LOC "size");
}

unsigned int adc_conf_get_size(adc_conf *conf) {
    return(conf->size);
}

int adc_conf_set_cycles_per_clock (adc_conf *conf, unsigned int cycles) {
    conf->cycles_per_clock = cycles;
}

unsigned int adc_conf_get_cycles_per_clock (adc_conf *conf) {
    return (conf->cycles_per_clock) ;
}

int adc_conf_set_mode(adc_conf *conf, unsigned int mode){
    conf->mode = mode;
} 

int adc_conf_set_block_size(adc_conf *conf, unsigned int block_size){
    conf->block_size = block_size;
} 

unsigned int adc_conf_get_mode (adc_conf *conf) {
    return (conf->mode) ;
}

int adc_conf_set_down_sample(adc_conf *conf, unsigned int down_sample){
    conf->down_sample = down_sample;
} 

unsigned int adc_conf_get_down_sample(adc_conf *conf){
    return (conf->down_sample); 
} 
int adc_conf_set_control_string(adc_conf *conf, unsigned int control_string){
    conf->control_string = control_string;
} 

unsigned int adc_conf_get_control_string(adc_conf *conf){
    return (conf->control_string); 
} 

int adc_conf_set_data_file_size(adc_conf *conf, unsigned int data_file_size){
    conf->num_blocks = data_file_size;
} 

unsigned int adc_conf_get_data_file_size(adc_conf *conf){
    return (conf->num_blocks); 
}

int default_daq_init(adc_conf *conf) {
    adc_conf_set_cycles_per_clock(conf, ADS_CLOCKS);
    adc_conf_set_mode(conf, STOP);
    adc_conf_set_down_sample(conf, ADS_SAMPLE_DIVISOR);
    adc_conf_set_control_string(conf, ADS_CONTROL_STRING);
    adc_conf_set_block_size(conf, BUFSIZE);  // Default block size is 128k
    adc_conf_set_data_file_size(conf, DAQ_FILE_SIZE);
    adc_conf_set_address(conf);
    adc_conf_set_size(conf);
} 

int adc_conf_settings(adc_conf *conf) {
    
return (OK);
}

int main(int argc, char **argv) 
{
    char in_char = '0';
    struct adc_conf *conf;
    unsigned int *sample;
    void *addr;
    int read_fd;
    int client_sock_fd = 0;
    int i = 0;

    conf = malloc(sizeof (adc_conf));
    sample = malloc(sizeof(unsigned int) * 8);

    if(getuid()!=0){
        printf("You must run this program as root. Exiting.\n");
        exit(EXIT_FAILURE);
    }
    // Initialize structure used by prussdrv_pruintc_intc
    // PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h
    tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;

    // Read in the location and address of PRU shared memory. This value changes
    // each time a new block of memory is allocated.

    default_daq_init((adc_conf *) conf);


    // data for setting up the ADC on PRU0. 
    adc_pru_data0[0] = adc_conf_get_cycles_per_clock ((adc_conf *)conf);
    adc_pru_data0[1] = adc_conf_get_mode ((adc_conf *)conf); 
    adc_pru_data0[2] = adc_conf_get_control_string ((adc_conf *)conf); 
    adc_pru_data0[3] = CHANGE;   // First time through, set to reflect changed.
    adc_pru_data0[4] = adc_conf_get_address((adc_conf *)conf);
    adc_pru_data0[5] = adc_conf_get_size((adc_conf *)conf);
    adc_pru_data0[6] = adc_conf_get_down_sample ((adc_conf *)conf); 
    adc_pru_data0[7] = adc_conf_get_data_file_size ((adc_conf *)conf);

    // data for setting up the ADC on PRU1. 
    adc_pru_data1[0] = adc_conf_get_cycles_per_clock ((adc_conf *)conf);
    adc_pru_data1[1] = adc_conf_get_mode ((adc_conf *)conf); 
    adc_pru_data1[2] = adc_conf_get_control_string ((adc_conf *)conf); 
    adc_pru_data1[3] = CHANGE;   // First time through, set to reflect changed.
    adc_pru_data1[4] = adc_conf_get_address((adc_conf *)conf); 
    adc_pru_data1[5] = adc_conf_get_size((adc_conf *)conf);
    adc_pru_data1[6] = adc_conf_get_down_sample ((adc_conf *)conf); 
    adc_pru_data1[7] = adc_conf_get_data_file_size ((adc_conf *)conf);

    for (i = 8; i < 32; i ++)
        adc_pru_data1[i] = 0;
    printf("PRU1 Clock Counter %d \n", adc_pru_data1[0]);
    printf("PRU1 ADC State   %d \n", adc_pru_data1[1]);
    printf("ADC1 Control String   %x \n", adc_pru_data1[2]);
    printf("ADC1 Control String Modified    %d \n", adc_pru_data1[3]);
    printf("PRU Shared Memory Address Pool: 0x%x\n", adc_pru_data1[4]);
    printf("PRU Shared Memory Pool Size: 0x%x\n", adc_pru_data1[5]);
    printf("Sample Divisor: 0x%x\n", adc_pru_data1[6]);
    printf("Number of 128k blocks to record 0x%x\n", adc_pru_data1[7]);

    // Allocate and initialize memory
    prussdrv_init ();
    prussdrv_open (PRU_EVTOUT_0);
 
    // Write the address and size into PRU0 Data RAM0. 
    prussdrv_pru_write_memory(PRUSS0_PRU1_DATARAM, 0, adc_pru_data1, 32);

    // Map PRU's interrupts
    prussdrv_pruintc_init(&pruss_intc_initdata);
 
    // Load and execute the PRU program on the PRU
    prussdrv_exec_program (PRU1_ADC_CONTROL_PRU, "./pruadc1.bin");
    printf("ADC Control PRU1 program running on PRU %x \n", PRU1_ADC_CONTROL_PRU);

    //prussdrv_exec_program (PRU0_ADC_CONTROL_PRU, "./pruadc0.bin");
    //printf("ADC Control PRU0 program running on PRU %x \n", PRU0_ADC_CONTROL_PRU);
 
    if((read_fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1){
        printf("Failed to open memory!");
       return (ERR);
    }

    while (in_char != 'q') {
        printf("Enter Command : \n");
        scanf( " %c", &in_char);
        switch (in_char) {

            // Write the file to a binary file. It cannot be opened to look at 
            // and will require postprocessing.
            case  'b' :
               printf("Binary data collection to file \n");
               printf("Data collection started \n");
               adc_pru_data0[1] = RUN;
               adc_pru_data0[3] = CHANGE;
               prussdrv_pru_write_memory(PRUSS0_PRU1_DATARAM, 0, adc_pru_data1, ADC_PRU_DATA_SIZE);
              // prussdrv_pru_write_memory(PRUSS0_PRU0_DATARAM, 0, adc_pru_data0, ADC_PRU_DATA_SIZE);
               data_to_bin_file (conf, client_sock_fd);           
               break;

            case 'c' :
               generate_cal_data( );
               break;

            case 'e':
               client_sock_fd = create_bind_accept_sock();
               if (client_sock_fd != ERR)
                  printf("Socket Successfully created \n");
               printf("Entering client control mode \n");
               client_main(conf, client_sock_fd, adc_pru_data0, adc_pru_data1);

               break;
            // Write the file in a formatted hex value, with space separation 
            // between each of the channels in a sample so that the file is
            // human readable for quick looks.
            case 'f' : 
               printf("Formatted data collection \n");
               printf("Data collection started \n");
               adc_pru_data1[1] = RUN;
               adc_pru_data1[3] = CHANGE;
               prussdrv_pru_write_memory(PRUSS0_PRU1_DATARAM, 0, adc_pru_data1, ADC_PRU_DATA_SIZE);
               //prussdrv_pru_write_memory(PRUSS0_PRU0_DATARAM, 0, adc_pru_data0, ADC_PRU_DATA_SIZE);
               write_file_formatted ((adc_conf *)conf, 1);           
               //write_file_formatted ((adc_conf *)conf, 0);           
               break;

            case 'h' :
               printf("Data collection halted \n");
               adc_pru_data1[1] = STOP;
               adc_pru_data1[3] = CHANGE;
               prussdrv_pru_write_memory(PRUSS0_PRU1_DATARAM, 0, adc_pru_data1, ADC_PRU_DATA_SIZE);
               prussdrv_pru_write_memory(PRUSS0_PRU0_DATARAM, 0, adc_pru_data0, ADC_PRU_DATA_SIZE);
               break;

            // Get a single sample, print it to the screen
            case 'o':
               printf("getting a single sample \n");
               adc_pru_data1[1] = RUN;
               adc_pru_data1[3] = CHANGE;
               adc_conf_set_block_size((adc_conf *)conf, 32);
               prussdrv_pru_write_memory(PRUSS0_PRU1_DATARAM, 0, adc_pru_data1, ADC_PRU_DATA_SIZE);
               sampleGet((adc_conf *)conf, 1, sample);
               break;

            // Write the file as binary. Indicate to PRU1 that this is a throughput test.
            // The data from PRU1 will contain a 24 bit counter which is incremented for 
            // each sample.`
            case 't' :
            printf("Throughput Test Mode, Binary data collection to file \n");
               printf("Data generation started \n");
               adc_pru_data1[1] = TEST;
               adc_pru_data1[3] = CHANGE;
               addr = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, read_fd, adc_pru_data1[4] & ~MAP_MASK);
               bzero((void *) addr, (size_t) (PRU1_START_OFFSET + BUF_HDR_SIZE)); 
               prussdrv_pru_write_memory(PRUSS0_PRU1_DATARAM, 0, adc_pru_data1, ADC_PRU_DATA_SIZE);
               data_to_bin_file ((adc_conf *)conf, client_sock_fd);
               break;

            case 's' :
               printf("Start PRU Data collection, and return \n");
               adc_pru_data1[1] = TEST;
               adc_pru_data1[3] = CHANGE;
               prussdrv_pru_write_memory(PRUSS0_PRU1_DATARAM, 0, adc_pru_data1, ADC_PRU_DATA_SIZE);
               break;

            case 'p' :
               get_buff_stats((adc_conf *)conf);
               break;
            default :
               printf("Command not recognized %c \n", in_char);
        }
    }
    prussdrv_exit ();
}
 
int client_main(adc_conf *conf, int clientSockFd)
{
    char sendMessage[256] = {'\0'};
    char recvMessage[256];
    char exit[256];
    char timeString[32];
    int readSize = 0;
    char recStartMessage[256];
    size_t length = 0;

    strcpy(exit, "exit"); 

    while (strncmp(exit, recvMessage, 4) != 0)
    {
               readSize = recv(clientSockFd, recvMessage, sizeof(recvMessage) , 0);
               printf("Received message: %s   from client \n", recvMessage);
               if (strncmp("record", recvMessage, 6) == 0)
               {
                 strncpy (sendMessage, "recstart,2000   \0 ", 13);
                 length = strlen(sendMessage);
                 send(clientSockFd, sendMessage, length, 0);             
                 strncpy(timeString, &recvMessage[6], 14);
                 recording_start(conf,
                                 clientSockFd,
                                 timeString); 
            
                 strncpy (sendMessage, "recdone         ", 16);
                 length = strlen(sendMessage);
                 send(clientSockFd, sendMessage, length, 0);             
               }
    }
    close(clientSockFd);
}

int recording_start( adc_conf *conf,
                     int client_sock_fd,
                     char timeString[])
{
    char fileString[128] = "/home/erps/data/dataFile";

    printf("Binary data collection to file \n");
    printf("Data collection started \n");
    //adc_pru_data1[1] = TEST;
    adc_pru_data1[1] = RUN;
    adc_pru_data1[3] = CHANGE;
    prussdrv_pru_write_memory(PRUSS0_PRU1_DATARAM, 0, adc_pru_data1, ADC_PRU_DATA_SIZE);
    // prussdrv_pru_write_memory(PRUSS0_PRU0_DATARAM, 0, adc_pru_data0, ADC_PRU_DATA_SIZE);

    strncat(fileString, timeString, 14);
    strncat(fileString, ".bin\0", 6);
    printf("Recording File = %s \n", fileString);

    data_to_bin_file (conf, client_sock_fd, fileString);

}


// Write data to a file so that the hex info can be looked at raw.
// Formatting makes it easy to look at hex values of each channel per sample.
// Also, this operation takes a relatively small set of samples, as opposed to
// a longer duration continuous recording.
int write_file_formatted (adc_conf *conf, int pru_num)
{

    int read_fd;
    FILE *write_fd;
    void *map_base, *virt_addr;
    unsigned long read_result;
    int result;
    unsigned int addr = conf->addr;
    unsigned int size = conf->size;

    if((read_fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1){
        printf("Failed to open memory!");
       return (ERR);
    }
    printf("Memory file open successfully \n");

    if (pru_num == 1) {
        if((write_fd = fopen("/home/erps/data/outfile1.dat", "w")) == NULL){
            printf("Failed to open data file \n");
           return (ERR);
           addr += PRU1_START_OFFSET;
           printf("waiting on interrupt \n");
           prussdrv_pru_wait_event (PRU_EVTOUT_0);
        }
    } else if (pru_num == 0) { 
        if((write_fd = fopen("/home/erps/data/outfile0.dat", "w")) == NULL){
            printf("Failed to open data file \n");
           return (ERR);
           addr += PRU0_START_OFFSET;
        }
    } else {
        printf("Invalid PRU %d \n", pru_num);
        return(ERR);
    } 
   
    printf("Data file open successfully \n");

    map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, read_fd, addr & ~MAP_MASK);

    if(map_base == (void *) -1) {
       printf("Failed to map base address");
       return (ERR);
    }


    printf("Reading data \n");
    int i = 0;
    int j = 0;
    for(i = 0; i < size; i+= (8 * BYTES_PER_SAMPLE)){

        for (j = 0; j < 8; j++) {
            virt_addr = map_base + (addr & MAP_MASK);
            read_result = *((uint32_t *) virt_addr);
           fprintf( write_fd, "0x%08x ", read_result);
           addr += BYTES_PER_SAMPLE;
        }
        fprintf(write_fd, "\n");
    }

    if ((result = fclose (write_fd)) != 0) {
       printf("Error closing data file %d \n", result);
    }

    return (OK);
}


int sampleGet (adc_conf *conf, int pru_num, uint32_t *sample)
{

    int read_fd;
    FILE *write_fd;
    void *map_base, *virt_addr;
    unsigned long read_result;
    int result;
    unsigned int addr = conf->addr;

    if((read_fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1){
        printf("Failed to open memory!");
       return (ERR);
    }
    printf("Memory file open successfully \n");


    map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, read_fd, conf->addr & ~MAP_MASK);

    if(map_base == (void *) -1) {
       printf("Failed to map base address");
       return (ERR);
    }


    printf("Reading data \n");
    int i = 0;
    int j = 0;
    for(i = 0; i < conf->size; i+= (8 * BYTES_PER_SAMPLE)){

        for (j = 0; j < 8; j++) {
           virt_addr = map_base + (addr & MAP_MASK);
           //read_result = *((uint32_t *) virt_addr);
           *sample  = *((uint32_t *) virt_addr);
           sample ++;
           printf( "0x%08x ", sample);
           addr += BYTES_PER_SAMPLE;
        }
        fprintf(write_fd, "\n");
    }

    return (OK);
}


int get_buff_stats(adc_conf *conf) 
{

    int i, buffs, buff_head_ptr;
    int read_fd;
    void *map_base, *virt_addr;
    unsigned long read_result;
    unsigned int addr = conf->addr;

    unsigned int pru1_wr_flag = addr;
    unsigned int pru1_buff_count = addr + 4;
    unsigned int pru1_head = addr + 8;
    unsigned int pru1_wrap_count = addr +12;

    unsigned int pru0_wr_flag = addr + 0x80;
    unsigned int pru0_buff_count = addr + 0x84;
    unsigned int pru0_head = addr + 0x88;
    unsigned int pru0_wrap_count = addr + 0x92;

    if((read_fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1){
        printf("Failed to open memory!");
       return (ERR);
    }
    printf("Memory file open successfully \n");


    map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, read_fd, addr & ~MAP_MASK);

    if(map_base == (void *) -1) {
       printf("Failed to map base address");
       return (ERR);

    }

    virt_addr = map_base + (pru1_wr_flag & MAP_MASK);
    read_result = *((uint32_t *) virt_addr);
    printf ("PRU1_WR_FLAG = %x ", read_result);
    
    virt_addr = map_base + (pru1_buff_count & MAP_MASK);
    read_result = *((uint32_t *) virt_addr);
    printf ("PRU1_BUFF_COUNT = %x ", read_result);

    addr += 4;
    virt_addr = map_base + (pru1_head & MAP_MASK);
    read_result = *((uint32_t *) virt_addr);
    printf ("PRU1_HEAD = %x ", read_result);

    virt_addr = map_base + (pru1_wrap_count & MAP_MASK);
    read_result = *((uint32_t *) virt_addr);
    printf ("PRU1_WRAP_COUNT = %x  \n", read_result);

    virt_addr = map_base + (addr & MAP_MASK);
    read_result = *((uint32_t *) virt_addr);
    printf ("PRU0_WR_FLAG = %x ", read_result);

    virt_addr = map_base + (addr & MAP_MASK);
    read_result = *((uint32_t *) virt_addr);
    printf ("PRU0_BUFF_COUNT = %x ", read_result);

    virt_addr = map_base + (addr & MAP_MASK);
    read_result = *((uint32_t *) virt_addr);
    printf ("PRU0_HEAD = %x ", read_result);

    virt_addr = map_base + (addr & MAP_MASK);
    read_result = *((uint32_t *) virt_addr);
    printf ("PRU0_WRAP_COUNT = %x  \n\n", read_result);

    return (OK);
}




int tail_increment(int tail, int num)
{
    tail += num;
    if (tail == QUEUE_SIZE)
        tail = 0;
    return (tail);
}


int create_bind_accept_sock()
{
    #define PORT 8888 
    struct sockaddr_in server, client;
    int addr_len = sizeof(server);
    int sock_descriptor, client_sock;
    int backlog = 5;

    sock_descriptor = socket(AF_INET, SOCK_STREAM, 0);
    if(sock_descriptor == ERR)
    {
        printf("Error opening socket\n");
        return -1;
    }
    printf("Socket Created \n");

    server.sin_port = htons(PORT);
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_family = AF_INET;

    if (bind(sock_descriptor, (struct sockaddr *)&server, sizeof(struct sockaddr_in) ) == -1)
    {
        printf("Error binding socket\n");
        return (ERR);
    }

    printf("Socket Successfully bound to port %u\n", PORT);

    listen(sock_descriptor, backlog);

    addr_len = sizeof(server);
    client_sock = accept(sock_descriptor, (struct sockaddr *) &client, (socklen_t*) &addr_len);
    if (client_sock < 0) 
    {
          error("ERROR on accept"); 
    }
    else
    {
        printf("Client socket connection accepted \n");
    }
    return(client_sock);
}

int send_buffs_to_host (void *virt_addr, int num_buffs_to_write, int sock_fd) {

    int32_t bytesToSend = num_buffs_to_write * BUFSIZE;
    int32_t sent = 0;
    uint8_t *addr = virt_addr;
    struct timeval timeout;

    timeout.tv_sec = 2;
    timeout.tv_usec = 0;

    while (bytesToSend > 0 || (setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) > 0))
    //while (bytesToSend > 0)
                                                                                                                  
    { 
	if ((sent = send(sock_fd, (uint8_t*)addr, bytesToSend, 0)) < 0)
	{
            printf("ERROR: Failed to send buffer contents across TCPIP.\n");
            system("ifconfig eth0 192.168.8.2");
            return(ERR);
        }
        bytesToSend -= sent;
        addr += sent;
    }
}


process_buffers(void *virt_addr, int num_buffs_to_write, int sock_fd, FILE *write_fd, int8_t flash_record, int8_t network_send)
{
    uint32_t wr_num;

    if (flash_record == ENABLED) 
         wr_num = write_buffs_to_flash (virt_addr, num_buffs_to_write, write_fd); 

    if (network_send == ENABLED)
        send_buffs_to_host (virt_addr, num_buffs_to_write, sock_fd);

}


int write_buffs_to_flash (void *virt_addr, int num_buffs_to_write, FILE * write_fd) {
    int wr_num = 0;

    wr_num = fwrite ( virt_addr, sizeof(uint8_t),   num_buffs_to_write * BUFSIZE, write_fd);

    if (wr_num != num_buffs_to_write * BUFSIZE) 
    {
      printf ("Error writing flash file %d \n", errno);
      strerror(errno);
    }
    return (wr_num);
}
 
int data_to_bin_file (adc_conf *conf, int client_sock_fd, char dataFile[])
{            
    int i, j, k;
    int  buffs = 0;
    unsigned long read_result;
    int read_fd;
    FILE *write_fd;
    void *map_base, *virt_addr;
    void *pru1_wr_flag_addr;
    void *pru1_buff_count_addr;
    void *pru1_head_addr;
    void *pru1_wrap_count_addr;
    int wr_num = 0;
    int tail = 0;
    int num_buffs = 0;
    int num_buffs_to_write = 0;
    int last_count = 0;
    int num_buffs_processed = 0;
    volatile unsigned long pru1_wr_flag = 0;
    volatile unsigned long pru1_buff_count = 0;
    volatile unsigned long pru1_head = 0; 
    volatile unsigned long pru1_wrap_count = 0; 
    unsigned int addr = conf->addr;
    unsigned int pru1_conf_addr = conf->addr;
    int iteration = 0;
    int count = 0;
    int network_send = TRUE;
    //int network_send = FALSE;
    int flash_record = TRUE;

    if((read_fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1){
        printf("Failed to open memory!");
       return (ERR);
    }
    printf("Memory file open successfully \n");

    write_fd =  fopen(dataFile, "wb");
    if( write_fd  == NULL){
        printf("Failed to open data file \n");
       return (ERR);
    }

    printf("Data file open successfully \n");

    map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, read_fd, addr & ~MAP_MASK);

    if(map_base == (void *) -1) {
       printf("Failed to map base address");
       return (ERR);
    }
    else {
       printf("Base Address = %x \n", map_base);
    }
 
#if DEBUG 
    printf("Dumping header data \n");
    i = 0;
    j = 0;
    for(i = 0; i < (PRU1_START_OFFSET + 256); i+= (8 * BYTES_PER_SAMPLE)){

        for (j = 0; j < 8; j++) {
            virt_addr = map_base + (addr & MAP_MASK);
            read_result = *((uint32_t *) virt_addr);
           printf(  "0x%08x ", read_result);
           addr += BYTES_PER_SAMPLE;
        }
        printf("\n");
    }
#endif 
    pru1_conf_addr = conf->addr;

    addr = conf->addr + PRU1_START_OFFSET; 
    pru1_wr_flag_addr = map_base + ((pru1_conf_addr + PRU1_WR_FLAG) & MAP_MASK);
    pru1_wr_flag = *((uint32_t *) pru1_wr_flag_addr);

    pru1_buff_count_addr = map_base + ((pru1_conf_addr + PRU1_BUFF_COUNT) & MAP_MASK);
    pru1_buff_count = *((uint32_t *) pru1_buff_count_addr);

    pru1_head_addr = map_base + ((pru1_conf_addr + PRU1_HEAD) & MAP_MASK);
    pru1_head = *((uint32_t *) pru1_head_addr);

    pru1_wrap_count_addr = map_base + ((pru1_conf_addr + PRU1_WRAP_COUNT) & MAP_MASK);
    pru1_wrap_count = *((uint32_t *) pru1_wrap_count_addr);

    tail = 0;

    while (num_buffs_processed < DAQ_FILE_SIZE)  
    { 
        virt_addr  = map_base + ((pru1_conf_addr + PRU1_WR_FLAG) & MAP_MASK);
        pru1_wr_flag = *((uint32_t *) pru1_wr_flag_addr);

        pru1_buff_count_addr = map_base + ((pru1_conf_addr + PRU1_BUFF_COUNT) & MAP_MASK);
        pru1_buff_count = *((uint32_t *) pru1_buff_count_addr);

        pru1_head_addr = map_base + ((pru1_conf_addr + PRU1_HEAD) & MAP_MASK);
        pru1_head = *((uint32_t *) pru1_head_addr);

        pru1_wrap_count_addr = map_base + ((pru1_conf_addr + PRU1_WRAP_COUNT) & MAP_MASK);
        pru1_wrap_count = *((uint32_t *) pru1_wrap_count_addr);

        if (pru1_wr_flag == 1)
        { 
            num_buffs = pru1_buff_count - last_count;
            
            if (num_buffs > 0)
            {
               // printf("h = %d, t = %d, num_buffs = %d ", pru1_head, tail, num_buffs);
            }
            if (num_buffs <= QUEUE_SIZE) 
            {
                if (tail < pru1_head) 
                {
                    num_buffs_to_write = pru1_head - tail;
                    virt_addr =  map_base + (addr & MAP_MASK);
                    process_buffers(virt_addr, num_buffs_to_write, client_sock_fd, write_fd, flash_record, network_send);
                    num_buffs_processed += num_buffs_to_write;
                    addr += num_buffs_to_write * BUFSIZE;
                    tail = tail_increment(tail, num_buffs_to_write);
                    pru1_wr_flag = 0;
                }
                else if (tail > pru1_head)
                {
                    // Write the buffers from the tail to the end of the queue.
                    virt_addr =  map_base + (addr & MAP_MASK);
                    num_buffs_to_write = QUEUE_SIZE - tail;
                    process_buffers(virt_addr, num_buffs_to_write, client_sock_fd, write_fd, flash_record, network_send);

                    //addr += num_buffs_to_write * BUFSIZE; 
                    tail = tail_increment(tail, num_buffs_to_write);
                    num_buffs_processed += num_buffs_to_write;

                    // Write the buffers from the beginning of the queue (zeroth element) to the head
                    // We have to remember to set the pointer back to the beginning of shared memory

                    addr = conf->addr + PRU1_START_OFFSET;
                    virt_addr =  map_base + (addr & MAP_MASK);
                    num_buffs_to_write = pru1_head;

                    process_buffers(virt_addr, num_buffs_to_write, client_sock_fd, write_fd, flash_record, network_send);
                    addr += num_buffs_to_write * BUFSIZE;
                    num_buffs_processed += num_buffs_to_write;
                    tail = tail_increment(tail, num_buffs_to_write);
                }
                else if ((tail == pru1_head) && (num_buffs == QUEUE_SIZE))
                {
                    // Write the buffers from the tail to the end of the queue.
                    virt_addr =  map_base + (addr & MAP_MASK);
                    num_buffs_to_write += QUEUE_SIZE  - tail;

                    process_buffers(virt_addr, num_buffs_to_write, client_sock_fd, write_fd, flash_record, network_send);
                    num_buffs_processed += num_buffs_to_write;
                    addr += num_buffs_to_write * BUFSIZE; 
                    tail = tail_increment(tail, num_buffs_to_write);

                    // Write the buffers from the beginning of the queue to the head
                    addr = conf->addr + PRU1_START_OFFSET;
                    virt_addr =  map_base + (addr & MAP_MASK);
                    num_buffs_to_write = pru1_head;

                    process_buffers(virt_addr, num_buffs_to_write, client_sock_fd, write_fd, flash_record, network_send);
                    addr += num_buffs_to_write * BUFSIZE; 
                    num_buffs_processed += num_buffs_to_write;
                    tail = tail_increment(tail, num_buffs_to_write); // This may look funny, but we are incrementing tail by tail.
                }
                pru1_wr_flag = 0;
                if (num_buffs > 0)
                {
                    count += num_buffs;
                    if (count >= 30)
                    {
                        printf("h = %d, t = %d, num_buffs = %d ", pru1_head, tail, num_buffs);
                        printf("num_procd = %d, addr = %x \n", num_buffs_processed, addr);
                        count = 0;
                    }
                   // if (num_buffs_processed >= 180) 
                   // {
                   //     printf("num_procd = %d, addr = %x \n", num_buffs_processed, addr);
                   //     // this is a place to set a breakpoint
                   // }
                }
                last_count = pru1_buff_count;
                usleep(100);
            }
            else if (num_buffs > QUEUE_SIZE) 
            {
                // We have detected an overflow condition, likely due to some linux related activity.
                // For now we throw out the buffers that have accumulated. Later we can explore saving 
                // those buffers.
                printf("Queue overflow %d \n", num_buffs);
                last_count = pru1_buff_count;
                num_buffs_processed += num_buffs;
                usleep(100);
            }
        }
    }
    printf("TOTAL num_buffs_processed = %d \n", num_buffs_processed);
    fclose ((FILE *)write_fd);

    return (OK);
}


generate_cal_data( ) {

    FILE *read_5v_fd;
    FILE *read_0v_fd;
    FILE *write_fd;
    uint32_t average_5v[8];
    uint32_t average_0v[8];
    float ma[8];
    uint32_t b[8];
    int i;
    uint32_t temp;

    read_5v_fd =  fopen("/home/erps/caldata/board1_5v_golden_063018.dat", "r");
    if( read_5v_fd  == NULL){
        printf("Failed to open 5v cal data file \n");
       return (ERR);
    }
    else
        printf("Cal File Opened \n");

    read_and_average_cal_data(read_5v_fd, &average_5v[0]);
    printf("5V Averages %x %x %x %x %x %x %x %x \n\n",average_5v[0], average_5v[1],
             average_5v[2], average_5v[3], average_5v[4], average_5v[5], average_5v[6], average_5v[7]);
    
    read_0v_fd =  fopen("/home/erps/caldata/board1_0v_golden_063018.dat", "r");
    if( read_0v_fd  == NULL){
        printf("Failed to open 0v cal data file \n");
       return (ERR);
    }
    else
        printf("Cal File Opened \n");

    write_fd =  fopen("/home/erps/caldata/board1_cal_golden_063018.dat", "w");
    if( write_fd  == NULL){
        printf("Failed to open cal result file \n");
       return (ERR);
    }
    else
        printf("Cal Result File Opened \n");

    read_and_average_cal_data(read_0v_fd, &average_0v[0]);

    printf("0V Averages %x %x %x %x %x %x %x %x \n\n",average_0v[0], average_0v[1],
             average_0v[2], average_0v[3], average_0v[4], average_0v[5], average_0v[6], average_0v[7]);

    for (i = 0; i < 8; i++)
    {
        // y = mx + b
        //ma = (yH - yL) / (xH - xL)
        if (average_0v[i] & 0x800000) 
        {
            temp = ~average_0v[i] & 0x00FFFFFF; 
            ma[i] = (float)(average_5v[i] + temp) / 0x7FFFFF ;
            b[i] = temp * ma[i];       
        } 
        else 
        {
            ma[i] = (float)(average_5v[i] - average_0v[i]) / 0x7FFFFF ;
            b[i] = average_0v[i] * ma[i];
        }
    }

    printf("\n\n");
    printf(" ma:%f %f %f %f %f %f %f %f \n", ma[0], ma[1], ma[2], ma[3], ma[4], ma[5], ma[6], ma[7]);
    printf(" b :%x %x %x %x %x %x %x %x \n", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);

    fprintf(write_fd, "%f %f %f %f %f %f %f %f \n", ma[0], ma[1], ma[2], ma[3], ma[4], ma[5], ma[6], ma[7]);
    fprintf(write_fd, "%x %x %x %x %x %x %x %x \n", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
    fclose ((FILE *)read_5v_fd);
    fclose ((FILE *)read_0v_fd);
    fclose ((FILE *)write_fd);
}

read_and_average_cal_data(FILE *read_fd, uint32_t *average[]) {
    int i = 0;
    int j = 0;
    uint32_t s[8];
    uint64_t total[8];
    int64_t tot[8];

    for (i = 0; i < 9; i ++){
        fscanf(read_fd, "%x %x %x %x %x %x %x %x \n", &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7]);
//        printf("%x %x %x %x %x %x %x %x \n", s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]);
    }

    for (i = 0; i < 8; i ++) {
        total[i] = 0;
        s[i] = 0;
        average[i] = 0;
    }

    for (i = 0; i < 8; i ++) {
        for (j = 0; j < 8; j++) {
            fscanf(read_fd, "%x", &s[j]);
            s[j] &= s[j] & 0x0FFFFFF;
            if (s[j] & 0x800000) 
            {
              s[j] = ~s[j] & 0xFFFFFF;
              tot[j] -= s[j];
            }
            tot[j] += s[j]; 
            total[j] += s[j];
        }
        fscanf(read_fd, "\n");
    }

        printf("Totals  %x %x %x %x %x %x %x %x \n \n",total[0], total[1], total[2], total[3], total[4], total[5], total[6], total[7]);
        printf("Totals  %x %x %x %x %x %x %x %x \n \n",(uint32_t)tot[0], (uint32_t) tot[1], (uint32_t) tot[2], (uint32_t) tot[3],
             (uint32_t) tot[4], (uint32_t) tot[5], (uint32_t)tot[6], (uint32_t)tot[7]); 
    for (i = 0; i < 8; i ++)
    {
        average[i] = total[i] / 8;
    }
}
