// xp1current.cpp
// Nathan Rollins
// 15 July 2003
/**********************************************************************
 ** This file reads the current from the SLAAC-1V board and reports it
 ** in mA to a file (power.dat) or to STDOUT.  The current is sampled
 ** on channel 1 (2.5 V channel) 5000 times and averaged.
 **********************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <S1VBoard.h>
#include <sys/time.h>

#define MAX_RAND 30000
#define MIN_RAND 25
#define REPEAT 5000

_SYSTEM_TYPE(Slaac1VBoard);

void averageCurrent(UINT uset[REPEAT][3]);
void faverageCurrent(UINT uset[REPEAT][3], ofstream* ofs);
void parseArgs(int argc, char *argv[]);
void readChannels(S1VBoard *board, UINT mask);
void readCurrent(S1VBoard *board);
void recordChannels(S1VBoard *board, UINT mask, UINT* uset);
void sampleStepped(S1VBoard* board, int steps, UINT mask, UINT uset[REPEAT][3]);
void sampleFreerun(S1VBoard* board, UINT mask, UINT uset[REPEAT][3]);
void sampleStopped(S1VBoard* board, UINT mask, UINT uset[REPEAT][3]);
void stall(int rndusec);
void usage();

bool freerun = true;
bool runx0 = false;
bool runx1 = false;
bool runx2 = false;
bool screen = false;
bool stepped = false;
bool stop = false;
bool resetall = false;

char* file = "power.dat";
char* x0 = NULL;
char* xp1 = NULL;
char* xp2 = NULL;

int freq = 20;

int main(int argc, char* argv[]) {

  int steps = 100000;
  ofstream* ofs;
  S1VBoard* board;
  UINT mask = (ADC_2_5V_CH);
  UINT values[REPEAT][3];

  if (argc < 1) {
    usage();
    exit(-1);
  }
  else {
    parseArgs(argc, argv);
  }
  
  ofs = new ofstream(file);
  cout << "XP1 FILE: " << xp1 << endl;
  cout << "X0 FILE: " << x0 << endl;
  cout << "XP2 FILE: " << xp2 << endl;

  /* Setup the SLAAC-1V board */
  board = new S1VBoard(0, FALSE);
  SetMessageLevel(AbsolutelySilent);
  board->InitBoard(x0, xp1, xp2, freq);
  board->ADCStart(mask);
  board->ADCReadoutHex(mask);
  board->Reset(SLAAC_X0 | SLAAC_X1 | SLAAC_X2, TRUE);
  board->Stop();
  board->ADCReadoutHex(mask);
  
  /* Unreset the FPGAs */
  if (runx1 || resetall)
    board->Reset(SLAAC_X1, FALSE);
  if (runx0 || resetall)
    board->Reset(SLAAC_X0, FALSE);
  if (runx2 || resetall)
    board->Reset(SLAAC_X2, FALSE);

  /* Sample the current from a freerunning, stepped, or stopped board */
  if (freerun) {
    board->Run();
    sampleFreerun(board, mask, values);
  }
  else if (stepped) { 
    sampleStepped(board, steps, mask, values);
  }
  else {
    sampleStopped(board, mask, values);
  }

  /* Powerdown the board */
  board->ADCPowerdown(ADC_FULLPD);
  board->ADCStop();

  /* Print the results of the sampling */
  if (screen)
    averageCurrent(values);
  else
    faverageCurrent(values, ofs);

  return 0;
  
}

/**********************************************************************
 ** This function takes the current samples, averages them and
 **  prints the results to STDOUT 
**********************************************************************/
void averageCurrent(UINT uset[REPEAT][3]) {
  
  float avg[3] = {0.0, 0.0, 0.0};

  for (int i=0; i<REPEAT; i++) {
    avg[0] += (float)uset[i][0];
    avg[1] += (float)uset[i][1];
    avg[2] += (float)uset[i][2];
  }
  
  avg[0] = avg[0]/(float)REPEAT;
  avg[1] = avg[1]/(float)REPEAT;
  avg[2] = avg[2]/(float)REPEAT;

  cout << "Average Currents:" << endl << "\tChannel0: " << avg[0] << endl;
  cout << "\tChannel1: " << avg[1] << endl << "\tChannel2: " << avg[2] << endl << endl;

}

/**********************************************************************
 ** This function takes the current samples, averages them and
 ** prints the results to a given file. 
**********************************************************************/
void faverageCurrent(UINT uset[REPEAT][3], ofstream* ofs) {
  
  float avg[3] = {0.0, 0.0, 0.0};

  for (int i=0; i<REPEAT; i++) {
    avg[0] += (float)uset[i][0];
    avg[1] += (float)uset[i][1];
    avg[2] += (float)uset[i][2];
  }
  
  avg[0] = avg[0]/(float)REPEAT;
  avg[1] = avg[1]/(float)REPEAT;
  avg[2] = avg[2]/(float)REPEAT;

  *(ofs) << "Average Currents:" << endl << "\tChannel0: " << avg[0] << endl;
  *(ofs) << "\tChannel1: " << avg[1] << endl << "\tChannel2: " << avg[2] << endl << endl;

}

/**********************************************************************
 ** This function parses the command-line arguments 
**********************************************************************/
void parseArgs(int argc, char *argv[]) {

  for (int params=1; params<argc; params++) {
    if (!strcmp(&(argv[params][1]), "freerun") ||
        !strcmp(&(argv[params][1]), "fr")) {
      stepped = false;
      stop = false;
      freerun = true;
    }
    else if (!strcmp(&(argv[params][1]), "freq")) {
      params++;
      freq = atoi(argv[params]);
    }
    else if (!strcmp(&(argv[params][1]), "help") ||
             !strcmp(&(argv[params][1]), "h")) {
      usage();
      exit(0);
    }
    else if (!strcmp(&(argv[params][1]), "o")) {
      params++;
      file = new char[strlen(argv[params])+1];
      strcpy(file, argv[params]);
    }
    else if (!strcmp(&(argv[params][1]), "resetall") ||
             !strcmp(&(argv[params][1]), "ra")) 
      resetall = true;
    else if (!strcmp(&(argv[params][1]), "screen"))
      screen = true;
    else if (!strcmp(&(argv[params][1]), "stepped") ||
             !strcmp(&(argv[params][1]), "s")) {
      stepped = true;
      freerun = false;
      stop = false;
    }
    else if (!strcmp(&(argv[params][1]), "stop")) {
      stop = true;
      stepped = false;
      freerun = false;
    }
    else if (!strcmp(&(argv[params][1]), "x0")) {
      params++;
      runx0 = true;
      x0 = new char[strlen(argv[params])+1];
      strcpy(x0, argv[params]);
    }
    else if (!strcmp(&(argv[params][1]), "x1")) {
      params++;
      runx1 = true;
      xp1 = new char[strlen(argv[params])+1];
      strcpy(xp1, argv[params]);
    }
    else if (!strcmp(&(argv[params][1]), "x2")) {
      params++;
      runx2 = true;
      xp2 = new char[strlen(argv[params])+1];
      strcpy(xp2, argv[params]);
    }
    else {
      usage();
      exit(-1);
    }
  }    
}

/**********************************************************************
 ** This function takes a single current sample from the SLAAC-1V board and 
 ** immediately prints the results out to STDOUT. 
 **********************************************************************/
void readChannels(S1VBoard *board, UINT mask) {

  CHANNEL_STRUCT chan;
  
  board->ADCRead(mask, &chan);
  cout << "Channels:\n\tChannel 1: " << chan.ch1 << endl << "\tChannel 2: ";
  cout << chan.ch2 << endl << "\tChannel 0: " << chan.ch0 << endl << endl;

}

/**********************************************************************
 ** This function takes a single current sample from the SLAAC-1V board and 
 ** immediately prints the results out to STDOUT (DEPRICATED). 
 **********************************************************************/
void readCurrent(S1VBoard *board) {

  UINT curr[3];

  curr[0] = 0;
  curr[1] = 0;
  curr[2] = 0;

  curr[0] = board->ReadCurrent(ADC_5V, ADC_NOPD);
  curr[1] = board->ReadCurrent(ADC_3_3V, ADC_NOPD);
  curr[2] = board->ReadCurrent(ADC_2_5V, ADC_NOPD);

  cout << "\t+5V: " << curr[0] << endl << "\t+3.3V: " << curr[1] << endl;
  cout << "\t+2.5V: " << curr[2] << endl << endl;
  
}

/**********************************************************************
 ** This function takes a current sample from the SLAAC-1V board and records
 ** the results in a data structure. 
 **********************************************************************/
void recordChannels(S1VBoard *board, UINT mask, UINT* uset) {

  CHANNEL_STRUCT chan;
  
  board->ADCRead(mask, &chan);
  uset[0] = chan.ch0;
  uset[1] = chan.ch1;
  uset[2] = chan.ch2;

}

/**********************************************************************
 ** This function samples the SLAAC-1V board current while the clock is
 ** running.  The function waits a 'random' number of us between each
 ** sample 
 **********************************************************************/
void sampleFreerun(S1VBoard* board, UINT mask, UINT uset[REPEAT][3]) {

  struct timeval t1;
  struct timezone zone;
  int usec = 0, rnd = 0;

  for (int i=0; i<REPEAT; i++) {
    gettimeofday(&t1, &zone);
    usec = t1.tv_usec;
    srand(usec); 
    rnd = rand()%(MAX_RAND);  
    //cout << "round " << i << endl;
    if (rnd < MIN_RAND)
      rnd = MIN_RAND;

    stall(rnd);
    recordChannels(board, mask, uset[i]);
  }

}

/**********************************************************************
 ** This function samples the SLAAC-1V board current after stepping the 
 ** clock a given number of steps.  The current is repeatedly sampled a
 ** set number of times.
 **********************************************************************/
void sampleStepped(S1VBoard* board, int steps, UINT mask, UINT uset[REPEAT][3]) {
  for (int i=0; i<REPEAT; i++) {
    board->Step(steps);
    recordChannels(board, mask, uset[i]);
  }
}

/**********************************************************************
 ** This function samples the SLAAC-1V board current while the clock is
 **  stopped.  The function waits a 'random' number of us between each 
 **  sample.
 **********************************************************************/
void sampleStopped(S1VBoard* board, UINT mask, UINT uset[REPEAT][3]) {

  struct timeval t1;
  struct timezone zone;
  int usec = 0, rnd = 0;

  for (int i=0; i<REPEAT; i++) {
    gettimeofday(&t1, &zone);
    usec = t1.tv_usec;
    srand(usec); 
    rnd = rand()%(MAX_RAND);  
    if (rnd < MIN_RAND)
      rnd = MIN_RAND;

    stall(rnd);
    recordChannels(board, mask, uset[i]);
  }
}

/**********************************************************************
 ** This function will cause the program to wait a given number 
 ** of us (could have just used the C 'wait()' function) 
 **********************************************************************/
void stall(int rndusec) {
  
  struct timeval t1, t2;
  struct timezone zone;
  int usec = 0, diff = 0;

  gettimeofday(&t1, &zone);
  usec = t1.tv_usec;

  while(diff < rndusec) {
    gettimeofday(&t2, &zone);
    diff = abs(t2.tv_usec - usec);
    //cout << "usec " << usec << " diff " << diff << " rnd " << rndusec << endl;
  }
  
}

/**********************************************************************
 ** This function prints the usage information. 
 **********************************************************************/
void usage() {

  cout << endl;
  cout << "xp1current Usage: -x1 <xp1_bitfile> [opts]" << endl;
  cout << "opts:" << endl;
  cout << "\t-freerun | -fr \t\t Keep the clock SLAAC-1V clock freerunning while sampling (default)" << endl;
  cout << "\t-freq <frequency> \t Run the SLAAC-1V clock at the specified frequency (default = 20 MHz)" << endl;
  cout << "\t-help | -h \t\t Print this message" << endl;
  cout << "\t-o <output_file> \t Print the results to the specified file (default = power.dat)" << endl;
  cout << "\t-screen \t\t Print the results to STDOUT" << endl;
  cout << "\t-stepped | -s \t\t Step the SLAAC-1V clock (rather than freerun)" << endl;
  cout << "\t-stop \t\t\t Do not run the SLAAC-1V clock at all" << endl;
  cout << "\t-x0 <x0_bitfile> \t Load PE0 with the specified bitfile (default = NULL)" << endl;
  cout << "\t-x2 <xp2_bitfile> \t Load PE2 with the specified bitfile (default = NULL)" << endl;
  cout << endl;
}
