Skip to content
 
 

Latest commit

 

History

144 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

National Instruments Data Acquisition Interface

This package provides an interface to NI-DAQmx--- National Instruments' driver for their data acquisition boards. Their entire C header file was ported using Clang.jl, and a rudimentary higher-level API is provided for ease of use.

Similar functionality for the Python language is provided by PyDAQmx.

System Requirements

NI-DAQmx Base is not supported, so you'll need a Windows box, and a National Instruments card of course.

Installation

First download and install NI-DAQmx version 18.6 ( or for julia v6, 17.1.0; or for Julia v5, 16.0.0; or for Julia v4, 15.1.1; or for Julia v3, 14.1.0, 14.0.0, or 9.6.0) from National Instruments. Then on the Julia command line:

]add NIDAQ

Basic Usage

With no input arguments, the high-level getproperties function can be used to query the system:

julia> using NIDAQ

julia> getproperties()
Dict{String,Tuple{Any,Bool}} with 7 entries:
  "DevNames"           => (SubString{String}["Dev1"],false)
  "GlobalChans"        => (SubString{String}[""],false)
  "NIDAQMajorVersion"  => (0x00000010,false)
  "NIDAQMinorVersion"  => (0x00000000,false)
  "NIDAQUpdateVersion" => (0x00000000,false)
  "Scales"             => (SubString{String}[""],false)
  "Tasks"              => (SubString{String}[""],false)

Returned is a dictionary of tuples, the first member indicating the property value and the second a boolean indicating whether the former is mutable.

getproperties can also input a string containing the name of a data acquisition device:

