I'm trying to synchronize a set of signals including two analog outputs to a counter signal. This is using X-series PXIe-6356 DAQ cards. This counter signal is in turn gated by another counter signal. This would be an example of what the TTL (or LV-TTL) output looks like:
The red line is the gate signal (ctr2), the blue line is the timing signal (ctr0) and reflects time-to-digital conversion (TDC) pulses measured on the rising edge. The gate signal is required to be low every 10 s by a third-party device to do ... something. In this plot, the period of the gate is 3x the TDC period, plus 25 ns so the last rising edge shows up, although in practice the TDC will probably be ~1 MHz and the gate about 0.1 Hz. What I want to do, is also send out a two analog outputs (ai0:1) that change with each blue TDC rising edge. Importantly, the analog outputs are not repeated every gate pulse, I need to provide the entire analog output sequence, one for each TDC. In the below script I am just using random numbers to illustrate the requirements. This 6356 card has two simultaneous AOs.
from time import perf_counter as pc, sleep
import numpy as np
import nidaqmx as ni
from nidaqmx.constants import AcquisitionType, TaskMode, TimeUnits, Level, Edge
SLEEP_TIME = 0.001
SAFETY_FACTOR = 1.5
duty_cycle = 0.5
pulse_period = 1e-3 #
frequency = 1.0 / pulse_period
n_trigger = 8
n_gate = 8
tot_samples = (n_trigger + 1) * n_gate
scan_volts = 8.0
scan_max = 1.125 * scan_volts
coords = np.random.uniform(low=-scan_volts, high=scan_volts, size=(tot_samples, 2))
readout_period = 0.005
debounce_period = 25e-9
gate_high = pulse_period * n_trigger + debounce_period
gate_low = np.maximum(pulse_period, readout_period)
t_acquire = n_gate * (gate_high + gate_low)
parent_timing = ni.Task()
parent_gate = ni.Task()
parent_scan = ni.Task()
try:
parent_gate.co_channels.add_co_pulse_chan_time(
'dev1/ctr2',
units = TimeUnits.SECONDS,
idle_state = Level.LOW,
initial_delay = 0.0,
low_time = gate_low,
high_time = gate_high)
parent_gate.timing.ref_clk_src = 'PXIe_Clk100'
parent_gate.timing.ref_clk_rate = 100000000
parent_gate.timing.cfg_implicit_timing(
sample_mode = AcquisitionType.FINITE,
samps_per_chan = n_gate)
parent_gate.triggers.sync_type.MASTER = True
parent_timing.co_channels.add_co_pulse_chan_time(
'dev1/ctr0',
units=TimeUnits.SECONDS,
idle_state=Level.LOW,
initial_delay = 0.0,
low_time = (1.0 - duty_cycle) * pulse_period,
high_time = duty_cycle * pulse_period)
parent_timing.timing.ref_clk_src = 'PXIe_Clk100'
parent_timing.timing.ref_clk_rate = 100000000
parent_timing.timing.cfg_implicit_timing(
samps_per_chan=n_trigger + 1,
sample_mode=AcquisitionType.FINITE)
parent_timing.triggers.start_trigger.cfg_dig_edge_start_trig(
'Ctr2Source',
trigger_edge=Edge.RISING)
parent_timing.triggers.start_trigger.retriggerable = True
parent_scan.ao_channels.add_ao_voltage_chan(
'dev1/ao0:1',
max_val=scan_max,
min_val=-scan_max)
parent_scan.triggers.start_trigger.cfg_dig_edge_start_trig(
'dev1/ctr0source')
parent_scan.timing.cfg_samp_clk_timing(frequency,
source='dev1/ctr0source',
active_edge=Edge.RISING,
sample_mode=AcquisitionType.FINITE,
samps_per_chan=tot_samples)
'''
So what is valid here for source?
nidaqmx.errors.DaqError: Source terminal to be routed could not be found on the device.
Make sure the terminal name is valid for the specified device. Refer to Measurement & Automation Explorer for valid terminal names.
Property: DAQmx_SampClk_Src
Property: DAQmx_SampClk_ActiveEdge
Source Device: dev1
Source Terminal: dev1/ctr0internaloutput
Some of the ANSI C examples use PFI sources, but 'dev1/PFI12' generates the same error.
nidaqmx.errors.DaqError: Source terminal to be routed could not be found on the device.
Make sure the terminal name is valid for the specified device. Refer to Measurement & Automation Explorer for valid terminal names.
Property: DAQmx_SampClk_Src
Property: DAQmx_SampClk_ActiveEdge
Source Device: dev1
Source Terminal: dev1/PFI12
'''
transposed_coords = coords.T.copy()
parent_scan.write(transposed_coords, timeout=SAFETY_FACTOR * t_acquire, auto_start=False)
parent_scan.start()
parent_timing.start()
# Everything should start with this Task
parent_gate.start()
sleep(t_acquire)
while not parent_gate.is_task_done():
sleep(SLEEP_TIME)
# Close tasks
# -----------
finally:
parent_timing.close()
parent_gate.close()
parent_scan.close()
My main question is, what are valid ids for the `source` argument in `cfg_samp_clk_timing` (or `CfgSampClkTiming` in C parlance)? I've not been able to establish any acceptable strings for this input argument. I've taken a look at the ANSI C examples, which seem to be the most exhaustive, and there's no use cases of it for the analog outputs where the `sample` argument isn't the default sample clock. For the analog inputs, sometimes they specify by PFI, but if I try `dev1/PFI12` which corresponds to `ctr0` I get the same error as if I specify `dev1/Ctr0Source`:
Any thoughts? Using the latest DAQmx 2022 Q4 release.