02-29-2024 12:23 PM - edited 02-29-2024 12:55 PM
I want to highlight parts of that last post from Kavanakt7, emphasis added by me using red text.
@Kavanakt7 wrote:
The DAQmx Export Signal node I do not believe is doing what you want. You should probe the outputs of this or attach an indicator. I believe, you should be getting an empty string response. So you are effectively still just using onboard timing (I believe passing an empty string is acceptable, it just defaults to onboard clock). I would have expected your data to be misaligned, but I suppose that the start trigger is fixing your alignment in this instance. For your case, you want to use the DAQmx Timing Node and query the SampleClk.Term, this will output the appropriate clock and synchronize the timing. How you have it now, I am curious if you run over a long period of time, if the data drifts, this would confirm your timing sources are disconnected. (If you can run for a minute, I would expect you to see something by then). You could also introduce a large enough delay that is out of phase with your sine wave to where you can see the misalignment?
To the OP: Plan to take that advice. But first I'd also recommend that you try the suggested experiment of a longer run using the export signal method you posted, though I would suggest running quite a bit longer than a minute to help make any timing skew much more obvious. Whereas I previously mentioned a 3 msec / minute rule of thumb, that's basically a spec limit. The few times I've tried measuring such things in practice, the actual discrepancy was quite a bit less than that. So it might take a significantly longer run for the timing skew to show up in an unambiguous way. I'm thinking 30-60 minutes should be safe.
Please post back to let us know how that test goes. Please include both code and the resulting data from the test.
-Kevin P
[Edit: P.S. @Kavanakt7, regarding the empty string from the export node query. Sometimes when setting up signal routing I've found that queries for signal terminals don't "tell the truth" until after the task advances to the reserved or committed state. (Technically that's not quite fair, it's really that the *eventual* truth hasn't yet been determined.) So my typical actual habit is to commit tasks before doing such a query "just in case". But I thought I remembered that analog tasks didn't need it, and was trying to spare the OP from needing to deal with one more bit of sorta advanced DAQmx stuff. Does that node output change any if one first calls DAQmx Control Task to either "reserve" or "commit"?
Part two: I've found on some occasions when probing a DAQmx purple wire that displays as a string, the probe will show an empty string even though the functionality suggests that the DAQmx object wire wire is carrying additional invisible info behind the scenes and just not showing it in the probe. I suspect you're right that the OP's code using an export node to share the sample clock won't work but am still interested in the OP's experiment results to confirm the behavior. I'm just not 100% prepared to equate empty string to null contents, based on past experience with DAQmx wires and probes. Granted, a considerable bulk of that experience was 15+ years ago.]
02-29-2024 03:06 PM
Hi all,
You are right, the export signal indeed do return an empty string when probed (i also tried to commit the task before query, same results). This means that the only thing holding both of these tasks are trigger sync.
Following your advice in running a long acquisition (3 minutes total, x axis is time in seconds), I noticed that the signal drifts above the 1 minute mark, and even get worse after that. This is not an ideal scenario for me, as everything will be back to square one.
Regarding in your advice in syncing the sample clock terminal through timing property node, it seems that PXI-4496 doesn't have the sample clock terminal property, as it didn't appear in the property list (which make sense on why the export signal is empty). Attempting to sync it through the SampClk.Src property will give an error (attached below), which basically is the same error message as I written in message #1.
Am I out of luck in synchronizing these two tasks?
02-29-2024 03:37 PM - edited 02-29-2024 03:48 PM
You should still have a chance to synchronize this way. I am wondering if my DAQmx version and yours are creating a bit of a different lookup.
For now, lets just hardwire a string constant. Use "/PXI1Slot2/ai/SampleClock" into your slave DAQmx Timing Node's SampClk.Src.
The SampClk.Src that the master is showing I believe is correct, just not the full terminal name.
@Kevin_Price, you are absolutely correct in terms of querying the device. Committing the task will help finalize resources so that queries will return the most information.
[EDIT: I should expand on the SampClk.Src not being the full terminal name. This is likely outputting /PXI1Slot2/SampleClock instead of /PXI1Slot2/ai/SampleClock. In my version of DAQmx 17.1, I get both the Source and Terminal available to me. I am unsure why your version is not showing it. Anyways, when I simulate your hardware, going into NI MAX I can see the full terminal name we want is the one with the /ai/ in it.]
02-29-2024 04:07 PM
Just for clarification, the DAQmx Export Signal is outputting an empty string because it has not been told to export the SampClk.OutputTerm. If for instance, you made that a write terminal, and wrote "/PXI1Slot2/PXI_Trig0" to the node, it would export "/PXI1Slot2/ai/SampleClock" to the backplane "/PXI1Slot2/PXI_Trig0" location. From there, you could tell your slave device to Use "/PXI1Slot2/PXI_Trig0" or "/PXI1Slot[SlaveSlotNumberHere]/PXI_Trig0". This is how you would manually route the signal to your slave device.
Right now, if we can just tell the slave device specifically to go to "/PXI1Slot2/ai/SampleClock" then we let LabVIEW hardware optimize the backplane routing.
02-29-2024 04:13 PM
I don't have any specific familiarity with the 4496 device to draw on, but try this. Right-click on the yellow part of the Timing node and choose "Select Filter...", then choose "Show All Attributes".
I'm not sure if that'll help make the SampClk.Term property show up (or whether it will be valid to use for your specific device or your multi-device task), but it's something to try.
In case that doesn't work, I looked into your devices' datasheets a little bit. Unfortuntately, I didn't quickly identify a "nice" solution there. I'm not ruling out the possibility that there might be one, but the hypothetical approaches I tried to set up in my head didn't seem to pan out.
1. Sync both independent timebases to the PXI chassis.
The Synchronization section of the 4496 specs makes this sync sound automatic for those devices. But there's no mention of it for the 6723, and my long-ago memory of the 6733 makes me believe that it not only isn't automatic, there's actually no route available to bring the chassis clock into the AO card.
2. Generate an appropriate clock to be used as a timebase by both sets of cards.
But it isn't clear that the 6723 counters can generate arbitrary frequency pulse trains. I say arbitrary b/c the very little DSA experience I've had seems to demand a fairly small window of allowable timebase frequencies. However I didn't see anything in the 4496 specs to define that. I may have missed it though, I didn't spend a ton of time on this.
But besides all that, this would probably turn into quite a complicated approach. You'd probably need to configure each 4496 individually as a separate task in order to be allowed to be in control of this shared timebase.
3. Add another PXI device with counters and access to the chassis clock.
Then you could define a pulsetrain that depends on (and is thus sync'ed to) the chassis, and route its output onto one of the PXI_Trig lines. From there, the 6723 could use that pulsetrain as its SampClkTimebase.Source, and derive a sample clock from it.
This would get both sets of devices sync'ed to the chassis and thus sync'ed to each other with regard to any drift. Triggering could then sync their start times. The downside is that you might not be able to exactly match sample rates.
My main limited experience I've referred to involved syncing up output signals from several 6733's, a few DSA cards, and some other cards spread across 2 PXI chassis. Ultimately, I took manual control over the PXI_Trig bus so I could place timebase clocks, sample clocks and offset trigger pulses (to compensate for the DSA delays) that enabled me to get everything in sync.
It took a while to work the whole scheme out as I kept running into one little gotcha or another. But in the end it did the job. So I *think* there will be a solution for you, I'm just not quite sure what it will need to look like. Hopefully it can be simpler than the thing I came up with a long time ago. But at least you only have one output device and multiple inputs. That leaves more wiggle room for post-processing compensation and stuff. When it's all output signals, they hit the real world and there's no wiggle room at all.
-Kevin P
02-29-2024 04:26 PM
Hi,
I tried using the constant "/PXI1Slot2/ai/SampleClock" to route to the Slave DAQmx timing node, and it seemed to work! So for all the while, my code have been messed up by the DAQmx itself as it doesn't have the "ai" term (weird, I'm using LabView 2020).
Now, comparing the final 3 minutes run using the sample clock sync, I found that the sample sync are fairly consistent even up to 60 seconds. There is a 'small' drift at 110 seconds onwards, but it is much better than the one before.
Again, many thanks to both of you, you really help me a lot!
02-29-2024 09:37 PM - edited 02-29-2024 09:40 PM
Great news that you were able to share the sample clock by typing its name out explicitly!
However, you should see NO drift at all when both tasks are sync'ed to the same sample clock. There are reasons there could be some small phase offset, but if there is, it should stay constant over time rather than change.
Looking closer at your 3 graphs, the timescales seem pretty inconsistent. Assuming a constant frequency sine wave throughout the test time:
- In the first graph, 8 sine cycles takes ~400 msec, indicating a ~20 Hz sine
- In the second graph, 8 sine cycles takes ~80 msec, indicating a ~100 Hz sine
- In the third graph, 8 sine cycles takes ~10 msec, indicating a ~800 Hz sine
That's not what's really happening, is it? Until getting that inconsistency ironed out, I'm not sure how to interpret the apparent drift. It may indicate a flaw in your data handling that also needs attention. Can you post the whole vi? First please run it for a couple minutes with typical settings and when done, choose Edit->Make Current Values Default, and save.
-Kevin P
02-29-2024 09:56 PM
I agree with Kevin. Drift should not be occurring in the scheme we chose. Initial skew or delay could exist but would remain constant.
I'd recommend simplifying your code and testing the synchronization functionality independently. If you can just set up a single channel to acquire your AO data. Then modify your AO to produce a sine wave at a constant frequency. Keep this frequency rather low, say 10Hz. You can use some of the built in waveform generation VIs to generate this easily. I'd recommend then turning on regeneration for the AO task. This way you do not need to constantly write the data out.
I believe this should allow you to make sure data stays in sync, unaffected by filtering, and not impacted by other aspects of your code. Initial skew may not be determined by this method but we can look at that later if necessary.
03-01-2024 05:45 AM - edited 03-01-2024 05:46 AM
The graph you are seeing here is swept sine, and yes, I agree the part where I should use a pure sine tone to test the drift rather than the signal I'm going to use for my acquisition.
So I have re-did the whole thing for sample clock sync (I turned the trigger sync off) and perform a pure tone sine test for 3 minutes.
You are right, what I'm seeing here is the constant sample offset rather than the drift. In particular, the constant sample offset is 1, which is reasonable for my application.
03-01-2024 07:25 AM
That is much better and I am glad to see things working well.
In the article I shared earlier, Synchronization Explained , it makes mention of a vi, Get Full Terminal Name.vi. They do not really explain anything about the VI, just where to locate it. Well looking in the examples, Examples/Hardware Input and Output/DAQmx/Synchronization/Analog Input - Synchronization.vi they make use of this vi, and it conditions the terminal you desire to be the full terminal path.
To get away from hardcoding constants in your code, you can drop that VI inline and wire "ai/StartTrigger" and "ai/SampleClock" to the Local Terminal Name input to retrieve the full terminal name.
I believe that is the last thing I wanted to share, best of luck!