unit Labio;
{ ==============================================================
  Laboratory interface I/O module V1.0a
  (c) J.Dempster, University of Strathclyde, 1997, All Rights Reserved
  Supports :
  National Instruments interfaces
  Cambridge Electronic Design 1401 series
  V1.0 10/3/97
  V1.0a 3/4/97 -10412 error fixed
  V2.0 1/5/97 Digidata 1200 support added
  V2.0a 5/3/98 Digidata ports can now be set by program (bug fix)
               N.I. board DMA channels can be disabled
  ===============================================================}
interface

uses WinTypes,labpc,ced1401,dd1200,vds,classes,global ;
type

    TLaboratoryInterface = (None,NI,DD,CED) ;

  procedure GetLabInterfaceList( var NameList : TStringList ) ;
  procedure SelectLaboratoryInterface( InterfaceNumber : LongInt ) ;
  procedure ConfigureLaboratoryInterface ;
  Procedure GetADCVoltageRangeOptions( var RangeOptions : array of TADCRange ;
                                       var NumOptions : Integer) ;
  procedure ADCToMemory( var ADCBuf : array of integer ;
                         nChannels : LongInt ;
                         nSamples : LongInt ;
                         var dt : Single ;
                         ADCVoltageRange : Single ;
                         WaitForExtTrigger : Boolean ;
                         CircularBuffer : Boolean  ) ;
  procedure GetADCSamples( pADC : Pointer ;
                           var NumSamples : LongInt ) ;
  procedure MemoryToDAC( var DACBuf : array of integer ;
                         nChannels : LongInt ;
                         nPoints : LongInt ;
                         dt : Single ;
                         NumRepeats : LongInt ;
                         var DACActive : boolean ) ;
  procedure ConvertToDACCodes( var DACBuf : Array of Integer ;
                                  nChannels : LongInt ;
                                  nPoints : LongInt ) ;
  
  procedure StopDAC ;
  procedure WriteOutputPorts( CommandVoltage : Single ;
                              DigValue : LongInt ) ;
  procedure GetSyncPulseLevels( var OnVolts, OffVolts : single ) ;
  procedure StopADC ;
  procedure GetLabInterfaceInfo( var Supplier,Model : String ) ;
  procedure GetSamplingIntervalRange( var MinInterval,MaxInterval : single ) ;
  Function ReadADC( Channel : Integer ;
                    ADCVoltageRange : Single ) : Integer ;

  procedure CheckSamplingInterval( var SamplingInterval : single ) ;
  procedure MemoryToDigitalPort( const Hnd : Thandle ;
                               var DigBuf : array of Integer ;
                               nValues : LongInt ;
                               var DACBuf : array of Integer ;
                               NI_StartAt : LongInt ;
                               var dt : single ;
                               var NewDigitalPattern : boolean ) ;

  procedure StopDIG( Pattern : LongInt ) ;
  procedure WriteToDigitalOutPutPort( Pattern : LongInt ) ;
  function GetMaxDACVolts : single ;
  procedure GetChannelOffsets( var Offsets : Array of LongInt ; NumChannels : LongInt ) ;
  function IsLabInterfaceAvailable( InterfaceCardNum : LongInt ) : boolean ;
  procedure ConvertADCValue( var Value : Integer ;
                           const iSample : LongInt ;
                           var Chan : Integer ;
                           const nChannels : Integer ;
                           var x : single ;
                           const dx : single ) ;
  procedure CloseLaboratoryInterface ;

implementation
var
   LaboratoryInterface : TLaboratoryInterface ;
   NumBytes : LongInt ;

procedure GetLabInterfaceList( var NameList : TStringList ) ;
{ ------------------------------------------------------
  Returns list of Laboratory Interface hardware supported
  ------------------------------------------------------}
begin
     NameList.Add( 'No Lab. Interface hardware' ) ;
     NameList.Add( 'National Instruments card') ;
     NameList.Add( 'Axon Instruments Digidata 1200') ;
     NameList.Add( 'Cambridge Electronic Design' ) ;
     end ;


