/*******************************************************************************
 TESINA n°5 per esame di: ELABORAZIONI dei SEGNALI ed INFORMAZIONI di MISURA:

 NOMEFILE: Interfaccia.cpp

 Prof. : DAPONTE              Allievi:  CICERO    Leopoldo
                                        D'ONOFRIO Giovanni Matteo
 GRUPPO: « C »                          ZOLLO     Daniele
                                        ABATE     Maurizio
                                        DI GIOIA  Giuseppe

                   SPECIAL GUEST STAR:  GRELLA Francesca Rosaria

 NOTE: Nel file sono riportate le classi principali per l'applicazione e per il
       form principale.
*******************************************************************************/

#include <vcl.h>
#pragma hdrstop

// Vengono caricati gli Header relativi ai file necessari al funzionamento
// dell'applicazione.

#include "Interfaccia.h"
#include "Unit2.h"     // Header del Generatore di Segnale
#include "Unit3.h"     // Header dell'Oscilloscopio
#include "Unit4.h"     // Header per il Pannello acquisizione dati e FFT
#include "Unit5.h"     // Header relativo all'AboutBox sugli autori
#include "Mod_AM.h"    // Header per la modulazione di ampiezza
#include "Mod_FM.h"    // Header per la modulazione di frequenza
#include "Mod_BM.h"    // Header per la modulazione a Burst
#include "Mod_FSK.h"   // Header per la modulazione Frequency Shift Key
#include "Mod_FS.h"    // Header per la modulazione Frequency Sweep

#include "decl-32.h"   // Header file con le funzioni di libreria per la IEEE488

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

#pragma link "BorlandC_gpib-32.obj"  //File .OBJ per linkare il codice relativo alla 488
#pragma package(smart_init)
#pragma resource "*.dfm"

#define TIMEOUT T10s        // Tempo max relativo alla risposta del dispositivo
#define EOT 1               //
#define EOS 0               //

//

int PrimaryGwave   = 10;    // Indirizzo primario del Generatore di Segnale
int PrimaryOscil   =  1;    // Indirizzo primario dell'Oscilloscopio
int SecondaryGwave =  0;    // Indirizzo secondario del Generatore di Segnale
int SecondaryOscil =  0;    // Indirizzo secondario dell'Oscilloscopio
int BoardIndex     =  0;    // Indirizzo relativo alla scheda 488

int DevGwave, DevOscil;     // Identificativi dei due dispositivi da rilevare

bool Strum1Presente;
bool Strum2Presente;

TForm1 *Form1;
Interfaccia488 *Scheda488;

/*******************************************************************************
 La funzione GpibError notifica che c'é stato un errore nell'esecuzione di un
 comando IEEE 488 e stampa un messaggio di errore sul form principale nell'area
 console 488.
*******************************************************************************/
void __fastcall Interfaccia488 :: GpibError(char *msg,int Device, int BoardIndex)
{
    char error[100] ="";    // stringa che descrive l'errore occorso.

    strcat(error, msg);
    strcat(error, "\n");

    /*  viene verificato lo stato del dispositivo. 'ibsta' e' una variabile
        globale definita dalla libreria GPIB che viene aggiornata dalle
        funzioni di libreria GPIB NI-488 invocate. Le costanti utilizzate sono
        definite nella libreria e identificano gli stati possibili. */

    if (ibsta & TIMO) strcat(error, "TIMO\n");
    if (ibsta & END ) strcat(error, "END\n");
    if (ibsta & SRQI) strcat(error, "SRQI\n");
    if (ibsta & RQS ) strcat(error, "RQS\n");
    if (ibsta & CMPL) strcat(error, "CMPL\n");
    if (ibsta & LOK ) strcat(error, "LOK\n");
    if (ibsta & REM ) strcat(error, "REM\n");
    if (ibsta & CIC ) strcat(error, "CIC\n");
    if (ibsta & ATN ) strcat(error, "ATN\n");
    if (ibsta & TACS) strcat(error, "TACS\n");
    if (ibsta & LACS) strcat(error, "LACS\n");
    if (ibsta & DTAS) strcat(error, "DTAS\n");
    if (ibsta & DCAS) strcat(error, "DCAS\n");

    /*  Il tipo di errore viene individuato testando il valore della variabile
        globale iberr, della libreria GPIB. */

    if (iberr == EDVR) strcat(error, "EDVR <DOS Error>\n");
    if (iberr == ECIC) strcat(error, "ECIC <Not Controller-In-Charge>\n");
    if (iberr == ENOL) strcat(error, "ENOL <No Listener>\n");
    if (iberr == EADR) strcat(error, "EADR <Address error>\n");
    if (iberr == EARG) strcat(error, "EARG <Invalid argument>\n");
    if (iberr == ESAC) strcat(error, "ESAC <Not System Controller>\n");
    if (iberr == EABO) strcat(error, "EABO <Operation aborted>\n");
    if (iberr == ENEB) strcat(error, "ENEB <No GPIB board>\n");
    if (iberr == EOIP) strcat(error, "EOIP <Async I/O in progress>\n");
    if (iberr == ECAP) strcat(error, "ECAP <No capability>\n");
    if (iberr == EFSO) strcat(error, "EFSO <File system error>\n");
    if (iberr == EBUS) strcat(error, "EBUS <Command error>\n");
    if (iberr == ESTB) strcat(error, "ESTB <Status byte lost>\n");
    if (iberr == ESRQ) strcat(error, "ESRQ <SRQ stuck on>\n");
    if (iberr == ETAB) strcat(error, "ETAB <Table Overflow>\n");

    Form1->REdit->Text = Form1->REdit->Text + error +"\n" ;

    // Vengono rilasciati il dispositivo e la scheda.
    ibonl (Device,0);
    ibonl (BoardIndex,0);
    // Viene terminato il programma.
    // exit(1);
}


