#include "TDS1002.h"

/* Definizione di alcune macro utili */
#define ENDOF(A) (A+strlen(A))
#define HEADER(A) if(TDS1002_headerOn)strcat(buffer,A)
#define DATAWORD(A,B) sprintf(buffer+strlen(buffer),A,B)

/********************************************************* 
 * Alcune variabili che permettono di tradurre in stringhe
 * gli enum presenti nell'header file
 *********************************************************/ 
char *OnOff[2]={"OFF","ON"};
char *Couplings[3]={"GND","AC","DC"};
char *Views[3]={"MAIN","WINDow","ZONE"};
char *Channels[5]={"CH1","CH2","MATH","REFA","REFB"};
char *Encodings[5]={"ASCIi","RIBinary","RPBinary","SRIbinary","SRPbinary"};
char *WFMEncoding[2]={"ASC","BIN"};
char *Units[4]={"s","Hz","Volts","U"};
char *Format[2]={"ENV","Y"};
char *Slopes[2]={"FALL","RISE"};

/**************************************************************************
 * Insieme delle variabili che descrivono lo stato dell'oscilloscopio 
 * Sono state usate variabili globali. Questo in parte semplifica il lavoro
 * perché sono visibili da tutte le funzioni, dall'altra non permette di 
 * avere più di un oscilloscopio aperto nello stesso momento.
 **************************************************************************/
serialport *TDS1002_device=0;
int TDS1002_headerOn=ON;
/* Horizontal settings */
int TDS1002_viewSel =MAIN;
int TDS1002_nSamples=TDS1002_NSAMPLES;
double TDS1002_secDiv =0.5;    
double TDS1002_secPos =0. ;
double TDS1002_delDiv =0.5;
double TDS1002_delPos =0. ;
/* Vertical settings */ 
int    TDS1002_chAttenuation[2]={1,1};
double TDS1002_chVDiv[2]={5.,5.};
double TDS1002_chVPos[2]={0.,0.};
int    TDS1002_chCoupling[2]={DC,DC};
int    TDS1002_chBandwidth[2]={OFF,OFF};
int    TDS1002_chInvert[2]={OFF,OFF};
/* Data infomation */
int    TDS1002_source=CH1;
int    TDS1002_destination=REFA;
int    TDS1002_datastart=1;
int    TDS1002_datastop =TDS1002_NSAMPLES;
int    TDS1002_datawidth=1;
int    TDS1002_datamin;
int    TDS1002_datamax;
int    TDS1002_encoding=ASCII;
int    TDS1002_wfmenc  =ASC;
int    TDS1002_sign    =ON;
int    TDS1002_endian  =ON;
int    TDS1002_format=Y;
int    TDS1002_xunit=SEC;
double TDS1002_xincr=0.;
double TDS1002_xzero=0.;
int    TDS1002_yunit=VOLT;
double TDS1002_ymult[2];
double TDS1002_yzero[2];
double TDS1002_yoff [2];
/* Trigger settings */
int    TDS1002_trgsource=CH1;
int    TDS1002_trgslope =RISE;
double TDS1002_trglevel=0.;
/* waveform function and data */
void (*TDS1002Wave)(double*,double,double,double,double*,double*);
int TDS1002_waveform[TDS1002_NSAMPLES];


/*
 * Fa partire l'oscilloscopio sulla porta seriale definita dal file
 * nella stringa portname.
 * Il programma entra in un ciclo infinito in cui sta in attesa di 
 * input dalla porta seriale.
 * Quando si osserva un input, questo viene scomposto in sottocomandi
 * separati da ';', ognuno con i suoi eventuali argomenti, usando la 
 * funzione RS232PTekExtractCommand. Ogni comando viene poi eseguito
 * sequenzialmente.
 */