procedure SelectLaboratoryInterface( InterfaceNumber : LongInt ) ;
{ ------------------------------------------
  Sets the type of lab. interface to be used
  ------------------------------------------}
begin
     LaboratoryInterface := TLaboratoryInterface(InterfaceNumber) ;
     { Obtain hardware settings }
     ConfigureLaboratoryInterface ;
     end ;


procedure ConfigureLaboratoryInterface ;
{ ---------------------------------------------------------
  Configure the interface's I/O port, IRQ, and DMA settings
  Data obtained from "Settings" in global.pas.
  ---------------------------------------------------------}
begin
     {Note ... currently onlt digidata 1200 hardware set this way}
     case LaboratoryInterface of
          DD : DD_ConfigureHardware( Settings.DD1200IOPort,
                                     Settings.DD1200IRQ,
                                     Settings.DD1200DMA ) ;
          { Enable/disable DMA for NIDAQ library }
          NI : NI_ConfigureHardware( Settings.NIDisableDMA ) ;
          end ;
     end ;


Procedure GetADCVoltageRangeOptions( var RangeOptions : array of TADCRange ;
                                     var NumOptions : Integer) ;
{ -------------------------------------------------
  Returns a string list of A/D input voltage ranges
  -------------------------------------------------}
begin
     case LaboratoryInterface of
          NI : NI_GetADCVoltageRangeOptions( RangeOptions,NumOptions) ;
          DD : DD_GetADCVoltageRangeOptions( RangeOptions,NumOptions) ;
          CED : CED_GetADCVoltageRangeOptions( RangeOptions,NumOptions) ;
          else begin
               RangeOptions[0] := ' 5V ';
               NumOptions := 1 ;
               end ;
          end ;
     end ;


procedure ADCToMemory( var ADCBuf : array of integer ;
                       nChannels : LongInt ;
                       nSamples : LongInt ;
                       var dt : Single ;
                       ADCVoltageRange : Single ;
                       WaitForExtTrigger : Boolean ;
                       CircularBuffer : Boolean  ) ;
{ -----------------------------------------------------------------------
  Initiates an A/D converter sampling sweep
  Enter with :
  ADCBuf : A/D data buffer
  nChannel : number of A/D channels
  nSamples : number of samples per channel
  dt : sampling interval (s)
  ADCVoltageRange : A/D converter input voltage range (V)
  WaitforExtTrigger : TRUE = Wait for external trigger pulse before starting
  CircularBuffer : TRUE = Continuous A/D conversion into circular buffer
  -------------------------------------------------------------------------}
var
   i : LongInt ;
begin
     { Fill buffer with empty flag }
     for i := 0 to nSamples*nChannels-1 do ADCBuf[i] := EmptyFlag ;

     case LaboratoryInterface of
          NI : NI_ADCToMemory(ADCBuf,nChannels,nSamples,dt,ADCVoltageRange,
                              WaitForExtTrigger,CircularBuffer);
          DD : DD_ADCToMemory(ADCBuf,nChannels,nSamples,dt,ADCVoltageRange,
                              WaitForExtTrigger,CircularBuffer);
          CED : CED_ADCToMemory(ADCBuf,nChannels,nSamples,dt,ADCVoltageRange,
                                WaitForExtTrigger,CircularBuffer);
          end ;
     end ;

procedure GetADCSamples( pADC : Pointer ;
                         var NumSamples : LongInt ) ;
{ ---------------------------------------------------------
  Special routine for extracting A/D samples from interface
  when direct DMA to host memory not supported. (Used by CED 1401)
  ---------------------------------------------------------}
begin
     case LaboratoryInterface of
          CED : CED_GetADCSamples( pADC, NumSamples ) ;
          end ;
     end ;