/*******************************************************************************
 La funzione Init stabilisce la connessione al dispositivo e acquisisce un
 descrittore di dispositivo da usare nelle successive chiamate alle funzioni di
 libreria
*******************************************************************************/
int __fastcall Interfaccia488 :: Init(int PrimaryAddr, int SecondaryAddr, int BoardIndex)
{
    int Device;
    // ibdev stabilisce la connessione e determina un descrittore di dispositivo
    Device = ibdev(BoardIndex, PrimaryAddr, SecondaryAddr,TIMEOUT,EOT,EOS);
    if (ibsta & ERR)
    {
        GpibError("ibdev Error",Device,BoardIndex);
    }
    /*  spedisce al dispositivo il comando Selected Device Clear che lo
        inizializza al suo stato standard */
    ibclr(Device);
    if (ibsta & ERR)
    {
       GpibError("ibclr Error",Device,BoardIndex);
    }
    // La funzione restituisce il descrittore per le successive operazioni.
    return (Device);
}

/*******************************************************************************
 ReadStatus richiede le informazioni di identificazione del dispositivo usando
 il comando specifico "*IDN?" e le funzioni di libreria NI-488 'ibwrt' e 'ibrd'
 invocate indirettamente mediante le funzioni EseguiCMD ed EseguiRead. Una volta
 che lo strumento è stato identificato lo si porta nelle condizioni predefinite
 dal costruttore.
*******************************************************************************/
void __fastcall Interfaccia488 :: ReadStatus(char *Buffer, int Device, int BoardIndex)
{
   EseguiCMD(Device,"*IDN?");     	// Invia il comando di identificazione
   EseguiRead(Device, Buffer);          // Legge la stringa di identificazione
   EseguiCMD(Device,"*RST");            // Effettua il RESET del dispositivo
}

/*******************************************************************************
 EseguiCMD esegue sul dispositivo una stringa di comando,il nome del dispositivo
 e la stringa sono passati alla funzione come parametro. Prima di eseguire il
 comando il dispositivo viene "pulito" attraverso il comando "*CLS" da eventuali
 condizioni di errore occorsi precedentemente. Il comando eseguito viene anche
 riportato sul Form principale nel RichTextBox chiamato "console 488". Eventuali
 messaggi di errore vengono allo stesso modo segnalati e riportati.
*******************************************************************************/
void __fastcall Interfaccia488 :: EseguiCMD(int Device, char S[])
{
  // "*CLS" pulisce il dispositivo da eventuali condizioni di errere precedenti
  ibwrt(Device,"*CLS",strlen("*CLS"));
  // Esegue la stringa di comando
  ibwrt(Device,S,strlen(S));
  // Aggiorna il campo RichTextBox sulla console 488 del form principale
  Form1->REdit->Text = Form1->REdit->Text+IntToStr(Device)+": "+S+"\n";
  // Effettua il controllo sulle cause degli eventuali errori
  if (ibsta & ERR) {
    Scheda488->GpibError("ibclr Error",Device,BoardIndex);
  }
};

/*******************************************************************************
 EseguiRead legge dal dispositivo scelto una stringa, la memorizza in un buffer,
 e la riporta sul form principale nell'apposito RichTextBox "console 488"
*******************************************************************************/
void __fastcall Interfaccia488 :: EseguiRead(int Device, char Buffer[])
{
  strcpy(Buffer,"");
  // ibrd legge una stringa dal dispositivo
  ibrd(Device, Buffer, 100);
  Buffer[ibcntl] = '\0';
  // aggiorna la console 488 sul form principale
  Form1->REdit->Text = Form1->REdit->Text+IntToStr(Device)+":<-- "+ Buffer;
  // effetua il controllo errori sulla scheda 488
  if (ibsta & ERR) {
    Scheda488->GpibError("ibclr Error",Device,BoardIndex);
  }
};

/*******************************************************************************
  Da questo punto in poi vengono riportati i metodi relativi alla classe
  Oscilloscopio che servono per operare sullo strumento rilevato
*******************************************************************************/

/*******************************************************************************
  CLASSE  : TOscilloscopio, METODO: Reset()
  FUNZIONE: Esegue il reset del dispositivo portandolo nelle condizioni
            predefinite imposte dal costruttore.
*******************************************************************************/
void __fastcall TOscilloscopio :: Reset()
{
  Scheda488->EseguiCMD(DevOscil,"*RST");
}

