@@ -178,6 +178,7 @@ def wrapped2(name):
178178DAQmxGetDevAIMaxMultiChanRate = float64_prop (PyDAQmx .DAQmxGetDevAIMaxMultiChanRate )
179179DAQmxGetDevAOVoltageRngs = float64_array_prop (PyDAQmx .DAQmxGetDevAOVoltageRngs )
180180DAQmxGetDevAIVoltageRngs = float64_array_prop (PyDAQmx .DAQmxGetDevAIVoltageRngs )
181+ DAQmxGetPhysicalChanAITermCfgs = int32_prop (PyDAQmx .DAQmxGetPhysicalChanAITermCfgs )
181182
182183
183184def port_supports_buffered (device_name , port , clock_terminal = None ):
@@ -252,8 +253,13 @@ def AI_start_delay(device_name):
252253 Vmin , Vmax = DAQmxGetDevAIVoltageRngs (device_name )[0 :2 ]
253254 num_samples = 1000
254255 chan = device_name + '/ai0'
256+ supp_types = DAQmxGetPhysicalChanAITermCfgs (chan )
257+ if supp_types & c .DAQmx_Val_Bit_TermCfg_RSE :
258+ input_type = c .DAQmx_Val_RSE
259+ elif supp_types & c .DAQmx_Val_Bit_TermCfg_Diff :
260+ input_type = c .DAQmx_Val_Diff
255261 task .CreateAIVoltageChan (
256- chan , "" , c . DAQmx_Val_RSE , Vmin , Vmax , c .DAQmx_Val_Volts , None
262+ chan , "" , input_type , Vmin , Vmax , c .DAQmx_Val_Volts , None
257263 )
258264 task .CfgSampClkTiming (
259265 "" , rate , c .DAQmx_Val_Rising , c .DAQmx_Val_ContSamps , num_samples
@@ -265,7 +271,12 @@ def AI_start_delay(device_name):
265271 sample_timebase_rate = float64 ()
266272
267273 task .GetStartTrigDelay (start_trig_delay )
268- task .GetDelayFromSampClkDelay (delay_from_sample_clock )
274+ try :
275+ task .GetDelayFromSampClkDelay (delay_from_sample_clock )
276+ except PyDAQmx .DAQmxFunctions .AttributeNotSupportedInTaskContextError :
277+ # seems simultaneous sampling devices do not have this property,
278+ # so assume it is zero
279+ delay_from_sample_clock .value = 0
269280 task .GetSampClkTimebaseRate (sample_timebase_rate )
270281
271282 task .ClearTask ()
@@ -275,6 +286,26 @@ def AI_start_delay(device_name):
275286 return total_delay_in_seconds
276287
277288
289+ def supported_AI_terminal_configurations (device_name ):
290+ """Determine which analong input configurations are supported for each AI.
291+
292+ Valid options are RSE, NRSE, Diff, and PseudoDiff.
293+ The labscript driver only supports RSE, NRSE, and Diff.
294+ """
295+ supp_types = {}
296+ poss_types = {'RSE' : c .DAQmx_Val_Bit_TermCfg_RSE ,
297+ 'NRSE' : c .DAQmx_Val_Bit_TermCfg_NRSE ,
298+ 'Diff' : c .DAQmx_Val_Bit_TermCfg_Diff ,
299+ 'PseudoDiff' : c .DAQmx_Val_Bit_TermCfg_PseudoDIFF }
300+ chans = DAQmxGetDevAIPhysicalChans (device_name )
301+ for chan in chans :
302+ byte = DAQmxGetPhysicalChanAITermCfgs (device_name + '/' + chan )
303+ chan_types = [key for key , val in poss_types .items () if val & byte ]
304+ supp_types [chan ] = chan_types
305+
306+ return supp_types
307+
308+
278309def supported_AI_ranges_for_non_differential_input (device_name , AI_ranges ):
279310 """Empirically determine the analog input voltage ranges for non-differential inputs.
280311
@@ -403,7 +434,8 @@ def get_min_semiperiod_measurement(device_name):
403434
404435 models = []
405436 for name in DAQmxGetSysDevNames ().split (', ' ):
406- model = DAQmxGetDevProductType (name )
437+ # ignore extra details in model names
438+ model = DAQmxGetDevProductType (name ).split (' ' )[0 ]
407439 print ("found device:" , name , model )
408440 if model not in models :
409441 models .append (model )
@@ -495,6 +527,120 @@ def get_min_semiperiod_measurement(device_name):
495527 capabilities [model ]["AI_range" ] = [Vmin , Vmax ]
496528 else :
497529 capabilities [model ]["AI_range" ] = None
530+ capabilities = json .load (f )
531+ except ValueError :
532+ pass
533+
534+
535+ models = []
536+ for name in DAQmxGetSysDevNames ().split (', ' ):
537+ # ignore extra details in model names
538+ model = DAQmxGetDevProductType (name ).split (' ' )[0 ]
539+ print ("found device:" , name , model )
540+ if model not in models :
541+ models .append (model )
542+ capabilities [model ] = {}
543+ try :
544+ capabilities [model ]["supports_buffered_AO" ] = DAQmxGetDevAOSampClkSupported (
545+ name
546+ )
547+ except PyDAQmx .DAQmxFunctions .AttrNotSupportedError :
548+ capabilities [model ]["supports_buffered_AO" ] = False
549+ try :
550+ capabilities [model ]["max_DO_sample_rate" ] = DAQmxGetDevDOMaxRate (name )
551+ capabilities [model ]["supports_buffered_DO" ] = True
552+ except PyDAQmx .DAQmxFunctions .AttrNotSupportedError :
553+ capabilities [model ]["max_DO_sample_rate" ] = None
554+ capabilities [model ]["supports_buffered_DO" ] = False
555+ if capabilities [model ]["supports_buffered_AO" ]:
556+ capabilities [model ]["max_AO_sample_rate" ] = DAQmxGetDevAOMaxRate (name )
557+ else :
558+ capabilities [model ]["max_AO_sample_rate" ] = None
559+
560+ capabilities [model ]["num_AO" ] = len (DAQmxGetDevAOPhysicalChans (name ))
561+ capabilities [model ]["num_AI" ] = len (DAQmxGetDevAIPhysicalChans (name ))
562+ if capabilities [model ]["num_AI" ] > 0 :
563+ single_rate = DAQmxGetDevAIMaxSingleChanRate (name )
564+ multi_rate = DAQmxGetDevAIMaxMultiChanRate (name )
565+ else :
566+ single_rate = None
567+ multi_rate = None
568+ capabilities [model ]["max_AI_single_chan_rate" ] = single_rate
569+ capabilities [model ]["max_AI_multi_chan_rate" ] = multi_rate
570+ if capabilities [model ]["num_AI" ] > 0 :
571+ capabilities [model ]["AI_term_cfg" ] = supported_AI_terminal_configurations (name )
572+ cfgs = [item for sublist in capabilities [model ]["AI_term_cfg" ].values ()
573+ for item in sublist ]
574+ capabilities [model ]["num_AI_Diff" ] = cfgs .count ('Diff' )
575+ capabilities [model ]["num_AI_RSE" ] = cfgs .count ('RSE' )
576+ else :
577+ capabilities [model ]["AI_term_cfg" ] = None
578+
579+ capabilities [model ]["ports" ] = {}
580+ ports = DAQmxGetDevDOPorts (name )
581+ chans = DAQmxGetDevDOLines (name )
582+ for port in ports :
583+ if '_' in port :
584+ # Ignore the alternate port names such as 'port0_32' that allow using two or
585+ # more ports together as a single, larger one:
586+ continue
587+ port_info = {}
588+ capabilities [model ]["ports" ][port ] = port_info
589+ port_chans = [chan for chan in chans if chan .split ('/' )[0 ] == port ]
590+ port_info ['num_lines' ] = len (port_chans )
591+ if capabilities [model ]["supports_buffered_DO" ]:
592+ port_info ['supports_buffered' ] = port_supports_buffered (name , port )
593+ else :
594+ port_info ['supports_buffered' ] = False
595+
596+ capabilities [model ]["num_CI" ] = len (DAQmxGetDevCIPhysicalChans (name ))
597+ supports_semiperiod = supports_semiperiod_measurement (name )
598+ capabilities [model ]["supports_semiperiod_measurement" ] = supports_semiperiod
599+ if capabilities [model ]["num_CI" ] > 0 and supports_semiperiod :
600+ min_semiperiod_measurement = get_min_semiperiod_measurement (name )
601+ else :
602+ min_semiperiod_measurement = None
603+ capabilities [model ]["min_semiperiod_measurement" ] = min_semiperiod_measurement
604+
605+ if capabilities [model ]['num_AO' ] > 0 :
606+ AO_ranges = []
607+ raw_limits = DAQmxGetDevAOVoltageRngs (name )
608+ for i in range (0 , len (raw_limits ), 2 ):
609+ Vmin , Vmax = raw_limits [i ], raw_limits [i + 1 ]
610+ AO_ranges .append ([Vmin , Vmax ])
611+ # Find range with the largest maximum voltage and use that:
612+ Vmin , Vmax = max (AO_ranges , key = lambda range : range [1 ])
613+ # Confirm that no other range has a voltage lower than Vmin,
614+ # since if it does, this violates our assumptions and things might not
615+ # be as simple as having a single range:
616+ assert min (AO_ranges )[0 ] >= Vmin
617+ capabilities [model ]["AO_range" ] = [Vmin , Vmax ]
618+ else :
619+ capabilities [model ]["AO_range" ] = None
620+
621+ if capabilities [model ]['num_AI' ] > 0 :
622+ AI_ranges = []
623+ raw_limits = DAQmxGetDevAIVoltageRngs (name )
624+ for i in range (0 , len (raw_limits ), 2 ):
625+ Vmin , Vmax = raw_limits [i ], raw_limits [i + 1 ]
626+ AI_ranges .append ([Vmin , Vmax ])
627+ # Find range with the largest maximum voltage and use that:
628+ Vmin_raw , Vmax_raw = max (AI_ranges , key = lambda range : range [1 ])
629+ # Confirm that no other range has a voltage lower than Vmin,
630+ # since if it does, this violates our assumptions and things might not
631+ # be as simple as having a single range:
632+ assert min (AI_ranges )[0 ] >= Vmin_raw
633+ capabilities [model ]["AI_range_Diff" ] = [Vmin_raw , Vmax_raw ]
634+ if 'RSE' in capabilities [model ]["AI_term_cfg" ]['ai0' ]:
635+ # Now limit to non-differential inputs (if available), which may have lower ranges
636+ AI_ranges = supported_AI_ranges_for_non_differential_input (name , AI_ranges )
637+ # Find RSE range with the largest maximum voltage and use that:
638+ Vmin , Vmax = max (AI_ranges , key = lambda range : range [1 ])
639+ assert min (AI_ranges )[0 ] >= Vmin
640+ capabilities [model ]["AI_range" ] = [Vmin , Vmax ]
641+ else :
642+ capabilities [model ]["AI_range" ] = None
643+ capabilities [model ]["AI_range_Diff" ] = None
498644
499645 if capabilities [model ]["num_AI" ] > 0 :
500646 capabilities [model ]["AI_start_delay" ] = AI_start_delay (name )
0 commit comments