procedure MemoryToDAC( var DACBuf : array of integer ;
                       nChannels : LongInt ;
                       nPoints : LongInt ;
                       dt : Single ;
                       NumRepeats : LongInt ;
                       var DACActive : boolean ) ;
{ ------------------------------------------
  Initiates an D/A converter output waveform
  ------------------------------------------}
begin
     case LaboratoryInterface of
          NI : begin
            { if not DACActive then}
                NI_MemoryToDAC( DACBuf,nChannels,nPoints,dt,NumRepeats,DACActive) ;
                { NOTE ... if DAC already active then it is not necessary
                  to call this routine. Simply placing the data in DACBuf
                  is enough }
             end ;
          DD : DD_MemoryToDAC( DACBuf,nChannels,nPoints,dt,NumRepeats,DACActive);
          CED : CED_MemoryToDAC( DACBuf,nChannels,nPoints,dt,NumRepeats,DACActive) ;
          end ;
     end ;


procedure ConvertToDACCodes( var DACBuf : Array of Integer ;
                              nChannels : LongInt ;
                              nPoints : LongInt ) ;
begin
     case LaboratoryInterface of
          DD :  DD_ConvertToDACCodes( DACBuf, nChannels, nPoints ) ;
          CED : CED_ConvertToDACCodes( DACBuf, nChannels, nPoints ) ;
          end ;
     end ;


procedure StopDAC ;
begin
     case LaboratoryInterface of
          NI : NI_StopDAC ;
          DD : DD_StopDAC ;
          CED : CED_StopDAC ;
          end ;
     end ;



procedure WriteOutputPorts( CommandVoltage : Single ;
                            DigValue : LongInt ) ;
var
   SyncPulseOnVolts, SyncPulseOffVolts : single ;
   DACVolts : Array[0..1] of single ;
begin
     { D/A 0 voltage clamp command voltage }
     DACVolts[0] := CommandVoltage ;
     GetSyncPulseLevels( SyncPulseOnVolts, SyncPulseOffVolts ) ;
     { D/A 1 TTL synchronisation pulse level (either 0 or 5V) }
     DACVolts[1] := SyncPulseOffVolts ;
     case LaboratoryInterface of
          NI : begin
             NI_WriteDACs( DACVolts, 2 ) ;
             NI_WriteToDigitalOutPutPort( DigValue ) ;
             end ;
          DD : DD_WriteDACsAndDigitalPort( DACVolts,2,DigValue ) ;
          CED : begin
             CED_WriteDACs( DACVolts, 2 ) ;
             CED_WriteToDigitalOutPutPort( DigValue ) ;
             end ;
          end ;
     end ;


procedure GetSyncPulseLevels( var OnVolts, OffVolts : single ) ;
{ ----------------------------------------------------------
  Define On and Off voltage levels of the synchorisation pulse
  supplied on D/A 1, used to trigger a recording sweep
  ----------------------------------------------------------}
begin
     case LaboratoryInterface of
          NI : begin
             { National Instruments boards use TTL Active-Low trigger
               i.e. a 5V --> 0V transition triggers }
             OnVolts := 0. ;
             OffVolts := 5. ;
             end ;
          DD : begin
             { Axon Instruments Digidata 1200 uses TTL Active-High trigger
               i.e. a oV --> 5V transition triggers}
             OnVolts := 5. ;
             OffVolts := 0. ;
             end ;
          CED : begin
             { CED 1401 boards use TTL Active-Low trigger
               i.e. a 5V --> 0V transition triggers }
             OnVolts := 0. ;
             OffVolts := 5. ;
             end ;
          else begin
             { Default settings }
             OnVolts := 0. ;
             OffVolts := 5. ;
             end ;
          end ;
     end ;


procedure StopADC ;
begin
     case LaboratoryInterface of
          NI : NI_StopADC ;
          DD : DD_StopADC ;
          CED : CED_StopADC ;
          end ;
     end ;