/*******************************************************************************
  CLASSE  : TOscilloscopio, METODO: AutoSet()
  FUNZIONE: Esegue l'autoset del dispositivo portando le scale orizzontali e
            verticali in condizioni tali da visualizzare l'intero segnale
*******************************************************************************/
void __fastcall TOscilloscopio :: AutoSet()
{
 Scheda488->EseguiCMD(DevOscil,"AUTOSET EXECUTE");
}

/*******************************************************************************
  CLASSE  : TOscilloscopio, METODO: Lock()
  FUNZIONE: Disabilita il pannello frontale dello strumento
*******************************************************************************/
void __fastcall TOscilloscopio :: Lock()
{
 Scheda488->EseguiCMD(DevOscil,"LOCK ALL");
}

/*******************************************************************************
  CLASSE  : TOscilloscopio, METODO: UnLock()
  FUNZIONE: Abilita il pannello frontale dello strumento
*******************************************************************************/
void __fastcall TOscilloscopio :: UnLock()
{
 Scheda488->EseguiCMD(DevOscil,"UNLOCK ALL");
}

/*******************************************************************************
  CLASSE   : TOscilloscopio, METODO: CH(ch,state)
  FUNZIONE : Accende/Spegne il canale selezionato
  PARAMETRI: ch    - identifica il canale. 1 per CH1, 2 per CH2.
             state - true per canale ON, false per canale OFF.
*******************************************************************************/
void __fastcall TOscilloscopio :: CH(int ch, bool state)
{
  char S[100];
  strcpy(S,"SELECT:");
  // si identifica il canale
  if (ch==1)
     strcat(S,"CH1 ");
  else
     strcat(S,"CH2 ");
  // si decide se accendere o spegnere il canale prescelto
  if (state)
     strcat(S,"ON");
  else
     strcat(S,"OFF");
  // si esegue la stringa di comando realizzata.
  Scheda488->EseguiCMD(DevOscil,S);
}

/*******************************************************************************
  CLASSE   : TOscilloscopio, METODO: CHscalaH(div)
  FUNZIONE : Cambia la scala orizzontale sull'Oscilloscopio.
  PARAMETRI: div - numero di punti per divisione.
*******************************************************************************/
void __fastcall TOscilloscopio :: CHscalaH(int div)
{
   AnsiString S;
   double val;
   char P[100] = "";
   // si prepara il comando per l'impostazione della scala orizzontale
   strcpy(P,"HOR:SCA ");
   // si definisce la scala
   val = ((double)div)/((double)1000)+0.0001;
   S.sprintf("%lf",val);
   strcat(P,S.c_str());
   // si esegue la stringa di comando realizzata
   Scheda488->EseguiCMD(DevOscil,P);
};

/*******************************************************************************
  CLASSE   : TOscilloscopio, METODO: PosizV(ch,pos)
  FUNZIONE : Sposta verticalmente il segnale sull'Oscilloscopio sul canale scelto.
  PARAMETRI: ch  - identifica il canale. 1 per CH1, 2 per CH2.
             pos - numero di punti dello spostamento.
*******************************************************************************/
void __fastcall TOscilloscopio :: PosizV(int CH,int pos)
{
  double val;
  AnsiString P;
  char S[100];
  // si sceglie il canale su cui effettuare lo shift verticale
  if (CH==1)
    strcpy(S,"CH1:POS ");
  else
    strcpy(S,"CH2:POS ");
  // si definisce l'entità dello spostamento
  val = ((double)pos)/((double)100);
  P.sprintf("%lf",val);
  strcat(S,P.c_str());
  // si esegue la stringa di comando realizzata
  Scheda488->EseguiCMD(DevOscil,S);
}

/*******************************************************************************
  CLASSE   : TOscilloscopio, METODO: ScalaV(ch,pos)
  FUNZIONE : Cambia la scala verticale dell'Oscilloscopio sul canale scelto.
  PARAMETRI: ch  - identifica il canale. 1 per CH1, 2 per CH2.
             pos - identifica una delle possibili scale
*******************************************************************************/
void __fastcall TOscilloscopio :: ScalaV(int CH,int pos)
{
   // Si memorizzano i possibili valori di scala verticale in un array in modo
   // tale che il parametro pos, passato alla funzione, faccia da indice tra i
   // valori di scala leciti.
   char (ValScala[11])[8] =
          {"2e-3","5e-3","10e-3","20e-3","50e-3",
            "100e-3","200e-3","500e-3","1e0","2e0","5e0"};

   AnsiString App;
   char S[100] = "";
   char P[100] = "";
   strcpy(S,ValScala[pos]);
   // si sceglie in canale relativo all'operazione di scaling e si aggiorna sul
   // form3 (quello dell'Oscilloscopio) l'editBox relativo alla scala verticale.
   if (CH==1) {
     strcat(P,"CH1:SCALE ");
     Form3->Edit2->Text = S;
   }else if (CH==2) {
     strcat(P,"CH2:SCALE ");
     Form3->Edit3->Text = S;
   };
   strcat(P,S);
   // si esegue la stringa di comando realizzata
   Scheda488->EseguiCMD(DevOscil,P);
};