int TDS1002Run(const char* portname) {
  char buffer[MAXBUFFERSIZE];
  char* loc, command[MAXBUFFERSIZE], argument[MAXBUFFERSIZE];
  int nread, length;

  TDS1002SetWFMParameters(TDS1002_source);
  if ( (TDS1002_device = OpenSerial(portname)) ) {
    while (1) {
      /* Legge una linea di comando dalla porta seriale */
      nread = ReadSerial(TDS1002_device,buffer);
      /* 
	 Se la linea e' non vuota, allora riconosce i singoli sottocomandi,
         separati dal ';' e li sottomette ad uno ad uno al parser dei
         comandi.
       */
      if ( nread<=0 ) continue;
      command[0]='\0';
      for (loc=buffer; loc<buffer+nread; loc+=length) {
	length=RS232PTekExtractCommand(loc,command,argument);
        TDS1002ExecuteCommand(command,argument);
      }      
    }
    CloseSerial(TDS1002_device);
    return 0;
  }
  return -1;
}


/*
 * Questa funzione esegue un singolo comando.
 * Per prima cosa si stabilisce se il comando richiede un output
 * (ovvero ha un '?' alla fine del  comando) usando RS232PIsQuery.
 * Poi la stringa di comando viene confrontata con tutti i comandi
 * possibili, attraverso la funzione RS232PMatchCommand.
 * Quando il comando viene riconosciuto, vengono eseguite le istruzioni
 * relative.
 * L'uso di RS232PMatchCommand permette di avere un trattamento più 
 * robusto dell'input (ad esempio il programma diventa case-insensitive.
 * Le macro HEADER e DATAWORD permettono di formattare l'output in maniera
 * diversa a seconda che l'opzione header sia on o off. Inoltre aiutano
 * ad appendere dati alla stringa di output, sena continuare a scrivere
 * esplicitamente tutte le istruzioni.
 */
