01-05-2023 04:35 AM - edited 01-05-2023 04:38 AM
Hi Kevin,
I did complete the labview core1 course. It helped with a lot of basic stuff, but counters and frequency were not covered. I've also not purchased Labview (yet) because I still hope I can get there with DaqExpress (free version)
I've worked on a for-loop with the inverse of frequency to make an array of accumulative periods, then tried to add the frequency measurement, after converting it to a feed-rate, into the array. I'm getting an error "object specified cannot be found in storage" that I'm still trying to solve. But otherwise, does this look like the right direction? Before writing the for-loop I did get the arm-start trigger working.
Thanks again
01-05-2023 08:21 AM
I'm not familiar with either DAQExpress or LabVIEW NXG. I wouldn't count on DAQExpress being able to support the ability to set up the low-level sync & timing signals needed to keep your freq and analog measurements correlated in time.
The NXG code looks awfully similar to the regular LabVIEW code you posted almost a year ago. Under regular LabVIEW, I would use a call to the regular DAQmx Timing square icon function rather than a property node. That function both configures sample clock timing *and* allocates a task buffer for acquisition. Your property node usage probably doesn't configure the task as completely as a call to the regular DAQmx Timing function.
Your data acq device and the DAQmx driver have the capability to do what you need, but I only know the specifics about how to configure and run it from "classic" LabVIEW. There are API's for DAQmx that support free languages like C and Python, but I don't have detailed knowledge about either.
-Kevin P
04-21-2023 08:11 AM
Hi Kevin,
just popping in for my annual post. just kidding.
I've been using this "WFS and Motor Speed" program based your original program for plotting frequency on an XY graph. It works. I can graph wire speed from the encoder channel and one analog signal with its own timestamp along a single time axis using excel. Because the counter is implicit (and varies from test to test,) I will adjust the rate of the sample clock timing node so that it records analog channel at roughly similar rates to the counter. My plots then have similar lengths. Is there any reason not to do this? Also what is the reason for reading at one quarter the sampling rate? Also, what is the significance of the sequence structure?
The "Torque and Speed" program is a similar program which so far, has more analog inputs. I don't have my torque sensor yet, but it will be used here to record output torque and speed from the motor along with the controller data and temperatures. While testing this, I've been getting rows with zeros, both in the times and the values but only counter or analog readings separately. Does this have to do with the sample rate or read rate? I suspect you might say something like lossy queue. I set the rate at 40hz in the last test, which was similar to the counters implicit rate, which seems relatively low to me.
Also, in addition to the 3 methods which you outlined previously. I've read where some have used a counter output wired to one of the counter inputs used to set the sample timing. Would this be worth trying? The advantage for me would be that everything could be on one fixed sample clock... maybe...no?
Best Regards,
04-22-2023 04:54 PM
My example that you linked wasn't meant to be a finished app. It was a fairly minimal mod of someone else's code that never tried to combine the data from the two tasks or write it to file. (More on this below).
1. Changing the AI sample rate to approximately match the encoder frequency isn't necessarily all that helpful, not really. You can't count on matching *exactly*, plus the encoder freq is probably variable. Your sample rate should be based mainly on capturing the signal dynamics that matter in your app.
2. Reading at 1/4 the sample rate is just a convenience. With no other wait states in the loop, timing is driven by the DAQmx task at 1/4 second per iteration. Updating a chart 4 times a second is more than fast enough for any human brain to observe and interpret.
3. The sequence structure in my example made sure that the code would first wait until 1/4 second of data could be read from the AI task and *then* read "all available" samples from the counter task without further waiting. You changed things so the reads can happen in parallel, likely returning counter data *before* waiting for 1/4 second of AI data to accumulate.
4. Your "rows of zeros" can be traced to the fact that you combine the AI and the CTR data into a 2D array. However, they usually won't have the same # of samples per iteration so the shorter one will get padded with zeros every iteration. And this 2D array packaging in turn is driven by your use of the simple "Write Delimited Spreadsheet" function.
A much better approach is to use the TDMS file functions. Then you can write the data as distinct "channels" and the different #'s of samples per iteration won't matter. Note that for counter frequency measurement, you should make both a frequency channel and a frequency_timestamp channel since the timing is likely to be variable. AI data can be written as a waveform where the timing information is already embedded.
5. The idea of having fixed, non-varying sample timing for the counter task is what I described as the "easy approach" up in msg #4 of this thread. But it also comes with the caveats I described up there. To get less than 1% quantization error would require an encoder frequency that's more than 100x the AI sample clock rate. (If encoder freq <= AI sample rate, you'd have to contend with >=100% quantization error!) So basically, the various approaches have distinct pros and cons you have to trade off.
-Kevin P
06-02-2023 08:06 AM
Hi Kevin,
1) I don't need to exactly match, but if they are similar in length it is easier to visualize and draw correlations between two measurments.
2) ok
3) why is it "good" to read AI data before counter data?
4) "rows of zeros" due to different numbers of samples, ok got it. I setup the program to read 3 AI waveforms, run calculations separately, combine and write to TDMS file. This works well, and I have spent some time running different trials and just basically enjoying that it works. I would like to add wire speed acquired through the encoder frequency, which you had previously help me with. I have a time channel based on "get date and time function"- I suspect this has nothing to do with frequency_timestamp. I tried the "build waveform" function by using the period as dt and WFS calculated value as Y but didn't get meaningful data. How do I get frequency_timestamp and frequency to the TDMS? I always get data-type problems when building arrays. Similarly when I build the array of waveforms, if I build the array with DBL as first index, the other channels become meaningless. I cannot add the array of DBL to the build array function when waveform is the first index.
5) OK i see. If I can't get this method working, that is essentially becomes a big con. which makes the "easy" method more attractive:)
cheers
06-02-2023 08:35 AM
By Jove, I've got something working using the build waveform function. At first glance it appears plausible. I need to figure out how to view all channels at once to see how they match up.
06-04-2023 10:38 PM
3). It's good in the example program because it helps make the chunks of data you read each loop iteration represent more nearly the same time window.
Loop timing will be governed by the AI read(s), which will generally have to wait to accumulate the requested # of samples. Once all available, you'll get that most recent chunk which will reprsent the time period since your previous read. But it's *also* the case that the counter task is buffering up data in the background as you wait for your AI read to complete. By waiting until *after* the AI read is done before you read "all available samples" from your counter task, the counter data will represent pretty nearly the same time period.
4). You really can't represent your counter frequency data with a waveform datatype. Waveforms are for data with a constant "dt" interval between samples which simply won't be true for your frequency data.
Here's what to do:
You're already accumulating an array of complex data with cumulative time as the Real part and Frequency as the Imaginary part. Since you seem comfortable with representing time and frequency as a complex number, try making a second call to TDMS where you wire in your complex array as the data. I'd recommend also wiring in a string like "time_freq" to the "channel name" terminal of TDMS Write.
(I'm not 100% sure TDMS accepts complex data. If not, you can break it back into separate DBL arrays, 1 each for time and for frequency, and use 2 calls to TDMS Write to store them. Again, with identifying channel names.)
-Kevin P
06-15-2023 04:32 AM
Hi Kevin,
Thanks for your instructions. I think I have some usefull data using two writes; I don't think it understood the complex numbers. In the pdf you can see both trials.
So now I believe I have a "time" channel matching the frequency measurements. It appears as a linear slope. Is the best way to visualize the frequency vs time along with the other channel data vs their time by exporting to excel and making the appropriate charts there?
I also experimented with using the "easy" approach via edge counting per time period where I used a wait function to provide the time period. I think there is something fundamentally wrong with my logic. Does anything stand out? The expected count frequency is 6 Hz, but I measure 1 count per 4 seconds. I can increase the encoder resolution, but didn't want to make the effort until the program looked like it was working.
Many thanks for your help and patience.
06-22-2023 04:40 PM
The TDMS file format spec sure seems to suggest that complex numbers should be supported, but since you worked out the method to write time & freq as distinct DBL channels there's also no need to spend time going back to the complex method. (But briefly: note that in the complex method you kept writing the full history of values every iteration as you accumulated it. Each write includes history-to-that-point plus the new data. I'm away from LV now, but wouldn't rule out the possibility that complex values are being written correctly but the simple file viewer converts them to just the real part when reading them back.)
I see several issues with your "easy" approach, among them:
- your 250 msec wait isn't doing anything here because your AI task is the pacing item in your loop. Reading 250 samples per iteration from a task with a 250 Hz sample rate will set your iteration pace at 1 per sec.
- however, your CTR task uses the same sample clock but only reads 1 single sample out of the buffer per iteration. So the CTR task buffer is building up almost a full second worth of backlog per iteration. Default buffer size for a 250 Hz sample rate is 10000 samples, or 40 seconds worth. During those 40 seconds, you'll only have retrieved 40 samples from the CTR task buffer, leaving a 9960 sample backlog behind. On the next iteration after that, you'll get an unrecoverable buffer overflow error from your CTR task.
- it's unclear why you want to write both the old value from the shift register and also the new value that's about to be put into that shift register. Other than the first and last iterations, every other piece of data is going to be duplicated.
- I'm fairly suspicious of the calculation that divides by the 250 msec constant. Does the other scaling account for that being milli-sec rather than full seconds? I see the factor of 60 that suggests to me a conversion to RPM...
- depending on your trigger source and when it asserts, you might not have the 2 tasks fully sync'ed
-Kevin P