/*******************************************************************************
  CLASSE   : TOscilloscopio, METODO: Run()
  FUNZIONE : L'immagine sull'Oscilloscopio è in movimento.
*******************************************************************************/
void __fastcall TOscilloscopio :: Run()
{
  Scheda488->EseguiCMD(DevOscil,"ACQUIRE:STATE ON");
}

/*******************************************************************************
  CLASSE   : TOscilloscopio, METODO: Stop()
  FUNZIONE : L'immagine sull'Oscilloscopio viene bloccata(istantanea del segnale).
*******************************************************************************/
void __fastcall TOscilloscopio :: Stop()
{
  Scheda488->EseguiCMD(DevOscil,"ACQUIRE:STATE OFF");
}


/*******************************************************************************
  CLASSE   : TOscilloscopio, METODO: AttenuaSonda(tipo)
  FUNZIONE : Definisce il tipo di attenuazione relativa al canale (tale funzione
             è stata implementata solo per il canale 1).
  PARAMERTI:  tipo - Identifica i possibili valori di attenuazione, i valori
              possibili sono: 1 -> 1x, 10 -> 10x, 100 -> 100x, 1000 -> 1000x.
*******************************************************************************/
void __fastcall TOscilloscopio :: AttenuazSonda(int tipo)
{
 switch (tipo)
 {
   case 1    : Scheda488->EseguiCMD(DevOscil,"CH1:PROBE 1");break;
   case 10   : Scheda488->EseguiCMD(DevOscil,"CH1:PROBE 10");break;
   case 100  : Scheda488->EseguiCMD(DevOscil,"CH1:PROBE 100");break;
   case 1000 : Scheda488->EseguiCMD(DevOscil,"CH1:PROBE 1000");break;
 }
}


/*******************************************************************************
  CLASSE   : TOscilloscopio, METODO: Contrasto(val)
  FUNZIONE : Definisce il contrasto sul display dell'Oscilloscopio
  PARAMERTI: val - Identifica i possibili valori di contrasto, i valori leciti
             sono da 1 (min) ... 100 (max).
*******************************************************************************/
void __fastcall TOscilloscopio :: Contrasto(int val)
{
  AnsiString P;
  char S[100];
  // sei prepara il comando
  strcpy(S,"DIS:CONTR ");
  P.sprintf("%d",val);
  strcat(S,P.c_str());
  // si esegue la stringa di comando appositamente costruita
  Scheda488->EseguiCMD(DevOscil,S);
}

/*******************************************************************************
  CLASSE   : TOscilloscopio, METODO: Acquisisci()
  FUNZIONE : Effettua l'aquisizione del segnale riportato su uno dei due canali
             dell'Oscilloscopio.
*******************************************************************************/
void __fastcall TOscilloscopio :: Acquisisci()
{
    char S[30];         // Stringa di appoggio per la manipolazione dei comandi
    char Buffer[101];   // Buffer per la ricezione dei dati
    FILE *File1;        // Descrittore di File per la creazione di 'unita.dat'
    int stop;

    // Si disabilita il pulsante per evitare invocazioni plurime
    Form4->AcqButton->Enabled = false;
    // Si decide il numero max di campioni da acquisire dall'Oscilloscopio
    // tale numero è letto sul form Acquisizione nel realtivo editBox
    stop = StrToInt(Form4->EditNumCamp->Text.c_str());

    // Il numero di campioni deve essere almeno 2 e massimo 2500
    if (stop > 2500) stop = 2500;
    if (stop < 2)    stop = 2 ;

    // Una volta controllato il valore di stop viene anche aggiornato il
    // relativo editBox sul form Acquisizione.
    Form4->EditNumCamp->Text = IntToStr(stop) ;

    // Viene avviata la sequenza di acquisizione
    Scheda488->EseguiCMD(DevOscil,"ACQ:STATE RUN");
    Form4->StatusBar1->SimpleText = "Acquisizione dei dati in corso";

    // Si seleziona il canale da cui acquisire, specificato dal groupBox canali
    if(Form4->Canali->ItemIndex == 0)
    {
        Scheda488->EseguiCMD(DevOscil,"SEL:CH1 ON");
        Scheda488->EseguiCMD(DevOscil,"DAT:SOU CH1");
    }
    else if(Form4->Canali->ItemIndex == 1)
    {
        Scheda488->EseguiCMD(DevOscil,"SEL:CH2 ON");
        Scheda488->EseguiCMD(DevOscil,"DAT:SOU CH2");

    }
    // Specifica il formato dei dati da trasferire alla scheda.
    // Il formato scelto e' quello ASCII
    Scheda488->EseguiCMD(DevOscil,"DAT:ENC ASCI");
    // Stabilisce il numero di byte per codificare ogni punto del segnale
    Scheda488->EseguiCMD(DevOscil,"DAT:WID 1");
    // Stabilisce come punto iniziale quello visualizzato sullo schermo
    // dell'oscilloscopio
    Scheda488->EseguiCMD(DevOscil,"DAT:STAR 1");
    // Stabilisce come punto finale quello visualizzato sul form Oscilloscopio
    strcpy(S,"DAT:STOP ");
    strcat(S, IntToStr(stop).c_str());
    Scheda488->EseguiCMD(DevOscil,S);

    // Scrittura nel file 'unita.dat' dei dati relativi al campionamento
    File1 = fopen(".\\unita.dat","w");
    // Si interroga lo strumento sull'intervallo di Campionamento
    Scheda488->EseguiCMD(DevOscil,"WFMP:XIN?");
    Scheda488->EseguiRead(DevOscil,Buffer);
    fprintf(File1,"%s",Buffer);
    // Richiesta della risoluzione verticale
    Scheda488->EseguiCMD(DevOscil, "WFMP:YMU?");
    Scheda488->EseguiRead(DevOscil,Buffer);
    fprintf(File1,"%s",Buffer);
    // Richiesta dell'unita' di misura verticale
    Scheda488->EseguiCMD(DevOscil,"WFMP:YUN?");
    Scheda488->EseguiRead(DevOscil,Buffer);
    fprintf(File1,"%s",Buffer);
    // Si chiude il file 'unita.dat'
    fclose(File1);

    // Richiede il trasferimento dei campioni della forma d'onda
    // dall'oscilloscopio alla scheda
    Scheda488->EseguiCMD(DevOscil,"CURV?");

    // I dati ricevuti dalla scheda vengono memorizzati nel file 'data.dat'
    ibrdf(DevOscil,".\\data.dat");
    if (ibsta & ERR)
       Scheda488->GpibError("ibrdf Error", DevOscil, BoardIndex);
    // Termina l'acquisizione da parte dell'oscilloscopio, e' equivalente al
    // pulsante RUN/STOP dello strumento
    Scheda488->EseguiCMD(DevOscil,"ACQ:STATE STOP");
    Form4->StatusBar1->SimpleText = "Acquisizione dei dati completata";
    // Riabilitazioni del bottone acquisizione
    Form4->AcqButton->Enabled = true;
};