void TDS1002ExecuteCommand(const char* command, const char* argument) {
  char buffer[MAXBUFFERSIZE], buffer2[MAXBUFFERSIZE];
  int  itmp;
  int IsQuery;
  double tmp;

  IsQuery = RS232PIsQuery(command);
  buffer[0]='\0';

  if      ( RS232PMatchCommand(command,"CH1") ) {
    if ( IsQuery ) {
      HEADER(":CH1:PROBE ");
      sprintf(ENDOF(buffer),"%d;",TDS1002_chAttenuation[CH1]);
      HEADER("SCALE ");
      sprintf(ENDOF(buffer),"%lf;",TDS1002_chVDiv[CH1]);
      HEADER("POSITION ");
      sprintf(ENDOF(buffer),"%lf;",TDS1002_chVPos[CH1]);
      HEADER("COUPLING ");
      sprintf(ENDOF(buffer),"%s;",Couplings[TDS1002_chCoupling[CH1]]);
      HEADER("BANDWIDTH ");
      sprintf(ENDOF(buffer),"%s;",OnOff[TDS1002_chBandwidth[CH1]]);
      HEADER("INVERT ");
      sprintf(ENDOF(buffer),"%s;",OnOff[TDS1002_chInvert[CH1]]);
    }
    else printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"CH1:SCALe") ||
	    RS232PMatchCommand(command,"CH1:VOLTs") ) {
    if ( IsQuery ) {
      HEADER(":CH1:SCALE ");
      sprintf(ENDOF(buffer),"%lf",TDS1002_chVDiv[CH1]);
    } else {
      if ( sscanf(argument,"%lf",&tmp) ) {
	TDS1002_chVDiv[CH1]=TDS1002VDiv(tmp);
        TDS1002SetWFMParameters(CH1);
      }
      else printf("%s\n",INVALID_ARGUMENT);
    }
  } 
  else if ( RS232PMatchCommand(command,"CH1:POSition") ) {
    if ( IsQuery ) {
      HEADER(":CH1:POSITION ");
      sprintf(ENDOF(buffer),"%lf;",TDS1002_chVPos[CH1]);
    } else {
      if ( sscanf(argument,"%lf",&tmp) ) {
	TDS1002_chVPos[CH1]=TDS1002VPos(tmp,CH1);
        TDS1002SetWFMParameters(CH1);
      }  else 
	printf("%s\n",INVALID_ARGUMENT);
    }
  } 
  else if ( RS232PMatchCommand(command,"CH2") ) {
    if ( IsQuery ) {
      HEADER(":CH2:PROBE ");
      sprintf(ENDOF(buffer),"%d;",TDS1002_chAttenuation[CH2]);
      HEADER("SCALE ");
      sprintf(ENDOF(buffer),"%lf;",TDS1002_chVDiv[CH2]);
      HEADER("POSITION ");
      sprintf(ENDOF(buffer),"%lf;",TDS1002_chVPos[CH2]);
      HEADER("COUPLING ");
      sprintf(ENDOF(buffer),"%s;",Couplings[TDS1002_chCoupling[CH2]]);
      HEADER("BANDWIDTH ");
      sprintf(ENDOF(buffer),"%s;",OnOff[TDS1002_chBandwidth[CH2]]);
      HEADER("INVERT ");
      sprintf(ENDOF(buffer),"%s;",OnOff[TDS1002_chInvert[CH2]]);
    }
    else printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"CH2:SCALe") ||
	    RS232PMatchCommand(command,"CH2:VOLTs") ) {
    if ( IsQuery ) {
      HEADER(":CH2:SCALE ");
      sprintf(ENDOF(buffer),"%lf",TDS1002_chVDiv[CH2]);
    } else {
      if ( sscanf(argument,"%lf",&tmp) ) {
	TDS1002_chVDiv[CH2]=TDS1002VDiv(tmp);
        TDS1002SetWFMParameters(CH2);
      }
      else printf("%s\n",INVALID_ARGUMENT);
    }
  } 
  else if ( RS232PMatchCommand(command,"CH2:POSition") ) {
    if ( IsQuery ) {
      HEADER(":CH2:POSITION ");
      sprintf(ENDOF(buffer),"%lf;",TDS1002_chVPos[CH2]);
    } else {
      if ( sscanf(argument,"%lf",&tmp) ) { 
	TDS1002_chVPos[CH2]=TDS1002VPos(tmp,CH2);
        TDS1002SetWFMParameters(CH2);
      } else 
	printf("%s\n",INVALID_ARGUMENT);
    }
  } 
  else if ( RS232PMatchCommand(command,"CURVe" ) ) {
    if ( IsQuery ) {
      HEADER(":CURVE ");
      if ( TDS1002Acquire(100*TDS1002_secDiv) ) {
        for (itmp=TDS1002_datastart;itmp<TDS1002_datastop;itmp++)  
	  DATAWORD("%d,",TDS1002_waveform[itmp-1]);
	DATAWORD("%d;",TDS1002_waveform[TDS1002_datastop-1]);
      } 
      else printf("%s\n",NO_TRIGGER);
    } 
    else printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"DATa") ) {
    if ( IsQuery ) {
      HEADER(":DATA:ENCDG ");
      DATAWORD("%s;",Encodings[TDS1002_encoding]);
      HEADER("DESTINATION ");
      DATAWORD("%s;",Channels[TDS1002_destination]);
      HEADER("SOURCE ");
      DATAWORD("%s;",Channels[TDS1002_source]);
      HEADER("START ");
      DATAWORD("%d;",TDS1002_datastart);
      HEADER("STOP ");
      DATAWORD("%d;",TDS1002_datastop);
      HEADER("WIDTH ");
      DATAWORD("%d;",TDS1002_datawidth);
    } else {
      if ( sscanf(argument,"%s",buffer2) && 
	   RS232PMatchCommand(buffer2,"INIT") ) {
	TDS1002_destination = REFA;
        TDS1002_source = CH1;
        TDS1002_datastart = 1;
        TDS1002_datastop = TDS1002_nSamples;
        TDS1002SetWFMParameters(CH1);
      } else 
	printf("%s\n",INVALID_ARGUMENT);
    }
  }
  else if ( RS232PMatchCommand(command,"DATa:SOUrce") ) {
    if ( IsQuery ) {
      HEADER(":DATA:SOURCE ");
      DATAWORD("%s",Channels[TDS1002_source]);
    } else if ( sscanf(argument,"%s",buffer2) ) {     
      tmp=RS232PSelectString(buffer2,Channels,5);
      if ( tmp>=0 && tmp<=1 ) TDS1002_source=tmp;
      TDS1002SetWFMParameters(TDS1002_source);
    } else 
      printf("%s\n",INVALID_ARGUMENT);
  }
  else if ( RS232PMatchCommand(command,"DATa:STARt") ) {
    if ( IsQuery ) {
      HEADER(":DATA:START ");
      DATAWORD("%d",TDS1002_datastart);
    } else if ( sscanf(argument,"%d",&itmp) ) {     
      if ( tmp<=1 ) TDS1002_datastart=1;
      else if ( tmp>=TDS1002_datastop) TDS1002_datastart=TDS1002_datastop;
      else TDS1002_datastart=tmp;
    } else 
      printf("%s\n",INVALID_ARGUMENT);
  }
  else if ( RS232PMatchCommand(command,"DATa:STOP") ) {
    if ( IsQuery ) {
      HEADER(":DATA:STOP ");
      DATAWORD("%d",TDS1002_datastart);
    } else if ( sscanf(argument,"%d",&itmp) ) {     
      if ( tmp<=TDS1002_datastart ) TDS1002_datastop=TDS1002_datastart;
      else if ( tmp>=TDS1002_NSAMPLES) TDS1002_datastop=TDS1002_NSAMPLES;
      else TDS1002_datastop=tmp;
    } else 
      printf("%s\n",INVALID_ARGUMENT);
  }
  else if ( RS232PMatchCommand(command,"DATa:WIDth" )) {
    if ( IsQuery ) {
      HEADER(":DATA:WIDTH ");
      DATAWORD("%d",TDS1002_datawidth);
    } else if ( sscanf(argument,"%d",&itmp) ) {     
      if ( itmp<=1 ) TDS1002_datawidth=1;
      else TDS1002_datawidth=2;
      TDS1002SetWFMParameters(TDS1002_source);
    } else 
      printf("%s\n",INVALID_ARGUMENT);
  }
  else if ( RS232PMatchCommand(command,"EXIT")) {
    exit(EXIT_SUCCESS);
  }
  else if ( RS232PMatchCommand(command,"HEADer") ) {
    if ( IsQuery ) {
      HEADER(":HEADER ");
      sprintf(ENDOF(buffer),"%d",TDS1002_headerOn);
    } else {
      if ( sscanf(argument,"%d",&TDS1002_headerOn) ) {
        TDS1002_headerOn=(TDS1002_headerOn?1:0); 
      } else {
	sscanf(argument,"%s",buffer2);
        if (strcasecmp(buffer2,OnOff[ON])==0) TDS1002_headerOn=1;
        else if (strcasecmp(buffer2,OnOff[OFF])==0) TDS1002_headerOn=0; 
	else printf("%s\n",INVALID_ARGUMENT);
      }
    }
  }
  else if ( RS232PMatchCommand(command,"HORizontal") ) {
    if ( IsQuery ) {
    HEADER(":HORIZONTAL:VIEW ");
    sprintf(ENDOF(buffer),"%s;",Views[TDS1002_viewSel]);
    HEADER("RECORDLENGTH ");
    sprintf(ENDOF(buffer),"%d;",TDS1002_nSamples);
    HEADER("MAIN:SCALE ");
    sprintf(ENDOF(buffer),"%lf;",TDS1002_secDiv);
    HEADER("POSITION ");
    sprintf(ENDOF(buffer),"%lf;",TDS1002_secPos);
    HEADER(":HORIZONTAL:DELAY:SCALE ");
    sprintf(ENDOF(buffer),"%lf;",TDS1002_delDiv);
    HEADER("POSITION ");
    sprintf(ENDOF(buffer),"%lf;",TDS1002_delPos);
    } else printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"HORizontal:RECOrdlength") ) {
    if ( IsQuery ) sprintf(ENDOF(buffer),"%d",TDS1002_nSamples);
    else printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"HORizontal:MAIn") ) {
    if ( IsQuery ) {
      HEADER(":HORIZONTAL:MAIN:SCALE ");
      sprintf(ENDOF(buffer),"%lf;",TDS1002_secDiv);
      HEADER("POSITION ");
      sprintf(ENDOF(buffer),"%lf;",TDS1002_secPos);
    } else printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"HORizontal:MAIn:SCAle") ||
	    RS232PMatchCommand(command,"HORizontal:MAIn:SECdiv") ) {
    if ( IsQuery ) {
      HEADER(":HORIZONTAL:MAIN:SCALE ");
      sprintf(ENDOF(buffer),"%lf;",TDS1002_secDiv);
    } else {
      if ( sscanf(argument,"%lf",&tmp)!=1 ) 
	printf("%s\n",INVALID_ARGUMENT);
      else TDS1002_secDiv=TDS1002secDiv(tmp);
      TDS1002SetWFMParameters(TDS1002_source);
    }
  } 
  else if ( RS232PMatchCommand(command,"ID") ) {
    if ( IsQuery ) strcat(buffer,ID_NAME);
    else printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"*IDN") ) {
    if ( IsQuery ) strcat(buffer,IDN_NAME);
    else printf("%s\n",QUERY_ONLY_COMMAND);
  } 
  else if ( RS232PMatchCommand(command,"TRIGger") 
	    || RS232PMatchCommand(command,"TRIGger:MAIn") ) {
    if ( IsQuery ) {
      HEADER(":TRIGGER:MAIN:MODE NORMAL;TYPE EDGE;");
      HEADER("LEVEL ");
      DATAWORD("%lg;",TDS1002_trglevel);
      HEADER("HOLDOFF:VALUE 5.0E-7;");
      HEADER(":TRIGGER:MAIN:EDGE:SOURCE ");
      DATAWORD("%s;",Channels[TDS1002_trgsource]);
      HEADER("COUPLING ");
      DATAWORD("%s;",Couplings[TDS1002_chCoupling[TDS1002_trgsource]]);
      HEADER("SLOPE ");
      DATAWORD("%s;",Slopes[TDS1002_trgslope]);
      HEADER(":TRIGGER:MAIN:VIDEO:SOURCE ");
      DATAWORD("%s;",Channels[TDS1002_trgsource]);
      HEADER("SYNC FIELD;POLARITY NORMAL;");
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"TRIGger:MAIn:EDGE") ) {
    if ( IsQuery ) {
      HEADER(":TRIGGER:MAIN:EDGE:SOURCE ");
      DATAWORD("%s;",Channels[TDS1002_trgsource]);
      HEADER("COUPLING ");
      DATAWORD("%s;",Couplings[TDS1002_chCoupling[TDS1002_trgsource]]);
      HEADER("SLOPE ");
      DATAWORD("%s;",Slopes[TDS1002_trgslope]);
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"TRIGger:MAIn:EDGE:SLOpe") ) {
    if ( IsQuery ) {
      HEADER(":TRIGGER:MAIN:EDGE:SLOPE ");
      DATAWORD("%s;",Slopes[TDS1002_trgslope]);
    } else if ( sscanf(argument,"%s",buffer2) ) {     
      tmp=RS232PSelectString(buffer2,Slopes,2);
      if ( tmp>=0 && tmp<=1 ) TDS1002_trgslope=tmp;
    } else 
      printf("%s\n",INVALID_ARGUMENT);
  }
  else if ( RS232PMatchCommand(command,"TRIGger:MAIn:EDGE:SOUrce") ) {
    if ( IsQuery ) {
      HEADER(":TRIGGER:MAIN:EDGE:SOURCE ");
      DATAWORD("%s;",Channels[TDS1002_trgsource]);
    } else if ( sscanf(argument,"%s",buffer2) ) {     
      tmp=RS232PSelectString(buffer2,Channels,2);
      if ( tmp>=0 && tmp<=1 ) TDS1002_trgsource=tmp;
    } else 
      printf("%s\n",INVALID_ARGUMENT);
  }
  else if ( RS232PMatchCommand(command,"TRIGger:MAIn:LEVel") ) {
    if ( IsQuery ) {
      HEADER(":TRIGGER:MAIN:LEVEL ");
      DATAWORD("%lg;",TDS1002_trglevel);
    } else {
      if ( sscanf(argument,"%lg",&tmp)!=1 ) 
	printf("%s\n",INVALID_ARGUMENT);
      else 
        TDS1002_trglevel=tmp;
    }
  }
  else if ( RS232PMatchCommand(command,"WAVFrm") ) {
    if ( IsQuery ) {
      TDS1002ExecuteCommand("WFMPRE?","");
      TDS1002ExecuteCommand("CURVE?","");
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"WFMPre") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:BYT_NR ");
      DATAWORD("%d;",TDS1002_datawidth);
      HEADER("BIT_NR ");
      DATAWORD("%d;",8*TDS1002_datawidth);
      HEADER("ENCDG ");
      DATAWORD("%s;",WFMEncoding[TDS1002_wfmenc]);
      HEADER("BN_FMT ");
      DATAWORD("%s;","RI");
      HEADER("BYTE_OR ");
      DATAWORD("%s;","LSB");
      HEADER("NR_PT ");
      DATAWORD("%d;",TDS1002_datastop-TDS1002_datastart+1);
      HEADER("WFID ");
      sprintf(ENDOF(buffer),
	 "\"%s %s COUPLING, %lg %s/div, %lg %s/div, %d POINTS, SAMPLE MODE\";",
	      Channels[TDS1002_source],Couplings[TDS1002_chCoupling[TDS1002_source]],
              TDS1002_chVDiv[TDS1002_source],Units[TDS1002_yunit],
	      TDS1002_secDiv,Units[TDS1002_xunit],TDS1002_datastop-TDS1002_datastart+1); 
      HEADER("PT_FMT ");
      DATAWORD("%s;",Format[TDS1002_format]);
      HEADER("XINCR ");
      DATAWORD("%lg;",TDS1002_xincr);
      HEADER("PT_OFF 0;");
      HEADER("XZERO ");
      DATAWORD("%lg;",TDS1002_xzero);
      HEADER("XUNIT ");
      DATAWORD("%s;",Units[TDS1002_xunit]);
      HEADER("YMULT ");
      DATAWORD("%lg;",TDS1002_ymult[TDS1002_source]);
      HEADER("YZERO ");
      DATAWORD("%lg;",TDS1002_yzero[TDS1002_source]);
      HEADER("YOFF ");
      DATAWORD("%lg;",TDS1002_yoff[TDS1002_source]);
      HEADER("YUNIT ");
      DATAWORD("%s;",Units[TDS1002_yunit]);
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"WFMPre:BIT_Nr") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:BIT_NR ");
      DATAWORD("%d;",8*TDS1002_datawidth);
    } else {
      if ( sscanf(argument,"%d",&itmp)!=1 ) 
	printf("%s\n",INVALID_ARGUMENT);
      else if (itmp<=12) TDS1002_datawidth=1;
      else TDS1002_datawidth=2;
    }
  }
  else if ( RS232PMatchCommand(command,"WFMPre:BYT_Nr") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:BYT_NR ");
      DATAWORD("%d;",TDS1002_datawidth);
    } else {
      if ( sscanf(argument,"%d",&itmp)!=1 ) 
	printf("%s\n",INVALID_ARGUMENT);
      else if (itmp<=1) TDS1002_datawidth=1;
      else TDS1002_datawidth=2;
    }
  }
  else if ( RS232PMatchCommand(command,"WFMPre:NR_Pt") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:NR_PT ");
      DATAWORD("%d;",TDS1002_datastop-TDS1002_datastart+1);
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"WFMPre:WFId") ) {
    if ( IsQuery ) {
      HEADER("WFMPRE:WFID ");
      sprintf(ENDOF(buffer),
        "\"%s %s COUPLING, %lg %s/div, %lg %s/div, %d POINTS, SAMPLE MODE\";",
	      Channels[TDS1002_source],Couplings[TDS1002_chCoupling[TDS1002_source]],
              TDS1002_chVDiv[TDS1002_source],Units[TDS1002_yunit],
	      TDS1002_secDiv,Units[TDS1002_xunit],TDS1002_datastop-TDS1002_datastart+1); 
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"WFMPre:XINcr") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:XINCR ");
      DATAWORD("%lg;",TDS1002_xincr);
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"WFMPre:XUNit") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:XUNIT ");
      DATAWORD("%s;",Units[TDS1002_xunit]);
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"WFMPre:XZEro") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:XZERO ");
      DATAWORD("%lg;",TDS1002_xzero);
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"WFMPre:YMUlt") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:YMULT ");
      DATAWORD("%lg;",TDS1002_ymult[TDS1002_source]);
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"WFMPre:YOFf") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:YOFF ");
      DATAWORD("%lg;",TDS1002_yoff[TDS1002_source]);
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"WFMPre:YUNit") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:YUNIT ");
      DATAWORD("%s;",Units[TDS1002_yunit]);
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else if ( RS232PMatchCommand(command,"WFMPre:YZero") ) {
    if ( IsQuery ) {
      HEADER(":WFMPRE:YZERO ");
      DATAWORD("%lg;",TDS1002_yzero[TDS1002_source]);
    } else 
      printf("%s\n",QUERY_ONLY_COMMAND);
  }
  else {
    printf("%s\n",COMMAND_NOT_EXIST);
    return;
  }
  if ( IsQuery ) WriteSerial(TDS1002_device,buffer); 
  return;
}

