#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include "wrg526api.h"

G5_STATUS G5Stat;
G5_HW G5Hw;
G5_RADIO Radios[16];
uint32_t NumberOfRadios;
uint8_t HasPllSpeed;
   
void ShowError(char *FunctionName)
{
 uint32_t i;

    printf("%s failed.\n",FunctionName);

    if(Radios!=NULL && NumberOfRadios!=0)
    {
        for(i=0;i<NumberOfRadios;i++)
        {
            printf("%d. radio status: %d\n",i,Radios[i].Status); 
        }
    }
}

/* ---------------------------------------------------------------------------*/
//display radio information 

void ShowSide(char flags)
{
  if (flags & 0x01)
    printf(" (lo-side)");
  else
    printf(" (hi-side)");
}

//Show the current status of the /RM and /TM hardware.

void ShowStatus(G5_STATUS * status)
{
  printf("========================================\n");
  printf("G526/RM (Master)\n");
  printf("LO1: DDSfreq = %8u, VCOfreq = %10u, LOfreq = %10u",
    status->lo1.ddsfreq,  status->lo1.vcofreq, status->lo1.freq);
  ShowSide(status->lo1.flags);
  printf("\n");

  printf("LO2: DDSfreq = %8u, VCOfreq = %10u, LOfreq = %10u",
    status->lo2.ddsfreq,  status->lo2.vcofreq, status->lo2.freq);
  ShowSide(status->lo2.flags);
  printf("\n");


  printf("\nG526/TM (Slaves)\n");
  printf("RF atten: %u dB, Band# %u [%u - %u]\n",
    status->rf.atten, status->rf.band, status->rf.fbot, status->rf.ftop);

  printf("IF1 filter: "); 
  if (status->rf.flags & 0x04)
    printf("480");
  else
    printf("860");
  printf(" MHz\n");
  
  printf("IF2 gain: ");
  if (status->if2.gain == 0)
    printf("0");
  else
    printf("-20");
   printf(" dB\n");  
  printf("========================================\n\n");
}

uint8_t SetFrequency(uint32_t Frequency)
{
 int32_t Error;
 uint32_t HwStatus;
 uint32_t i;

//First. set the RM module to select the new frequency, and update the band selection in the TM modules
    Error=G5_SetFrequency(Radios,NumberOfRadios,Frequency,&G5Stat,&G5Hw,0x01); //0x01 - force

    if(Error!=0)
    {
        printf("Set frequency to %u Hz failed.\n",Frequency);

        for(i=0;i<NumberOfRadios;i++)
        {
            printf("%d. radio status: %d\n",i,Radios[i].Status); 
        }

        return 0;
    }
    else
    {
        printf("G5_SetFrequency [%u] OK\n",Frequency);
    }

//Second. Update the phase adjustment register in each TM module.
//This step is not necessary if hardware phase adjustment is not required.

    for(i=1;i<NumberOfRadios;i++)
    {
        Radios[i].Phase=173; //set same arbitrary value in all slaves
    }

    Error=G5_SetPhase(Radios,NumberOfRadios);

    if(Error!=0)
    {
        ShowError("G5_SetPhase");
        return 0;
    }
    else
    {
        printf("G5_SetPhase OK\n");
    }

//Now fetch and display the hardware status

    Error=G5_GetHwStatus(Radios,NumberOfRadios,&HwStatus);

    if(Error!=0)
    {
        ShowError("G5_GetHwStatus");
        return 0;
    }

    if((HwStatus & 0x30000)==0x30000)
    {
        printf("LOs locked quickly\n");
    }
    else
    {
        printf("Waiting for LOs to lock\n");

        for(i=0;i<50;i++)
        {
            Error=G5_GetHwStatus(Radios,NumberOfRadios,&HwStatus);

            if(Error!=0)
            {
                ShowError("G5_GetHwStatus");
                return 0;
            }

            if((HwStatus & 0x30000)==0x30000)
            {
                break;
            }
        }

        if(i==50)
        {
            printf("Took too long to lock!\n");
            return 0;
        }
    }

//This test will over-ride the default "flipping" of LO2 to avoid "internal birdies".
//Normally you would not do this.
    
    if((G5Stat.lo1.flags ^ G5Stat.lo2.flags) & 1)
    {
        //if the two INJECTION flags are different, the output spectrum is inverted.
        //tune to the same frequency again, with alternate mixing on LO2
        printf("Output spectrum was inverted.\n");
        printf("Forcing alternate injection on LO2 to correct the spectrum.\n");
        G5_SetFrequency(Radios,NumberOfRadios,Frequency,&G5Stat,&G5Hw,0x80); //0x80 - force alternate injection on LO2
    }
    else
    {
        printf("Output spectrum is true\n");
    }

//Some older /RM models automatically shift to a "fast" settling speed during tuning
//This test then switches back to the "slow" setting afterwards, which gives better phase noise.
    
    if(HasPllSpeed)
    { 
        //need to switch LO1 to "slow" settling speed for best phase noise.
        Error=G5_SetPllSpeed(Radios,NumberOfRadios,1,&G5Stat,&G5Hw);

        if(Error!=0)
        {
            ShowError("G5_SetPllSpeed");
        }

    }

    return 1;
}

