#include "stdafx.h"
#include "ICS307.h"

// Declare and initialise static data members

const int IoAICS307Register::OutputDividerLUT[ 8 ] = { 10, 2, 8, 4, 5, 7, 3, 6 };  // output divider values

double IoAICS307Register::CrystalLoadCapacitance( ) const
{
    double CConst[ 2 ][ 4 ] = {  22.3,  23.1,  23.7,  24.4, 22.1,  22.9,  23.5,  24.2 };
    double CMulti[ 2 ][ 4 ] = { 0.083, 0.093, 0.106, 0.120, 0.094, 0.108, 0.120, 0.135 };

    return CConst[ Power( ) ][ C( ) ] - CMulti[ Power( ) ][ C( ) ] * ReferenceClockMHz( );
}

int IoAICS307Register::SetCLK1( double ClockHz )
{
    if ( ! IsReferenceClockValid( ) ) return FALSE;

    double ClockKHz = ClockHz  / 1000.0;
    double ClockMHz = ClockKHz / 1000.0;

    // See if output frequency within the device's absolute frequency range
    if ( ClockMHz < MinimumCLK1MHz( ) || ClockMHz > MaximumCLK1MHz( ) ) return FALSE;

    // Calculate range of output divider values which would keep VCO output frequency within allowable range
    double ODMinimum = MinimumVCOOutputMHz( ) / ClockMHz;
    double ODMaximum = MaximumVCOOutputMHz( ) / ClockMHz;

    double MinimumClockDelta = 1000000000.0;

    int ss, vv, rr;

    // Search for valid clock parameters
    for ( int s = 0; s <= 7; s++ )
    {
        double OD = (double) OutputDividerLUT[ s ];

        // See if output divider value is within allowable range
        if ( OD < ODMinimum || OD > ODMaximum ) continue;

        double VCOOutputKHz = OD * ClockKHz;

        for ( int v = 4; v <= 511; v++ )
        {
            double PLLInputKHz = VCOOutputKHz / ( 2.0 * (double) ( v + 8 ) );

            // See if PLL input frequency is within allowable range ( >= 200KHz )
            if ( PLLInputKHz < 200.0 ) continue;

            double R = ReferenceClockKHz( ) / PLLInputKHz - 2.0;

            // See if reference divider value is within allowable range
            if ( R < 1.0 || R > 127.0 ) continue;

            int r = (int) R;

            double ClockDelta = ( ReferenceClockHz( ) * 2.0 * (double) ( v + 8 ) / ( (double) ( r + 2 ) * OD ) ) - ClockHz;

            if ( ClockDelta < MinimumClockDelta )
            {
                ss = s;
                vv = v;
                rr = r;

                MinimumClockDelta = ClockDelta;
            }

            if ( (double) r < R )
            {
                double ClockDelta = ClockHz - ( ReferenceClockHz( ) * 2.0 * (double) ( v + 8 ) / ( (double) ( ( r + 1 ) + 2 ) * OD ) );

                if ( ClockDelta < MinimumClockDelta )
                {
                    ss = s;
                    vv = v;
                    rr = r + 1;

                    MinimumClockDelta = ClockDelta;
                }
            }
        }
    }

    // Check for failure to find valid clock parameters
    if ( MinimumClockDelta == 1000000000.0 ) return FALSE;

    // Configure device for clock output closest to requested frequency
    SetS( ss );
    SetV( vv );
    SetR( rr );

    return TRUE;
}

int IoAICS307Register::IsValid( ) const
{
    if ( ! IsReferenceClockValid( ) ) return FALSE;

    if ( ReferenceClockKHz( ) < 200.0 * (double) ( R( ) + 2 ) ) return FALSE;

    double VCOOutputMHz = ReferenceClockMHz( ) * 2.0 * (double) ( V( ) + 8 ) / (double) ( R( ) + 2 );

    if ( VCOOutputMHz < MinimumVCOOutputMHz( ) || VCOOutputMHz > MaximumVCOOutputMHz( ) ) return FALSE;

    return TRUE;
}

double IoAICS307Register::CLK1Hz( ) const
{
    if ( IsValid( ) )
    {
        double Clock1Hz = m_ReferenceClockHz * 2.0;

        Clock1Hz /= (double) ( R( ) + 2 );      // reference divider
        Clock1Hz *= (double) ( V( ) + 8 );      // VCO divider
        Clock1Hz /= (double) OutputDivider( );  // output divider

        return Clock1Hz;
    }

    return 0.0;
}

double IoAICS307Register::CLK2Hz( ) const
{
    if ( IsValid( ) )
    {
        switch ( F() )
        {
            case eReferenceClock:    return m_ReferenceClockHz;
            case eReferenceClockBy2: return m_ReferenceClockHz / 2.0;
            case eOff:               return 0.0;
            case eClock1By2:         return CLK1Hz( ) / 2.0;
        }
    }

    return 0.0;
}

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

    if ( ! Write( m_Register ) ) return FALSE;  // synchronize hardware and software registers

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

    return TRUE;
}

int IoAICS307::Synchronize( const IoAICS307Register& Register )
{
    if ( Register != m_Register )
    {
        if ( IsOpen( ) )
        {
            if ( ! Write( Register ) ) return FALSE;
        }

        m_Register = Register;
    }

    return TRUE;
}

int IoAICS307::Write( const IoAICS307Register& Register ) const
{
    int Bits[ 24 ];

    // Write 24 register bits
    for ( int i = 0; i < 24; i++ ) Bits[ i ] = Register.Bit( 23 - i );  // write MSB -> LSB

//    if ( ! WriteBSP( 0, Bits, 24, 0 ) ) return FALSE;
    if ( ! WriteBSP( 1, Bits, 24, 0 ) ) return FALSE;

    return TRUE;
}