/*
 * Dato un valore desiderato di V/div, restituisce un valore
 * accettabile per l'oscilloscopio (un valore compreso tra 2 mV
 * e 5 V).
 */
double TDS1002VDiv(double VDiv) {
  VDiv=fabs(VDiv);
  if ( VDiv<=2.E-3 ) return 2.E-3;
  else if ( VDiv>=5. ) return 5.;
  else return VDiv;
}

/*
 * Dato un valore per la posizione verticale di un canale (in divisioni), 
 * restituisce un valore accettabile (il range accetabile dipende dalla
 * scala verticale
 */
double TDS1002VPos(double VPos, int channel) {
  int VDivMax=0;
  if ( TDS1002_chVDiv[channel] <=  2.E-1 ) VDivMax=(int)( 2./TDS1002_chVDiv[channel]);
  else                 VDivMax=(int)(50./TDS1002_chVDiv[channel]);
  if ( VPos<-VDivMax )    return -(double)VDivMax;
  else if ( VPos>VDivMax) return  (double)VDivMax;
  else return VPos;  
}

/*
 * Dato un valore per la scala dei tempi dell'oscilloscopio (in s/div),
 * resituisce un valore accettabile (mantissa = 1, 1.5 o 5, esponente 
 * compreso tra 0 e 9. 
 */