int main(int argc, char *argv[])
{
 int32_t Error;
 uint32_t i;

    bzero(&G5Stat,sizeof(G5Stat));
    bzero(&G5Hw,sizeof(G5Hw));


    printf("\n");

    if(!LoadG526API())
    {
        return 0;
    }

    Error=G5_FindRadios(Radios,sizeof(Radios)/sizeof(Radios[0]),&NumberOfRadios);

    if(Error!=0)
    {
        printf("G5_FindRadios failed!\n");
        goto Exit;
    }

    if(NumberOfRadios==0)
    {
        printf("No G526 receiver found.\n");
        goto Exit;
    }

    printf("Found %u G526 receivers.\n",NumberOfRadios);

    for(i=0;i<NumberOfRadios;i++)
    {
        printf("%d. SerNum: %s\n",i,Radios[i].SerNum);
    }

    printf("\n");

    if (Radios[0].Model == 0)
    {
        printf("No Reference Modules found!\n");
        goto Exit;
    }

    switch(Radios[0].Model & 0x0D)  //ignore bit 1
    {
        case 0x01:
        case 0x04:
        case 0x05:
            HasPllSpeed=1; //these versions have a fast/low switch on LO1
            break;

        default:
            HasPllSpeed=0;
            break;
    }

    //open receivers
    Error=G5_OpenRadios(Radios,NumberOfRadios);

    if(Error!=0)
    {
        ShowError("G5_OpenRadios");
        goto Exit;
    }

    //power on for all the receivers
    for(i=0;i<NumberOfRadios;i++)
    {
        Radios[i].Power=1;
    }

    Error=G5_SetPower(Radios,NumberOfRadios);

    if(Error!=0)
    {
        ShowError("G5_SetPower");
        goto Exit;
    }
    else
    {
        printf("G5_SetPower OK\n");
    }


    Error=G5_SetIFGain(Radios,NumberOfRadios,1,&G5Stat,&G5Hw);

    if(Error!=0)
    {
        ShowError("G5_SetIFGain");
        goto Exit;
    }
    else
    {
        printf("G5_SetIFGain OK\n");
    }

    Error=G5_SetRFAttn(Radios,NumberOfRadios,0,&G5Stat,&G5Hw);

    if(Error!=0)
    {
        ShowError("G5_SetRFAttn");
        goto Exit;
    }
    else
    {
        printf("G5_SetRFAttn OK\n");
    }


    if(SetFrequency(20000000))
    {
        printf("Tuned to 20MHz\n");
    }
    else
    {
        goto Exit;
    }

    ShowStatus(&G5Stat);

    printf("Press ENTER to set frequency to 1200MHz\n");
    getchar();


    if(SetFrequency(1200000000))
    {
        printf("Tuned to 1200MHz\n");
    }
    else
    {
        goto Exit;
    }

    ShowStatus(&G5Stat);

    printf("Press ENTER to set frequency to 2000MHz\n");
    getchar();


    if(SetFrequency(2000000000))
    {
        printf("Tuned to 2000MHz\n");
    }
    else
    {
        goto Exit;
    }

    ShowStatus(&G5Stat);

    printf("\nPress ENTER to exit.\n");
    getchar();

    for(i=0;i<NumberOfRadios;i++)
    {
        Radios[0].Power=0;
    }

    G5_SetPower(Radios,NumberOfRadios);

    G5_CloseRadios(Radios,NumberOfRadios);

Exit:

    UnloadG526API();

    return 0;
}