/*******************************************************************************
  Da questo punto in poi vengono riportati i metodi relativi alla classe
  TGeneratore che servono per operare sullo strumento rilevato
*******************************************************************************/

/*******************************************************************************
  CLASSE  : TGeneratore, METODO: Reset()
  FUNZIONE: Esegue il reset del dispositivo portandolo nelle condizioni
            predefinite imposte dal costruttore.
*******************************************************************************/
void __fastcall TGeneratore :: Reset()
{
  Scheda488->EseguiCMD(DevGwave,"*RST");
}

/*******************************************************************************
  CLASSE  : TGeneratore, METODO: ApplicaParametri()
  FUNZIONE: Applica i parametri quali: Frequenza, Tensione, Offset alla forma
            d'onda visulaizzata. Effettua inoltre un controllo sugli stessi
            affinché soddisfino i vincoli imposti dal costruttore.
*******************************************************************************/
void __fastcall TGeneratore :: ApplicaParametri()
{
 char S[100];             // Stringa di appoggio per la creazione dei comandi
 double Vmax = 5;         // Tensione max di riferimento
 double Vpp;              // Tensione picco picco
 double Voff;             // Tensione di offset
 double Freq;             // Frequenza del segnale
 bool DatiValidi = true;  // Variabile per il controllo della validità dei dati.

 // Prendiamo dal form relativo al Generatore i parametri da applicare al segnale
 strcpy(S,Form2->Edit_Fre->Text.c_str()) ;
 sscanf(S,"%lf",&Freq);
 strcpy(S,Form2->Edit_Amp->Text.c_str());
 sscanf(S,"%lf",&Vpp);
 strcpy(S,Form2->Edit_Off->Text.c_str());
 sscanf(S,"%lf",&Voff);

 // Controllo dei vincoli sui parametri inseriti
 DatiValidi = (Freq<=15000000)&&(Freq>=0.0001);        // condizioni sulla frequenza
 DatiValidi = DatiValidi && (Vpp<=10) && (Vpp>=0.1);   // Condizione su Vpp
 DatiValidi = DatiValidi && ((Voff)<=(2.0*Vpp));       // condizioni sull'offset
 DatiValidi = DatiValidi && ((-Voff)<=(2.0*Vpp));      // condizioni sull'offset
 DatiValidi = DatiValidi && (((Voff)+Vpp/2.0)<=Vmax);  // Condizioni su Vpp+Voff
 DatiValidi = DatiValidi && (((-Voff)+Vpp/2.0)<=Vmax); // Condizioni su Vpp+Voff

 // Impostazione dei parametri scelti
 if (DatiValidi)
 {
   strcpy(S,"VOLT:OFFS 0");               // Si annullano eventuali offset precedenti
   Scheda488->EseguiCMD(DevGwave,S);
   strcpy(S,"FREQ ");                     // Si impostano i valori sulla frequenza
   strcat(S,Form2->Edit_Fre->Text.c_str());
   Scheda488->EseguiCMD(DevGwave,S);
   strcpy(S,"VOLT:OFFS ");                // Si impostano i valori sull'offset
   strcat(S,Form2->Edit_Off->Text.c_str());
   Scheda488->EseguiCMD(DevGwave,S);
   strcpy(S,"VOLT ");                     // Si impostano i valori di tensione
   strcat(S,Form2->Edit_Amp->Text.c_str());
   Scheda488->EseguiCMD(DevGwave,S);
 }
 else {
   // Se i parametri non rispettano i vincoli ne viene avvistao l'utente
   MessageBox(NULL,"1. 0.1 <=Vpp1 <= 10  Volts\n2. |Voff| <= 2*Vpp  Volts\n3. |Voff|+Vpp/2 <= Vmax  Volts\n4. 0.0001 <= Freq <= 15000000  Hz\n",
                    "Vincoli sui parametri...",MB_ICONEXCLAMATION);
 }
}