double TDS1002secDiv(double TDiv ) {
  double exp;
  TDiv=fabs(TDiv);
  if ( TDiv<=7.5E-9 ) return 5.E-9;
  else {
    for ( exp=1.E-8; exp<10.; exp*=10. ) {
      if ( TDiv>0.75*exp && TDiv<=1.75*exp ) return exp;
      if ( TDiv>1.75*exp && TDiv<=3.75*exp ) return 2.5*exp;
      if ( TDiv>3.75*exp && TDiv<=7.5*exp  ) return 5.*exp;      
    }
    return 5.;
  }
}

/*
 * Restituisce un numero casuale con distribuzione gaussiana
 * e larghezza sigma.
 * Il metodo è quello classico di generare due numeri a e b distribuiti
 * in maniera uniforme tra 0 ed 1, e poi osservare che il numero
 * sqrt(2.ln a)*cos(2*pi*b) ha una distribuzione gaussiana con larghezza 1.
 * Per generare a e b si usa la funzione rand() delle stdlib 
 * che restituisce un intero distribuito uniformemente tra 0 e RAND_MAX.
 */
double TDS1002Noise(double sigma) {
  double a, b, x, rho, phi;
  a = (double)rand()/(double)RAND_MAX;
  b = (double)rand()/(double)RAND_MAX;
  rho = sqrt(-2.*log(a));
  phi = 2.*M_PI*b;
  x = rho*cos(phi);
  return sigma*x;
}