procedure GetLabInterfaceInfo( var Supplier,Model : String ) ;
begin
     case LaboratoryInterface of
          NI : NI_GetLabInterfaceInfo( Supplier, Model ) ;
          DD : DD_GetLabInterfaceInfo( Supplier, Model ) ;
          CED : CED_GetLabInterfaceInfo( Supplier, Model ) ;
          else begin
               Supplier := 'No Interface Hardware ' ;
               Model := ' ' ;
               end ;
          end ;
     end ;


procedure GetSamplingIntervalRange( var MinInterval,MaxInterval : single ) ;
begin
     case LaboratoryInterface of
          NI : NI_GetSamplingIntervalRange( MinInterval,MaxInterval ) ;
          CED : CED_GetSamplingIntervalRange( MinInterval,MaxInterval ) ;
          DD : DD_GetSamplingIntervalRange( MinInterval,MaxInterval ) ;
          else begin
               MinInterval := 1E-5 ;
               MaxInterval := 1E3 ;
               end ;
          end ;
     end ;


Function ReadADC( Channel : Integer ; ADCVoltageRange : Single ) : Integer ;
begin
     case LaboratoryInterface of
          NI : Result := NI_ReadADC( Channel, ADCVoltageRange ) ;
          end ;
     end ;


procedure CheckSamplingInterval( var SamplingInterval : single ) ;
{ ----------------------------------------------------
  Adjust sampling interval (secs) to one whicb is compatible
  with laboratory interface timers
  -------------------------------------------------------}
var
    dbSamplingInterval : double ;
    Timebase : Integer ;
    wTimebase,ClockTicks : Word ;
    lTicks : LongInt ;
begin
     case LaboratoryInterface of
          NI : begin
             dbSamplingInterval := SamplingInterval ;
             NI_CheckSamplingInterval(dbSamplingInterval,TimeBase,ClockTicks);
             SamplingInterval := dbSamplingInterval ;
             end ;
          DD : DD_CalculateClockTicks( SamplingInterval,lTicks,wTimebase);

          CED : CED_ClockTicks(SamplingInterval,wTimeBase,ClockTicks);
          end ;
     end ;


procedure MemoryToDigitalPort( const Hnd : Thandle ;
                               var DigBuf : array of Integer ;
                               nValues : LongInt ;
                               var DACBuf : array of Integer ;
                               NI_StartAt : LongInt ;
                               var dt : single ;
                               var NewDigitalPattern : boolean ) ;
{ -------------------------------------------------------
  Set up a bit pattern for transfer to digital O/P ports
  ------------------------------------------------------- }
var
   j : LongInt ;
begin
     case LaboratoryInterface of
          NI : begin
             { National Instruments digital output procedure
               Digital output only occurs during the recording sweep
               so adjust shift digital waveform to account for the
               pre-recording phase of the command voltage waveform }
             if NewDigitalPattern then begin
                j := NI_StartAt ;
                NumBytes := 0 ;
                While j < nValues do begin
                   DigBuf[NumBytes] := DigBuf[j] ;
                   Inc(NumBytes) ;
                   j := j + 2 ;
                   end ;
                end ;
             NI_MemoryToDigitalPort( DigBuf, NumBytes ) ;
             end ;
          DD : begin
             { Digidata 1200 outputs digital channels simultaneously
               with D/A output. Data is located in lower 4 bits of
               each DAC Code word}
             DD_CombineDACAndDigitalWaveforms(DACBuf,DigBuf,2,nValues) ;
             NumBytes := nValues ;
             end ;
          CED : begin
             if NewDigitalPattern then begin
                j := NI_StartAt ;
                NumBytes := 0 ;
                While j < nValues do begin
                   DigBuf[NumBytes] := DigBuf[j] ;
                   Inc(NumBytes) ;
                   Inc(j) ;
                   end ;
                end ;
             CED_MemoryToDigitalPort( DigBuf, NumBytes, dt ) ;
             end ;
          end ;
     NewDigitalPattern := False ;
     end ;


procedure StopDIG( Pattern : LongInt ) ;
{ ---------------------------------------------------------
  Stop digital pattern generation and leave digital outputs
  bits set to "Pattern"
  ---------------------------------------------------------}
begin
     case LaboratoryInterface of
          NI : NI_StopDIG( Pattern ) ;
          CED : begin
              CED_StopDIG ;
              CED_WriteToDigitalOutPutPort( Pattern ) ;
              end ;
          end ;
     end ;


procedure WriteToDigitalOutPutPort( Pattern : LongInt ) ;
{ ---------------------------------------
  Write a single byte to digital O/P port
  ---------------------------------------}
begin
     case LaboratoryInterface of
          NI : NI_WriteToDigitalOutPutPort( Pattern ) ;
          CED : CED_WriteToDigitalOutPutPort( Pattern ) ;
          end ;
     end ;


function GetMaxDACVolts : single ;
{ -----------------------------------------
  Return maximum D/A output voltage (Volts)
  -----------------------------------------}
begin
     case LaboratoryInterface of
          NI : Result := NI_GetMaxDACVolts ;
          DD : Result := DD_GetMaxDACVolts ;
          CED : Result := CED_GetMaxDACVolts ;
          else Result := 5. ;
          end ;
     end ;


procedure GetChannelOffsets( var Offsets : Array of LongInt ; NumChannels : LongInt ) ;
{ -------------------------------------------------
  Return the sequence in which the A/D channels
  are stored in the A/D data buffer in "Offsets"
  -------------------------------------------------}
var
   ch : Integer ;
begin
     case LaboratoryInterface of
          NI : NI_GetChannelOffsets( Offsets,NumChannels ) ;
          DD : DD_GetChannelOffsets( Offsets,NumChannels ) ;
          CED : CED_GetChannelOffsets( Offsets,NumChannels ) ;
          else for ch := 0 to NumChannels-1 do Offsets[ch] := ch ;
          end ;
     end ;


function IsLabInterfaceAvailable( InterfaceCardNum : LongInt ) : boolean ;
{ -------------------------------------------------------
  Return TRUE if the selected interface card is available
  -------------------------------------------------------}
begin
     LaboratoryInterface := TLaboratoryInterface(InterfaceCardNum) ;

     case LaboratoryInterface of
          NI : Result := NI_IsLabInterfaceAvailable ;
          DD : Result := DD_IsLabInterfaceAvailable ;
          CED : Result := CED_IsLabInterfaceAvailable ;
          else Result := False ;
          end ;
     end ;


procedure ConvertADCValue( var Value : Integer ;
                           const iSample : LongInt ;
                           var Chan : Integer ;
                           const nChannels : Integer ;
                           var x : single ;
                           const dx : single ) ;
{ -------------------------------------------------------
  Convert A/D value from native hardware format to standard
  12 bit signed value (+/-2048). Also return the amalogue
  channel no. {Chan) calculated from the position in the array
  (iSample) and numberof channel (nChannels). Increment the X-axis
  position (x) by (dx) every time a complete set of channels have
  been sampled
  ------------------------------------------------------- }
begin
     case LaboratoryInterface of
          NI : begin
              Chan := nChannels - (iSample mod nChannels) - 1 ;
              if Chan = (nChannels-1) then x := x + dx ;
              end ;
          DD : begin
              Chan := iSample mod nChannels ;
              if Chan = 0 then x := x + dx ;
              end ;
          CED : begin
              Value := Value div 16 ;
              Chan := iSample mod nChannels ;
              if Chan = 0 then x := x + dx ;
              end ;
          end ;
     end ;


 procedure CloseLaboratoryInterface ;
 { ------------------------------------------------
   Shut down the laboratory interface sub-systems
   -----------------------------------------------}
 begin
      case LaboratoryInterface of
          CED : CED_CloseLaboratoryInterface ;
          DD : DD_CloseLaboratoryInterface ;
          end ;
      end ;


Initialization
    LaboratoryInterface := None ;

end.