/*******************************************************************************
  CLASSE   : TGeneratore, METODO: StopModulazione(tipo)
  FUNZIONE : Ferma un'eventuale modulazione in esecuzione.
  PARAMETRI: tipo - 1 termina una modulazione AM - 2 termina una modulazione FM
                    3 termina una modulazione FSK - 4 termina una modulazione BM
                    5 termina una modulazione FS
*******************************************************************************/
void __fastcall TGeneratore :: StopModulazione(int tipo){
  switch (tipo)
  {
    case 1: Scheda488->EseguiCMD(DevGwave,"AM:STAT OFF");  break;
    case 2: Scheda488->EseguiCMD(DevGwave,"FM:STAT OFF");  break;
    case 3: Scheda488->EseguiCMD(DevGwave,"FSK:STAT OFF"); break;
    case 4: Scheda488->EseguiCMD(DevGwave,"BM:STAT OFF");  break;
    case 5: Scheda488->EseguiCMD(DevGwave,"FS:STAT OFF");  break;
  }
}

/*******************************************************************************
  CLASSE   : TGeneratore, METODO: Shape(tipo)
  FUNZIONE : Imposta il tipo di segnale da generare sullo strumento.
  PARAMETRI: tipo - è una stringa che può essere uno dei seguenti valori:
             "SIN"    - genera una sinusoide;
             "SQUARE" - genera un'onda quadra;
             "TRI"    - genera un'onda triangolare;
             "RAMP"   - genera un'onda a dente di sega.
*******************************************************************************/
void __fastcall TGeneratore :: Shape(char Tipo[])
{
 char S[100];
 // Si prepara la stringa relativa al comando sulla scelta della forma d'onda.
 strcpy(S,"");
 strcat(S,"FUNC:SHAP ");
 strcat(S,Tipo);
 // Si esegue il comando appositamente costruito
 Scheda488->EseguiCMD(DevGwave,S);
}

/*******************************************************************************
  CLASSE   : TGeneratore, METODO: FreqSweep()
  FUNZIONE : Imposta lo sweep di frequenza sull'onda generata dallo strumento.
             I parametri della modulazione sono presi direttamente dal relativo
             form Form_FS; tali parametri sono: FrequenzaStart, FrequenzaStop,
             e tempoSweep.
*******************************************************************************/
void __fastcall TGeneratore :: FreqSweep()
{
  char S[100];
  FormFS->Visible = true;
  // Frequenza di start (per default è 50 Hz)
  strcpy(S,"FREQ:STAR ");
  strcat(S,FormFS->StartFS->Text.c_str());
  Scheda488->EseguiCMD(DevGwave,S);
  // Frequenza di stop (per default è 5 KHz)
  strcpy(S,"FREQ:STOP ");
  strcat(S,FormFS->StopFS->Text.c_str());
  Scheda488->EseguiCMD(DevGwave,S);
  // Tempo di Sweep  (per default è 10 secondi)
  strcpy(S,"SWE:TIME ");
  strcat(S,FormFS->TimeFS->Text.c_str());
  Scheda488->EseguiCMD(DevGwave,S);
  // Termina lo Sweep di frequenza
  Scheda488->EseguiCMD(DevGwave,"SWE:STAT ON");

}

/*******************************************************************************
  CLASSE   : TGeneratore, METODO: BurstMod()
  FUNZIONE : Imposta la modulazione a Burst sull'onda generata dallo strumento.
             I parametri della modulazione sono presi direttamente dal relativo
             form Form_BM; tali parametri sono: FrequenzaBurst, numBurst.
*******************************************************************************/
void __fastcall TGeneratore :: BurstMod()
{
  char S[100];
  FormBM->Visible = true;
  // Si imposta il numero di Burst (per default 3 cicli)
  strcpy(S,"BM:NCYC ");
  strcat(S,FormBM->CicliBM->Text.c_str());
  Scheda488->EseguiCMD(DevGwave,S);
  // Frequenza di burst (per default 200Hz)
  strcpy(S,"BM:INT:RATE ");
  strcat(S,FormBM->RateBM->Text.c_str());
  Scheda488->EseguiCMD(DevGwave,S);
  // Avvia la modulazione a Burst
  Scheda488->EseguiCMD(DevGwave,"BM:STAT ON");
}