julia> getproperties("Dev1")
Dict{String,Tuple{Any,Bool}} with 61 entries:
  "AIBridgeRngs"                           => (Float64[],false)
  "AICouplings"                            => (:Val_Transferred_From_Buffer,false)
  "AICurrentIntExcitDiscreteVals"          => (Float64[],false)
  "AICurrentRngs"                          => (Float64[],false)
  "AIDigFltrLowpassCutoffFreqDiscreteVals" => (Float64[],false)
  "AIDigFltrLowpassCutoffFreqRangeVals"    => (Float64[],false)
  "AIFreqRngs"                             => (Float64[],false)
  "AIGains"                                => (Float64[],false)
  "AILowpassCutoffFreqDiscreteVals"        => (Float64[],false)
  "AILowpassCutoffFreqRangeVals"           => (Float64[],false)
  "AIMaxMultiChanRate"                     => (2.0e6,false)
  "AIMaxSingleChanRate"                    => (2.0e6,false)
  "AIMinRate"                              => (0.0232831,false)
  "AIPhysicalChans"                        => (SubString{String}["Dev1/ai0","Dev1/ai1","Dev1/ai2",".
  "AIResistanceRngs"                       => (Float64[],false)
  "AISampModes"                            => (Symbol[:Val_FiniteSamps,:Val_ContSamps],false)
  "AISupportedMeasTypes"                   => (Symbol[:Val_Current,:Val_Resistance,:Val_Strain_Gage.
  "AITrigUsage"                            => (14,false)
  "AIVoltageIntExcitDiscreteVals"          => (Float64[],false)
  "AIVoltageIntExcitRangeVals"             => (Float64[],false)
  "AIVoltageRngs"                          => ([-1.0,1.0,-2.0,2.0,-5.0,5.0,-10.0,10.0],false)
  "AOCurrentRngs"                          => (Float64[],false)
  "AOGains"                                => (Float64[],false)
  "AOMaxRate"                              => (3.33333e6,false)
  "AOMinRate"                              => (0.0232831,false)
  "AOPhysicalChans"                        => (SubString{String}["Dev1/ao0","Dev1/ao1"],false)
  "AOSampModes"                            => (Symbol[:Val_FiniteSamps,:Val_ContSamps],false)
  "AOSupportedOutputTypes"                 => (Symbol[:Val_Voltage],false)
  "AOTrigUsage"                            => (10,false)
  "AOVoltageRngs"                          => ([-5.0,5.0,-10.0,10.0],false)
  "AccessoryProductNums"                   => (UInt32[0x00000000],false)
  "AccessoryProductTypes"                  => (SubString{String}[""],false)
  "AccessorySerialNums"                    => (UInt32[0x00000000],false)
  "BusType"                                => (:Val_USB,false)
  "CIMaxSize"                              => (0x00000020,false)
  "CIMaxTimebase"                          => (1.0e8,false)
  "CIPhysicalChans"                        => (SubString{String}["Dev1/ctr0","Dev1/ctr1","Dev1/ctr2.
  "CISampModes"                            => (Symbol[:Val_FiniteSamps,:Val_ContSamps],false)
  "CISupportedMeasTypes"                   => (Symbol[:Val_CountEdges,:Val_Freq,:Val_Period,:Val_Tw.
  "CITrigUsage"                            => (42,false)
  "COMaxSize"                              => (0x00000020,false)
  "COMaxTimebase"                          => (1.0e8,false)
  "COPhysicalChans"                        => (SubString{String}["Dev1/ctr0","Dev1/ctr1","Dev1/ctr2.
  "COSampModes"                            => (Symbol[:Val_FiniteSamps,:Val_ContSamps],false)
  "COSupportedOutputTypes"                 => (Symbol[:Val_Pulse_Freq,:Val_Pulse_Ticks,:Val_Pulse_T.
  "COTrigUsage"                            => (42,false)
  "ChassisModuleDevNames"                  => (SubString{String}[""],false)
  "DILines"                                => (SubString{String}["Dev1/port0/line0","Dev1/port0/lin.
  "DIMaxRate"                              => (1.0e7,false)
  "DIPorts"                                => (SubString{String}["Dev1/port0","Dev1/port1","Dev1/po.
  "DITrigUsage"                            => (14,false)
  "DOLines"                                => (SubString{String}["Dev1/port0/line0","Dev1/port0/lin.
  "DOMaxRate"                              => (1.0e7,false)
  "DOPorts"                                => (SubString{String}["Dev1/port0","Dev1/port1","Dev1/po.
  "DOTrigUsage"                            => (10,false)
  "NumDMAChans"                            => (0x00000000,false)
  "ProductCategory"                        => (:Val_XSeriesDAQ,false)
  "ProductNum"                             => (0x000075a1,false)
  "ProductType"                            => (SubString{String}["USB-6366 (64 MS) (Mass Terminatio.
  "SerialNum"                              => (0x01719e54,false)
  "Terminals"                              => (SubString{String}["/Dev1/PFI0","/Dev1/PFI1","/Dev1/P.

One can index into the dictionary to get a list of channels:

julia> getproperties("Dev1")["AIPhysicalChans"]
(SubString{ASCIIString}["Dev1/ai0","Dev1/ai1","Dev1/ai2","Dev1/ai3","Dev1/ai4","Dev1/ai5","Dev1/ai6","Dev1/ai7"],false)

A bit simpler in this case though is to use another high-level function which returns just the string Array:

julia> analog_input_channels("Dev1")
8-element Array{String,1}:
 "Dev1/ai0"
 "Dev1/ai1"
 "Dev1/ai2"
 "Dev1/ai3"
 "Dev1/ai4"
 "Dev1/ai5"
 "Dev1/ai6"
 "Dev1/ai7"

To add, for example, analog input channels, use the high-level analog_input function:

julia> t = analog_input("Dev1/ai0:1")
NIDAQ.AITask(Ptr{Nothing} @0x0000000025d18600)

julia> typeof(t)
NIDAQ.AITask (constructor with 3 methods)

julia> supertype(NIDAQ.AITask)
NIDAQ.Task

Two channels were added above using the : notation. Additional channels can be added later by inputing the returned Task:

julia> analog_input(t, "Dev1/ai2")

getproperties can also input a Task:

julia> getproperties(t)
Dict{String,Tuple{Any,Bool}} with 5 entries:
  "Devices"    => (SubString{String}["Dev1"],false)
  "Channels"   => (SubString{String}["Dev1/ai0","Dev1/ai1","Dev1/ai2"],false)
  "Name"       => (SubString{String}["_unnamedTask<0>"],false)
  "NumChans"   => (0x00000003,false)
  "NumDevices" => (0x00000001,false)

as well as a string containing the name of the channel:

julia> getproperties(t, "Dev1/ai0")
Dict{String,Tuple{Any,Bool}} with 52 entries:
  "AccelUnits"                        => (:Val_g,false)
  "AutoZeroMode"                      => (:Val_None,false)
  "BridgeUnits"                       => (:Val_VoltsPerVolt,false)
  "ChanCalDesc"                       => (SubString{String}[""],false)
  "ChanCalOperatorName"               => (SubString{String}[""],false)
  "ChanCalPolyForwardCoeff"           => (Float64[],false)
  "ChanCalPolyReverseCoeff"           => (Float64[],false)
  "ChanCalScaleType"                  => (:Val_Table,false)
  "ChanCalTablePreScaledVals"         => (Float64[],false)
  "ChanCalTableScaledVals"            => (Float64[],false)
  "ChanCalVerifAcqVals"               => (Float64[],false)
  "ChanCalVerifRefVals"               => (Float64[],false)
  "Coupling"                          => (:Val_DC,false)
  "CurrentACRMSUnits"                 => (:Val_Amps,false)
  "CurrentUnits"                      => (:Val_Amps,false)
  "CustomScaleName"                   => (SubString{String}[""],false)
  "DataXferMech"                      => (:Val_ProgrammedIO,false)
  "DataXferReqCond"                   => (:Val_OnBrdMemNotEmpty,false)
  "DevScalingCoeff"                   => ([0.000102924,0.000312673,5.87393e-14,-3.31855e-19],false)
  "EddyCurrentProxProbeUnits"         => (:Val_Meters,false)
  "ForceUnits"                        => (:Val_Newtons,false)
  "FreqUnits"                         => (:Val_Hz,false)
  "Gain"                              => (1.0,false)
  "InputSrc"                          => (SubString{String}["_external_channel"],false)
  "LVDTUnits"                         => (:Val_Meters,false)
  "LossyLSBRemovalCompressedSampSize" => (0x00000010,false)
  "Max"                               => (10.0,false)
  "MeasType"                          => (:Val_Voltage,false)
  "Min"                               => (-10.0,false)
  "PressureUnits"                     => (:Val_PoundsPerSquareInch,false)
  "RVDTUnits"                         => (:Val_Degrees,false)
  "RawDataCompressionType"            => (:Val_None,false)
  "RawSampJustification"              => (:Val_RightJustified,false)
  "RawSampSize"                       => (0x00000010,false)
  "ResistanceUnits"                   => (:Val_Ohms,false)
  "Resolution"                        => (16.0,false)
  "ResolutionUnits"                   => (:Val_Bits,false)
  "RngHigh"                           => (10.0,false)
  "RngLow"                            => (-10.0,false)
  "SoundPressureUnits"                => (:Val_Pascals,false)
  "StrainGageCfg"                     => (:Val_FullBridgeI,false)
  "StrainUnits"                       => (:Val_Strain,false)
  "TempUnits"                         => (:Val_DegC,false)
  "TermCfg"                           => (:Val_Diff,false)
  "ThrmcplCJCVal"                     => (25.0,false)
  "TorqueUnits"                       => (:Val_NewtonMeters,false)
  "UsbXferReqCount"                   => (0x00000004,false)
  "UsbXferReqSize"                    => (0x00008000,false)
  "VelocityUnits"                     => (:Val_MetersPerSecond,false)
  "VoltageACRMSUnits"                 => (:Val_Volts,false)
  "VoltageUnits"                      => (:Val_Volts,false)
  "VoltagedBRef"                      => (1.0,false)

Use setproperty! to change a mutable property:

julia> setproperty!(t, "Dev1/ai0", "Max", 5.0)

Once everything is configured, get some data using the read function:

julia> start(t)

julia> read(t, 10)
10x3 Array{Float64,2}:
 1.52407   -0.448835   0.381075
 1.37546   -0.213537   0.305847
 1.2363    -0.0268698  0.262826
 1.109      0.118619   0.243117
 0.995797   0.2311     0.240073
 0.896695   0.315782   0.248004
 0.811452   0.378752   0.262746
 0.739429   0.424257   0.281893
 0.679263   0.456223   0.302402
 0.629672   0.477774   0.323473

julia> stop(t)

julia> clear(t)

read can also return Int16, Int32, UInt16, and UInt32 by specifying those types as an additional argument:

julia> read(t, 10, Int16)
10×3 Array{Int16,2}:
 -12619  -5351  -13973
 -12618  -5350  -13973
 -12620  -5350  -13973
 -12619  -5350  -13974
 -12618  -5351  -13972
 -12618  -5348  -13974
 -12619  -5350  -13973
 -12619  -5350  -13973
 -12619  -5350  -13972
 -12620  -5350  -13973

Similar work flows exist for analog_output, digital_input, and digital_output. The high-level API also supports many counter functions too, including count_edges and generate_pulses. For a full list of convenience functions use the names function in Julia Base:

julia> names(NIDAQ)
25-element Array{Symbol,1}:
  :analog_output_channels 
  :digital_input_channels 
  :setproperty!           
  :line_to_line           
  :counter_input_channels 
  :counter_output_channels
  :NIDAQ                  
  :analog_input_ranges    
  :digital_input          
  :stop                   
  :generate_pulses        
  :count_edges            
  :digital_output_channels
  :analog_input           
  :channel_type           
  :analog_output_ranges   
  :devices                
  :digital_output         
  :getproperties          
  :quadrature_input       
  :analog_input_channels  
  :analog_output          
  :Bool32                 
  :clear                  

NIDAQmx is a powerful interface, and while NIDAQ.jl provides wrappers for all of it's functions, it only abstracts a few of them. If these don't suit your needs you'll have to dive deep into src/functions_V*.jl and src/constants_V*.jl. Complete documentation of this low-level API is here and here.

One situation where the low-level API is needed is to specify continous output of pulses using a counter:

julia> t = generate_pulses("Dev1/ctr0")
NIDAQ.COTask(Ptr{Nothing} @0x00000000059d8790)

julia> fieldnames(typeof(t))
(:th,)

julia> NIDAQ.CfgImplicitTiming(t.th, NIDAQ.Val_ContSamps, UInt64(1))
0

Note that tasks consist of just a single field th, and that this "task handle" is what must be passed into many low-level routines.

Also, for brevity NIDAQ.jl strips the "DAQmx" prefix to all functions and constants in NI-DAQmx, and converts the latter to 32 bits. One must still take care to caste the other inputs appropriately though.

Adding Support for a Version of NI-DAQmx

Install Clang.jl. If there are build problems, make sure that llvm-config is on your PATH, and that libclang can be found, as described in the Clang.jl README. Clang defaults to using a system installed version of LLVM. An alternative is to set BUILD_LLVM_CLANG=1 in Make.user, and compile Julia from source.

Find NIDAQmx.h, which usually lives in C:\Program Files (x86)\National Instruments\NI-DAQ\DAQmx ANSI C Dev\include.

Then,

julia> using Clang
julia> context = wrap_c.init()
julia> context.common_file = "common.jl"
julia> context.headers = ["NIDAQmx.h"]
julia> run(context)
$ mv NIDAQmx.h src/NIDAQmx_V<version>.h
$ mv NIDAQmx.jl src/functions_V<version>.jl
$ mv common.jl src/constants_V<version>.jl

The following manual edits are then necessary:

  • For NI-DAQmx v9.6.0 in NIDAQmx.h change defined(__linux__) to defined(__linux__) || defined(__APPLE__).
  • In constants_V<version>.jl
    • comment out const CVICALLBACK = CVICDECL,
    • in NI-DAQmx v17.1.0 comment out const CVIAbsoluteTime = VOID
    • change const bool32 = uInt32 to const bool32 = Bool32.
    • in NI-DAQmx v15.1.1 and greater comment out using Compat
  • In functions_V<version>.jl
    • globally search for Ptr and replace with Ref, then globally search for CallbackRef and replace with CallbackPtr.
    • globally search for Cstring and replace with SafeCstring
    • (for Julia 0.7 support) replace type with _type

Author

Ben Arthur, arthurb@hhmi.org
Scientific Computing
Janelia Research Campus
Howard Hughes Medical Institute

Picture

About

National Instruments Data Acquisition Interface

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages