#include "stdafx.h"
#include "AD7940.h"
#include "Stopwatch.h"

int IoAAD7940::Open( int )
{
    Close( );

    int DataBits;

    if ( ! Read( DataBits ) ) return FALSE;  // dummy read operation to force device out of power-down mode

    IoADevice::Open( );  // flag device as open

    return TRUE;
}

int IoAAD7940::SampleRange( double& VoltsMinimum, double& VoltsMaximum, int NumberOfSamples ) const
{
    if ( NumberOfSamples < 1 ) return FALSE;

    double Voltage;

    // Collect first sample
    if ( ! Sample( Voltage ) ) return FALSE;

    VoltsMinimum = VoltsMaximum = Voltage;

    // Collect remaining samples
    for ( int i = 1; i < NumberOfSamples; i++ )
    {
        if ( ! Sample( Voltage ) ) return FALSE;

        // Update sample range
        if ( Voltage < VoltsMinimum) VoltsMinimum = Voltage;
        if ( Voltage > VoltsMaximum) VoltsMaximum = Voltage;
    }

    return TRUE;
}

int IoAAD7940::SampleMedian( double& VoltsMinimum, double& VoltsMedian, double& VoltsMaximum, int NumberOfSamples ) const
{
    const int MaximumNumberOfSamples = 500;

    if ( NumberOfSamples < 1 || NumberOfSamples > MaximumNumberOfSamples ) return FALSE;

    double VBuffer[ MaximumNumberOfSamples ];

    // Collect batch of samples
    for ( int i = 0; i < NumberOfSamples; i++ )
    {
        double Voltage;

        if ( ! Sample( Voltage ) ) return FALSE;

        // Right shift collected samples greater than current sample value
		int j;

        for ( j = i; j > 0 && VBuffer[ j - 1 ] > Voltage; j-- ) VBuffer[ j ] = VBuffer[ j - 1 ];

        // Store current sample value in sorted position
        VBuffer[ j ] = Voltage;
    }

    VoltsMinimum = VBuffer[ 0 ];

    // Find median value
    int MedianIndex = NumberOfSamples / 2;

    if ( NumberOfSamples & 0x1 ) 
    {
        VoltsMedian = VBuffer[ MedianIndex ];  // odd number of samples
    }
    else
    {
        VoltsMedian = ( VBuffer[ MedianIndex - 1 ] + VBuffer[ MedianIndex ] ) / 2.0;  // even number of samples
    }

    VoltsMaximum = VBuffer[ NumberOfSamples - 1 ];

    return TRUE;
}

int IoAAD7940::SampleStatistics( double& VoltsMean, double& VoltsVariance, int NumberOfSamples ) const
{
    if ( NumberOfSamples < 1 ) return FALSE;

    double SumV = 0.0, SumVV = 0.0;

    // Collect batch of samples
    for ( int i = 0; i < NumberOfSamples; i++ )
    {
        double Voltage;

        if ( ! Sample( Voltage ) ) return FALSE;

        SumV  += Voltage;
        SumVV += Voltage * Voltage;
    }

    double Sum1 = (double) NumberOfSamples;

    // Calculate sample mean and variance
    VoltsMean = SumV / Sum1;

    VoltsVariance = SumVV / Sum1 - VoltsMean * VoltsMean;

    return TRUE;
}

int IoAAD7940::SampleStatistics( double& VoltsMean, double& VoltsVariance, double& VoltsPerSecond, int NumberOfSamples ) const
{
    if ( NumberOfSamples < 2 ) return FALSE;

    // Start counting the passage of time
    IoAStopwatch Stopwatch;

    double SumT = 0.0, SumV = 0.0, SumTT = 0.0, SumVV = 0.0, SumTV = 0.0;

    // Collect batch of samples
    for ( int i = 0; i < NumberOfSamples; i++ )
    {
        double Voltage;

        double StartTime = Stopwatch.Seconds( );

        if ( ! Sample( Voltage ) ) return FALSE;

        double EndTime = Stopwatch.Seconds( );

        double T = ( StartTime + EndTime ) / 2.0;  // better approximation for sample time
        double V = Voltage;

        SumT += T;
        SumV += V;

        SumTT += T * T;
        SumVV += V * V;
        SumTV += T * V;
    }

    double Sum1 = (double) NumberOfSamples;

    // Calculate sample mean and variance
    VoltsMean = SumV / Sum1;

    VoltsVariance = SumVV / Sum1 - VoltsMean * VoltsMean;  // = ( Sum1 * SumVV - SumV * SumV ) / ( Sum1 * Sum1 )

    // Calculate sample rate of change using linear regression
    VoltsPerSecond = ( Sum1 * SumTV - SumT * SumV ) / ( Sum1 * SumTT - SumT * SumT );

    return TRUE;
}