/*******************************************************************************
  CLASSE   : TGeneratore, METODO: FSK()
  FUNZIONE : Imposta la modulazione FSK sull'onda generata dallo strumento.
             I parametri della modulazione sono presi direttamente dal relativo
             form Form_FSK; tali parametri sono: FrequenzaFSK e ShiftRate.
*******************************************************************************/
void __fastcall TGeneratore :: FSK()
{
  char S[100];
  FormFSK->Visible = true;
  // Salto di frequenza (per default 500 Hz)
  strcpy(S,"FSK:FREQ ");
  strcat(S,FormFSK->FreqFSK->Text.c_str());
  Scheda488->EseguiCMD(DevGwave,S);
  // Shift rate (per default 100 Hz)
  strcpy(S,"FSK:INT:RATE ");
  strcat(S,FormFSK->RateFSK->Text.c_str());
  Scheda488->EseguiCMD(DevGwave,S);
  // Si avvia la modulazione FSK
  Scheda488->EseguiCMD(DevGwave,"FSK:STAT ON");
}

/*******************************************************************************
  CLASSE   : TGeneratore, METODO: AmplMod()
  FUNZIONE : Imposta la modulazione di Ampiezza sull'onda generata dallo
             strumento. I parametri della modulazione sono presi direttamente
             dal relativo form Form_AM e sono: FrequenzaAM e ProfonditaAM.
*******************************************************************************/
void __fastcall TGeneratore :: AmplMod()
{
  int Prof;                 // Variabile usata per la profondità di modulazione
  double Freq;              // variabile usata per la frequenza
  char S[100];              // stringa di appoggio per i comandi
  bool DatiValidi = true;   // variabile usata per il controllo sui dati.

  // Viene acquisito il valore della frequenza
  strcpy(S,FormAM->FreqMod->Text.c_str()) ;
  sscanf(S,"%lf",&Freq);

  // Viene acquisito il valore della profondità di modulazione
  strcpy(S,FormAM->ProfMod->Text.c_str()) ;
  sscanf(S,"%d",&Prof);

  FormAM->Visible = true;
  // Si effettua il controllo sui dati inseriti sul formAM
  DatiValidi = DatiValidi && (Freq>=0.01) && (Freq<=20000);
  DatiValidi = DatiValidi && (Prof>=1) && (Prof<=100);

  if (DatiValidi)
  {
    // Scelta dell'onda modulante
    strcpy(S,"AM:INT:FUNC ");
    switch (FormAM->SceltaMod->ItemIndex) {
      case 0: strcat(S,"SIN")   ;break;
      case 1: strcat(S,"SQUARE");break;
      case 2: strcat(S,"TRI")   ;break;
      case 3: strcat(S,"RAMP") ;break;
    }
    Scheda488->EseguiCMD(DevGwave,S);
    // Modulazione di frequenza (per default è 200Hz)
    strcpy(S,"AM:INT:FREQ ");
    strcat(S,FormAM->FreqMod->Text.c_str());
    Scheda488->EseguiCMD(DevGwave,S);
    // Profondità di modulazione (per default 80%)
    strcpy(S,"AM:DEPT ");
    strcat(S,FormAM->ProfMod->Text.c_str());
    Scheda488->EseguiCMD(DevGwave,S);
    // Inizio della modulazione AM
    Scheda488->EseguiCMD(DevGwave,"AM:STAT ON");
  }
  else
   // Il seguente messageBox informa sui vincoli sui parametri.
   MessageBox(NULL,"1. Freq: 0.01 ... 20000 Hz\n2. Prof: 0..100\n",
                    "Vincoli sui parametri...",MB_ICONEXCLAMATION);
}

/*******************************************************************************
  CLASSE   : TGeneratore, METODO: FreqMod()
  FUNZIONE : Imposta la modulazione di Ampiezza sull'onda generata dallo
             strumento. I parametri della modulazione sono presi direttamente
             dal relativo form Form_FM e sono: FrequenzaFM e DeviazioneFM.
*******************************************************************************/
void __fastcall TGeneratore :: FreqMod()
{
  char S[100];
  FormFM->Visible = true;
  // Scelta della forma d'onda della modulante
  strcpy(S,"FM:INT:FUNC ");
  switch (FormFM->SceltaMod->ItemIndex) {
    case 0: strcat(S,"SIN")   ;break;
    case 1: strcat(S,"SQUARE");break;
    case 2: strcat(S,"TRI")   ;break;
    case 3: strcat(S,"RAMP")  ;break;
  }
  Scheda488->EseguiCMD(DevGwave,S);
  // Modulazione di frequenza (per default è 200Hz)
  strcpy(S,"FM:INT:FREQ ");
  strcat(S,FormFM->FreqMod->Text.c_str());
  Scheda488->EseguiCMD(DevGwave,S);
  // Picco di frequenza (per default è 3kHz)
  strcpy(S,"FM:DEV ");
  strcat(S,FormFM->DevMod->Text.c_str());
  Scheda488->EseguiCMD(DevGwave,S);
  // Inizio della modulazione FM
  Scheda488->EseguiCMD(DevGwave,"FM:STAT ON");
}

