/* Red Pitaya C API example Acquiring a signal from a buffer and check it
 * This application acquires a signal on a specific channel */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <string.h>

#include "rp_hw-profiles.h"
#include "rp.h"

#define EPS 0.05
#define EPS_F 100


uint32_t getADCRate(){
    uint32_t c = 0;
    if (rp_HPGetBaseFastADCSpeedHz(&c) != RP_HP_OK){
        fprintf(stderr,"[Error] Can't get fast ADC channels count\n");
    }
    return c;
}

#define c_osc_fpga_smpl_freq  getADCRate()
#define c_meas_time_thr  (ADC_BUFFER_SIZE / (1000000000 / getADCRate()))
const float c_meas_freq_thr = 0.05;
const float c_min_period = 19.6e-9; // 51 MHz

void filterBuffer(float *_buffer,int _size){
    float *n_b = malloc(_size * sizeof(float));
    memcpy(n_b,_buffer,_size);
    float core[] = { 1.0 / 8.0 , 1.0 / 4.0 , 1.0 / 4.0 , 1.0 / 4.0 , 1.0 / 8.0};
    for(int i = 2 ; i < _size - 2 ; i++ ){
        float sum = 0;
        for (int j = -2 ; j <= 2 ; j++ ){
            sum += core[j + 2] * _buffer [i + j];
        }
        n_b[i] = sum;
    }
    memcpy(_buffer,n_b,_size);
    free(n_b);
}

bool checkAmplitudeAndFreq(float *_buff, uint32_t _size,float _nominal,float *min, float *max,float *frequency){
        int trig_t[2] = { 0, 0 };
        int trig_cnt = 0;
        int state = 0;
        if (_size > 0){
                *min = _buff[0];
                *max = _buff[0];
                for (int i = 1 ; i < _size ; ++i){
                        if (*min > _buff[i]) *min = _buff[i];
                        if (*max < _buff[i]) *max = _buff[i];
                }

                uint32_t dec_factor = 1;
                rp_AcqGetDecimationFactor(&dec_factor);

                float acq_dur=(float)(_size)/((float) c_osc_fpga_smpl_freq) * (float) dec_factor;
                float cen = (*max + *min) / 2;
                float thr1 = cen + 0.2 * (*min - cen);
                float thr2 = cen + 0.2 * (*max - cen);
                float res_period = 0;
                for(int i = 0; i < _size; i++) {
                        float sa = _buff[i];
                        if((state == 0) && (sa < thr1)) {
                                state = 1;
                        }
                        if((state == 1) && (sa >= thr2) ) {
                                state = 0;
                                if (trig_cnt++ == 0) {
                                trig_t[0] = i;
                                } else {
                                trig_t[1] = i;
                                }
                        }
                        if ((trig_t[1] - trig_t[0]) > c_meas_time_thr) {
                                break;
                        }
                }
                if(trig_cnt >= 2) {
                        res_period = (float)(trig_t[1] - trig_t[0]) /
                                ((float)c_osc_fpga_smpl_freq * (trig_cnt - 1)) * dec_factor;
                }

                if( ((thr2 - thr1) < c_meas_freq_thr) ||
                        (res_period * 3 >= acq_dur)   ||
                        (res_period < c_min_period) ){
                        res_period = 0;
                }
                float period = res_period * 1000.f;
                period = (period == 0.f) ?  0.000001f : period;
                *frequency = (float) (1 / (period / 1000.0));
                if ((fabs(*min + _nominal) < EPS) && (fabs(*max - _nominal) < EPS))
                        return true;
                return false;
        }
        return false;
}

float trapezoidalApprox(double *data, float T, int size){
    double result = 0;
    for(int i = 0; i < size - 1; i++){
        result += data[i] + data[i+1];
    }
    result = ((T / 2.0) * result);
    return result;
}

bool isSineTester(float *data, uint32_t size)
{
        uint32_t dec_factor = 1;
        rp_AcqGetDecimationFactor(&dec_factor);
        double T = (dec_factor / getADCRate());
        double ch_rms[size];
        double ch_avr[size];
        for(int i = 0; i < size; i++) {
                ch_rms[i] = data[i] * data[i];
                ch_avr[i] = fabs(data[i]);
        }
        double K0 = sqrtf(T * size * trapezoidalApprox(ch_rms, T, size)) / trapezoidalApprox(ch_avr, T, size);
        return ((K0 > 1.10) && (K0 < 1.12));
}


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

        bool fillState = false;
	int  counter=100;
        /* Print error, if rp_Init() function failed */
        if(rp_Init() != RP_OK){
                fprintf(stderr, "Rp api init failed!\n");
        }

        rp_GenReset();
        rp_GenFreq(RP_CH_1, 20000.0);
        rp_GenAmp(RP_CH_1, 1.0);
        rp_GenWaveform(RP_CH_1, RP_WAVEFORM_SINE);
        rp_GenOutEnable(RP_CH_1);


        uint32_t buff_size = 16384;
        float *buff = (float *)malloc(buff_size * sizeof(float));

        rp_AcqReset();
        rp_AcqSetDecimation(RP_DEC_8);
        rp_AcqSetTriggerLevel(RP_CH_1, 0);
        rp_AcqSetTriggerDelay(ADC_BUFFER_SIZE/2.0);

        while(counter--){
                fillState = false;
                rp_AcqStart();

                /* After acquisition is started some time delay is needed in order to acquire fresh samples in to buffer*/
                /* Here we have used time delay of one second but you can calculate exact value taking in to account buffer*/
                /*length and smaling rate*/

                sleep(1);
                rp_AcqSetTriggerSrc(RP_TRIG_SRC_CHA_PE);
                rp_acq_trig_state_t state = RP_TRIG_STATE_TRIGGERED;

                while(1){
                        rp_AcqGetTriggerState(&state);
                        if(state == RP_TRIG_STATE_TRIGGERED){
                        break;
                        }
                }

                while(!fillState){
                        rp_AcqGetBufferFillState(&fillState);
                }

                rp_AcqStop();
                rp_AcqGetOldestDataV(RP_CH_1, &buff_size, buff);
                filterBuffer(buff,buff_size);
                printf("Acquiring Done\n");
                float min = 0;
                float max = 0;
                float frequency = 0;
                bool  isBrokenSignal = false;
                if (checkAmplitudeAndFreq(buff,buff_size,1.0, &min , &max , &frequency)) {
                        printf("\tAmplitude is correct MIN = %0.4f , MAX = %0.4f\n",min,max);
                }else{
                        printf("\tAmplitude is not correct MIN = %0.4f , MAX = %0.4f\n",min,max);
                        isBrokenSignal = true;
                }
                if (fabs(frequency - 20000.0) < EPS_F) {
                        printf("\tFrequency is correct %0.4f\n",frequency);
                }else{
                        printf("\tFrequency is not correct %0.4f\n",frequency);
                        isBrokenSignal = true;
                }

                if (isSineTester(buff,buff_size)){
                        printf("\tSignal form is sine\n");
                }else{
                        printf("\tSignal form is not sine\n");
                        isBrokenSignal = true;
                }

                printf("Signal is %s\n\n",isBrokenSignal ? "not correct" : "correct");
        }

        /* Releasing resources */
        free(buff);
        rp_Release();
        return 0;
}