/*
 * Ogni volta che si cambia la sorgente dei dati o le scale, bisogna
 * invocare questa funzione che mantiene la consistenza dei fattori di 
 * conversione da V/tempi a interi forniti dall'oscilloscopio.
 */
int TDS1002SetWFMParameters(int channel) {
  TDS1002_xzero = TDS1002_secPos-5.*TDS1002_secDiv;
  TDS1002_xincr = (10.*TDS1002_secDiv)/TDS1002_nSamples;
  TDS1002_datamin = -pow(2,8*TDS1002_datawidth-1);
  TDS1002_datamax = -TDS1002_datamin+1;
  TDS1002_yzero[channel] = 0.;
  TDS1002_ymult[channel] = (10.*TDS1002_chVDiv[channel])/(-2*TDS1002_datamin);
  TDS1002_yoff [channel] = ((-2*TDS1002_datamin)/10.)*TDS1002_chVPos[channel];
  return channel;
}

/*
 * Acquisisce una funzione d'onda chiamando la funzione puntata da 
 * TDS1002Wave, che calcola l'output su due canali. 
 */
int TDS1002Acquire(double TimeOut) {
  double DataStore[TDS1002_NSAMPLES][2];
  double sigma=1.e-2, xincr;
  int i, j, triggered=0;
  static double totaltime=0., ch1old=0., ch2old=0., ch1new, ch2new;
  xincr = TDS1002_xincr;
  /* Visto che il trigger deve stare al centro della serie di NSAMPLES punti
     raccolti, per prima cosa iniziamo a leggere almeno NSAMPLES/2. 
   */
  for (i=0; i<TDS1002_NSAMPLES/2; i++) {
    (*TDS1002Wave)(&totaltime,ch1old,ch2old,xincr,&ch1new,&ch2new);
    ch1old=DataStore[i%TDS1002_NSAMPLES][CH1]=ch1new;
    ch2old=DataStore[i%TDS1002_NSAMPLES][CH2]=ch2new;
  }  
  /* Ora si continua a leggere e memorizzare nuovi punti fino a quando
     avviene un trigger o si supera un TimeOut definito dal programma. 
     I dati vengono aggiunti a DataStore. Quando si raggiunge la fine di 
     DataStore, si ricomincia dal principio (per questo si calcola
     l'indice come i%NSAMPLES. 
   */
  while ( (TimeOut-=xincr)>0 && !triggered ) {
    (*TDS1002Wave)(&totaltime,ch1old,ch2old,xincr,&ch1new,&ch2new);
    /* Decisione del trigger */
    if ( TDS1002_trgsource==CH1 ) {
      if ( ( TDS1002_trgslope==RISE && ch1new>TDS1002_trglevel && ch1old<=TDS1002_trglevel )
	   ||( TDS1002_trgslope==FALL && ch1new<TDS1002_trglevel && ch1old>=TDS1002_trglevel ) )
	triggered=1;
    } else {
      if ( ( TDS1002_trgslope==RISE && ch2new>TDS1002_trglevel && ch2old<=TDS1002_trglevel )
	   ||( TDS1002_trgslope==FALL && ch2new<TDS1002_trglevel && ch2old>=TDS1002_trglevel ) )
	triggered=1;
    }     
    ch1old=DataStore[i%TDS1002_NSAMPLES][CH1]=ch1new;
    ch2old=DataStore[i%TDS1002_NSAMPLES][CH2]=ch2new;
    i++;
  }
  if (triggered) {
    /* Dopo il trigger bisogna leggere altri NSAMPLES/2 dati. */
    for (j=0;j<TDS1002_NSAMPLES/2-1;j++) {
      (*TDS1002Wave)(&totaltime,ch1old,ch2old,xincr,&ch1new,&ch2new);
      ch1old=DataStore[(i+j)%TDS1002_NSAMPLES][CH1]=ch1new;
      ch2old=DataStore[(i+j)%TDS1002_NSAMPLES][CH2]=ch2new;
    } 
    /* Poi tutt gli NSAMPLES dati devono essere digitalizzati e trasferiti 
       nella memoria dell'oscillscopio TDS1002_waveform. */  
    for (j=0;j<TDS1002_NSAMPLES;j++) {
      TDS1002_waveform[j]=(DataStore[(i-TDS1002_NSAMPLES/2-1+j)%TDS1002_NSAMPLES][TDS1002_source]+TDS1002Noise(sigma)-TDS1002_yzero[TDS1002_source])/TDS1002_ymult[TDS1002_source]+TDS1002_yoff[TDS1002_source]; 
      if ( TDS1002_waveform[j]<TDS1002_datamin ) TDS1002_waveform[j]=TDS1002_datamin; 
      else if ( TDS1002_waveform[j]>TDS1002_datamax ) TDS1002_waveform[j]=TDS1002_datamax; 
   }
    /* ...e si restituisce il numero di punti collezionati. */
    return TDS1002_NSAMPLES;
  } else 
    /* In assenza di trigger restituisci 0 */
    return 0;
}