/*******************************************************************************
  Da questo punto in poi c'è il codice relativo alle funzioni associate agli
  eventi sul form principale.
*******************************************************************************/

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)  : TForm(Owner) { }
//---------------------------------------------------------------------------

/*******************************************************************************
  Segmento di codice relativo all'evento Click sul tasto RilevaStrumenti. Per
  prima cosa vengono aggiornati i campi relativi al: BoardIndex, PrimaryAddress,
  SecondaryAddress con i rispettivi valori; poi i dispositivi connessi vengono
  interrogati al fine di essere identificati dalla scheda (si aggiornano i
  relativi campi sull'interfaccia) e se l'identificazione è andata a buon fine
  è possibile aprire i relativi form, mediante Click sugli appositi bottoni.
  Tutte le operazioni effettuate mediante la scheda 488 sono riportate nella
  apposito componente RichTextBox "console 488"
*******************************************************************************/
void __fastcall TForm1::BottoneRilevaClick(TObject *Sender)
{

  AnsiString S;
  char Buffer[100];

  // Viene rilevato il I strumento (Generatore di segnale)
  Strum1Presente = false;
  // Si inizializza il dispositivo
  DevGwave = Scheda488->Init(PrimaryGwave, SecondaryGwave, BoardIndex);
  S.sprintf("%d",DevGwave);
  Edit1->Text = S;                             // Si scrive l'identificativo
  Edit5->Text = IntToStr(BoardIndex);          // Si aggiornano i campi relativi
  Edit6->Text = IntToStr(PrimaryGwave);        //  al numero della scheda e agli
  Edit7->Text = IntToStr(SecondaryGwave);      //  indirizzi dei dispositivi.
  // Si legge lo stato del dispositivo
  Scheda488->ReadStatus(Buffer,DevGwave,BoardIndex);
  // Si abilita il tasto per aprire il form relativo al dispositivo
  SpeedButton1->Enabled = strcmp(Buffer,"");
  Strum1Presente = strcmp(Buffer,"");
  Edit2->Text = S.sprintf("%s",Buffer);

  // Viene rileavato il II strumento (Oscilloscopio)
  Strum2Presente = false;
  // Si inizializza il dispositivo
  DevOscil = Scheda488->Init(PrimaryOscil, SecondaryOscil, BoardIndex);
  S.sprintf("%d",DevOscil);
  Edit3->Text = S;                             // Si scrive l'identificativo
  Edit8->Text  = IntToStr(BoardIndex);         // Si aggiornano i campi relativi
  Edit9->Text  = IntToStr(PrimaryOscil);       //  al numero della scheda e agli
  Edit10->Text = IntToStr(SecondaryOscil);     //  indirizzi dei dispositivi.
  Scheda488->ReadStatus(Buffer,DevOscil,BoardIndex);
  // Si abilita il tasto per aprire il form relativo al dispositivo
  SpeedButton2->Enabled = strcmp(Buffer,"");
  Strum2Presente = strcmp(Buffer,"");
  Edit4->Text = S.sprintf("%s",Buffer);
  // L'acquisizione è resa possibile solo se entrambe gli strumenti sono stati
  // rilevati in modo corretto.
  if (Strum1Presente && Strum2Presente) Acquisizione->Enabled = true;
}

/*******************************************************************************
  All'evento Click del pulsante è associata l'apertura del form relativo allo
  Oscilloscopio e nello stesso tempo al form viene dato il nome dello strumento.
*******************************************************************************/
//---------------------------------------------------------------------------
void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
  Form2->Caption = Edit2->Text;
  Form2->Visible = true;
}

/*******************************************************************************
  All'evento Click del pulsante è associata l'apertura del form relativo al
  Generatore e nello stesso tempo al form viene dato il nome dello strumento.
*******************************************************************************/
//---------------------------------------------------------------------------
void __fastcall TForm1::SpeedButton2Click(TObject *Sender)
{
  Form3->Caption = Edit4->Text;
  Form3->Visible = true;
}


/*******************************************************************************
  All'evento Click del pulsante è associata l'operazione di Clear sulla
  "Console 488" riportata sul form principale.
*******************************************************************************/
void __fastcall TForm1::BottoneClearClick(TObject *Sender)
{
  REdit->Clear();
}

/*******************************************************************************
  All'evento Click del pulsante è associata l'apertura del form relativo alla
  acquisizione dati (dall'Oscilloscopio). Tale bottone è reso disponibile solo
  se il rilevamento degli strumenti è andato a buon fine!
*******************************************************************************/
void __fastcall TForm1::AcquisizioneClick(TObject *Sender)
{
  Form4->Visible = true;
}

/*******************************************************************************
  All'evento Click del pulsante è associata l'apertura del form relativo allo
  AboutBox sugli autori del progetto. (Il form sarà modificato in seguito!!!)
*******************************************************************************/
void __fastcall TForm1::AboutClick(TObject *Sender)
{
  Form5->Visible = true;
}