int IoAAD7940::SampleRateOfChange( double& VoltsPerSecond, int NumberOfSamples ) const
{
    if ( NumberOfSamples < 2 ) return FALSE;

    // Start counting the passage of time
    IoAStopwatch Stopwatch;

    double SumT = 0.0, SumV = 0.0, SumTT = 0.0, SumTV = 0.0;

    // Collect batch of samples
    for ( int i = 0; i < NumberOfSamples; i++ )
    {
        double Voltage;

        double StartTime = Stopwatch.Seconds( );

        if ( ! Sample( Voltage ) ) return FALSE;

        double EndTime = Stopwatch.Seconds( );

        double T = ( StartTime + EndTime ) / 2.0;  // better approximation for sample time
        double V = Voltage;

        SumT += T;
        SumV += V;

        SumTT += T * T;
        SumTV += T * V;
    }

    double Sum1 = (double) NumberOfSamples;

    // Calculate sample rate of change using linear regression
    VoltsPerSecond = ( Sum1 * SumTV - SumT * SumV ) / ( Sum1 * SumTT - SumT * SumT );

    return TRUE;
}

int IoAAD7940::WaitUntilSettled( double VoltsPerSecondThreshold, int NumberOfSamples, double TimeLimit ) const
{
    const int MaximumNumberOfSamples = 500;

    if ( NumberOfSamples < 2 || NumberOfSamples > MaximumNumberOfSamples ) return FALSE;

    // Buffer samples so that sample rate of change can be updated with each new sample
    double TBuffer[ MaximumNumberOfSamples ], VBuffer[ MaximumNumberOfSamples ];

    // Start counting the passage of time
    IoAStopwatch Stopwatch;

    double SumT = 0.0, SumV = 0.0, SumTT = 0.0, SumTV = 0.0, Sum1 = (double) NumberOfSamples;

    // Collect samples until sample rate falls below threshold (or time limit is exceeded)
    for ( int i = 0; ; i++ )
    {
        int j = i % NumberOfSamples;  // circular sample buffer

        // Once sample buffer is full, see if sample rate of change is below threshold
        if ( j != i )
        {
            double VoltsPerSecond = ( Sum1 * SumTV - SumT * SumV ) / ( Sum1 * SumTT - SumT * SumT );

            // Compare absolute values only
            if ( VoltsPerSecond * VoltsPerSecond < VoltsPerSecondThreshold * VoltsPerSecondThreshold ) break;
        }

        // Get new sample
        double StartTime, Voltage, EndTime;

        if ( ( StartTime = Stopwatch.Seconds( ) ) > TimeLimit ) return FALSE;

        if ( ! Sample( Voltage ) ) return FALSE;

        if ( ( EndTime = Stopwatch.Seconds( ) ) > TimeLimit ) return FALSE;

        // Once sample buffer is full, incrementally update sums
        if ( j != i )
        {
            // Old sample time and value
            double T = TBuffer[ j ];
            double V = VBuffer[ j ];

            // Subtract contribution from oldest sample
            SumT -= T;
            SumV -= V;

            SumTT -= T * T;
            SumTV -= T * V;
        }

        // New sample time and value
        double T = ( StartTime + EndTime ) / 2.0;  // better approximation for sample time
        double V = Voltage;

        // Add contribution from new sample
        SumT += T;
        SumV += V;

        SumTT += T * T;
        SumTV += T * V;

        // Save new sample
        TBuffer[ j ] = T;
        VBuffer[ j ] = V;
    }

    return TRUE;
}

int IoAAD7940::SampleMedianWhenSettled( double& VoltsMinimum, double& VoltsMedian, double& VoltsMaximum,
                                        int NumberOfSamples, double VoltsPerSecondThreshold, double TimeLimit ) const
{
    const int MaximumNumberOfSamples = 500;

    if ( NumberOfSamples < 2 || NumberOfSamples > MaximumNumberOfSamples ) return FALSE;

    // Buffer samples so that sample rate of change can be updated with each new sample
    double TBuffer[ MaximumNumberOfSamples ], VBuffer[ MaximumNumberOfSamples ];

    int Sort[ MaximumNumberOfSamples ] = { 0 };  // array of sample indices pointing to samples in ascending order

    // Start counting the passage of time
    IoAStopwatch Stopwatch;

    double SumT = 0.0, SumV = 0.0, SumTT = 0.0, SumTV = 0.0, Sum1 = (double) NumberOfSamples;

    // Collect samples until sample rate falls below threshold (or time limit is exceeded)
    for ( int i = 0; ; i++ )
    {
        int j = i % NumberOfSamples;  // circular sample buffer

        // Once sample buffer is full, see if sample rate of change is below threshold
        if ( j != i )
        {
            double VoltsPerSecond = ( Sum1 * SumTV - SumT * SumV ) / ( Sum1 * SumTT - SumT * SumT );

            // Compare absolute values only
            if ( VoltsPerSecond * VoltsPerSecond < VoltsPerSecondThreshold * VoltsPerSecondThreshold ) break;
        }

        // Get new sample
        double StartTime, Voltage, EndTime;

        if ( ( StartTime = Stopwatch.Seconds( ) ) > TimeLimit ) return FALSE;

        if ( ! Sample( Voltage ) ) return FALSE;

        if ( ( EndTime = Stopwatch.Seconds( ) ) > TimeLimit ) return FALSE;

        // Once sample buffer is full, incrementally update sums and sort list
        if ( j != i )
        {
            // Old sample time and value
            double T = TBuffer[ j ];
            double V = VBuffer[ j ];

            // Subtract contribution from oldest sample
            SumT -= T;
            SumV -= V;

            SumTT -= T * T;
            SumTV -= T * V;

            // Locate current sample index in sort list
			int k;

            for ( k = 0; Sort[ k ] != j; k++ );

            // Shift sample indices in sort list to accomodate new sample value
            for ( ; k > 0                   && VBuffer[ Sort[ k - 1 ] ] > Voltage; k-- ) Sort[ k ] = Sort[ k - 1 ];  // right shift
            for ( ; k < NumberOfSamples - 1 && VBuffer[ Sort[ k + 1 ] ] < Voltage; k++ ) Sort[ k ] = Sort[ k + 1 ];  // left  shift

            Sort[ k ] = j;
        }
        else
        {
            // Right shift sample indices in sort list to accomodate new sample value
			int k;

            for ( k = j; k > 0 && VBuffer[ Sort[ k - 1 ] ] > Voltage; k-- ) Sort[ k ] = Sort[ k - 1 ];

            Sort[ k ] = j;
        }

        // New sample time and value
        double T = ( StartTime + EndTime ) / 2.0;  // better approximation for sample time
        double V = Voltage;

        // Add contribution from new sample
        SumT += T;
        SumV += V;

        SumTT += T * T;
        SumTV += T * V;

        // Save new sample
        TBuffer[ j ] = T;
        VBuffer[ j ] = V;
    }

    VoltsMinimum = VBuffer[ Sort[ 0 ] ];

    // Find median value
    int MedianIndex = NumberOfSamples / 2;

    if ( NumberOfSamples & 0x1 ) 
    {
        VoltsMedian = VBuffer[ Sort[ MedianIndex ] ];  // odd number of samples
    }
    else
    {
        VoltsMedian = ( VBuffer[ Sort[ MedianIndex - 1 ] ] + VBuffer[ Sort[ MedianIndex ] ] ) / 2.0;  // even number of samples
    }

    VoltsMaximum = VBuffer[ Sort[ NumberOfSamples - 1 ] ];

    return TRUE;
}

int IoAAD7940::Read( int& DataBits ) const
{
    int Bits[ 16 ];

    // Read 16 bits of ADC sample value
    if ( ! ReadBSP( 0, Bits, 16, 0 ) ) return FALSE;

    DataBits = 0x0000;

    for ( int i = 0; i < 16; i++ )  // read MSB -> LSB
    {
        if ( Bits[ i ] ) DataBits |= ( 0x1 << ( 15 - i ) );
    }

    return TRUE;
}
