/*!
 * \file
 *    main.c
 * \brief
 *    Main application file
 *
 *  Created on: May 23, 2020
 *      Author: Christos Choutouridis AEM: 8997
 *      email : <cchoutou@ece.auth.gr>
 */
#include "assign2_impl.h"

/*
 * Global data
 */
stats_t stats;

// A function to implement bubble sort 
void bubbleSort(clock_t arr[], size_t n) {
   size_t i, j;
   clock_t t; 
   for (i =0; i <n-1; ++i)
      // Last i elements are already in place    
      for (j = 0; j<n-i-1; ++j)  
         if (arr[j] > arr[j+1]) {
            t = arr[j];
            arr[j] = arr[j+1];
            arr[j+1] = t;
         }
} 

/*!
 * Calculates and return the average of an array of measurements
 * \param t    Pointer to measurements
 * \param n    Size of measurements array
 * \return     The average
 */
fp_data_t average (const clock_t *t, size_t n) {
   fp_data_t ret =0;
   for (size_t i=0 ; i<n ; ++i)
      ret += t[i];
   return ret / n;
}

/*!
 * Calculates and return the median of an array of measurements
 * \param t    Pointer to measurements
 * \param n    Size of measurements array
 * \return     The average
 */
fp_data_t median (clock_t *t, size_t n) {
   bubbleSort (t, n);
   return (n % 2) ? t[n/2] : (t[n/2] + t[n/2 -1]) /2;
}

/*!
 * Calculates and return the std. deviation of an array of measurements
 * \param t    Pointer to measurements
 * \param n    Size of measurements array
 * \return     The average
 */
fp_data_t std_deviation (const clock_t* t, size_t n) {
   fp_data_t av = average (t, n);
   fp_data_t s =0;
   for (size_t i=0 ; i<n ; ++i) {
      s += (t[i]-av)*(t[i]-av);
   }
   return sqrt (s/n);
}

/*!
 * Leading edge trigger experiment
 * \param out     Pointer to array to store the measurements
 * \param n       Number of measurements
 */
void leading (clock_t *out, size_t n) {
   srand(0);
   rand();

   LED (OFF);
   for (size_t i =0 ; i<n ; ++i) {
      clock_t t1, t2;
      HAL_Delay(rand() % (MAX_WAIT_TIME + 1));
      LED(ON);
      t1 = clock ();
      while (BTN())
         ;
      t2 = clock ();
      LED (OFF);
      out[i] = CPUtime2msec(_CLOCK_DIFF(t2, t1));
   }
}

/*!
 * Trailing edge trigger experiment
 * \param out     Pointer to array to store the measurements
 * \param n       Number of measurements
 */
void trailing (clock_t *out, size_t n) {
   srand(0);
   rand();

   LED (ON);
   for (size_t i =0 ; i<n ; ++i) {
      clock_t t1, t2;
      HAL_Delay(rand() % (MAX_WAIT_TIME + 1));
      LED(OFF);
      t1 = clock ();
      while (BTN())
         ;
      t2 = clock ();
      LED (ON);
      out[i] = CPUtime2msec(_CLOCK_DIFF(t2, t1));
   }
}


/*
 * Main
 */
int main(void) {
   clock_t times[MEASUREMENTS];

   LLD_Init (SYSTICK_FREQ);      // Initialize the board

   // Experiment
   #if MODE == MODE_LEADING_EDGE
      leading (times, MEASUREMENTS);
   #elif MODE == MODE_TRAILING_EDGE
      trailing (times, MEASUREMENTS);
   #endif

   // Get statistical data
   stats.average = average ((const clock_t*)times, MEASUREMENTS);
   stats.median = median (times, MEASUREMENTS);
   stats.std_dev = std_deviation((const clock_t*)times, MEASUREMENTS);

   // Flash 10Hz to indicate the end of experiment
   while (1) {
      HAL_Delay(msec2CPUtime(50));
      LED (ON);
      HAL_Delay(msec2CPUtime(50));
      LED (OFF);
   }
}
