Achieving 2 hertz data logging with Matlab serial - matlab

I am trying to use matlab for data acquisition with a licor820 instrument. The instrument outputs data at 2 hertz.
I have tried many different methods using infinite loops with asynchronous sampling (readasync) and timed readings but I am unable to get 2 hertz data. I am getting reads in the .51 s range. here are three examples of my methods. Any advice on what I may be doing wrong or how to properly sample at the highest frequency would be greatly appreciated!
example1: using readasync
tinit=tic; %initialization timer
s=serial('COM4') %,'InputBufferSize',40);
fopen(s)
while toc(tinit)<2 %allow time to initialize
end
while 1<2 %infinite loop for continuous sampling
readasync(s)
data=fscanf(s)
toc %allows me to see time between data acquisitions
tic
end
example 2: using bytes available.
My thinking here is to acquire data when I have the minimum amount of bytes necessary. Although I am unsure exactly how to determine how many bytes are necessary for my instrument, besides through visually looking at the data and narrowing it down to around 40 bytes:
while 1<2 %infinite loop for continuous sampling
if s.BytesAvailable >35
scandata=fscanf(s);
toc
tic
end
end
example 3: time forcing.
Since I need 2 hertz data my thinking here was to just force read the buffer every .49 seconds. The weird thing I see here is that it initially provides samples every .49 seconds, but while I monitor the bytes available at the port I see it steady dropping from 512 until it gets to 0 and then I stop getting .49 second samples. I guess I don't really understand how to use serial efficiently.
while 1<2 %infinite loop
if toc(t2)>=.49 %only sample after .49 seconds have passed
t2=tic; %reinitiate the tic for this forced time loop
bytes=s.BytesAvailable %to monitor how many bytes there are at the port
scandata=fscanf(s);
if ~isempty(scandata) && length(scandata)== 3 %checks for successful read
toc
tic
end
end
end
I feel there must be some way to sample completely in sync with the an instrument but I can't figure it out. Any help, suggestions, or ideas would be greatly appreciated! Thanks!

Dont rely on tic and toc. These functions use the time supplied by the OS calls. Mathworks claims to use high resolution timers, but do not rely on this! If you do not use a realtime OS these measurements are subject to unknown variation.
Sampling should be performed by realtime capable hardware. In your case I suspect that your sampling rate is actually controlled by your instrument. The output of the instrument is buffered by your serial interface. Therefore it seems to me that Matlab does not influence the sampling rate at all. (As long as the buffer does not overflow)
Try to acquire about 2000 samples or more and see how long it takes. Then divide the total time by the number of samples (-1) and compare this to the expected 0.5 s. If there is a difference, try adjusting the configuration of your instrument.

Related

Is there a way to do a millisecond level delay in matlab?

Help for 'PAUSE' says
PAUSE(n) pauses for n seconds before continuing, where n can also be a
fraction. The resolution of the clock is platform specific. Fractional
pauses of 0.01 seconds should be supported on most platforms.
But in my case pause(0.01) doesn't do anything at all (pause, pause(n) with whole number n works)
Is there any way to make a millisecond level delay (50 ms, 100 ~ 500 ms) delay in matlab?
Matlab version is
MATLAB Version 7.9.0.529 (R2009b)
64 bit on a Windows 10 64 bit Home edition
I see two options. Let's call them the looping option and the native option. The first is just using a while loop to check if your desired time to wait is already reached. You can do this with MATLAB's stopwatch timer tic and toc. This is the normal time (not the CPU-time). As you are writing a loop, which runs at maximum speed, you might encounter a high CPU-usage but this should be OK if it is only for a couple of milliseconds.
%% looping
% desired time to wait
dt_des = 0.001; % 1 ms
% initialize clock
t_st = tic;
% looping
while toc(t_st) < dt_des
end
% read clock
toc(t_st)
The native option is using pause (make sure that you have enabled it once with pause('on')). I assumed from your question that this does not always work -- however, it does on my system (R2019b, windows 10).
%% use pause()
tic
pause(0.001)
toc
The results of both are
Elapsed time is 0.001055 seconds.
Elapsed time is 0.001197 seconds.
It's not too accurate but you might get better results if you tune the numbers on your PC.
you can also use
java.lang.Thread.sleep(10);
if you are using an old matlab version, see discussion here.

faster way to add many large matrix in matlab

Say I have many (around 1000) large matrices (about 1000 by 1000) and I want to add them together element-wise. The very naive way is using a temp variable and accumulates in a loop. For example,
summ=0;
for ii=1:20
for jj=1:20
summ=summ+ rand(400);
end
end
After searching on the Internet for some while, someone said it's better to do with the help of sum(). For example,
sump=zeros(400,400,400);
count=0;
for ii=1:20
for j=1:20
count=count+1;
sump(:,:,count)=rand(400);
end
end
sum(sump,3);
However, after I tested two ways, the result is
Elapsed time is 0.780819 seconds.
Elapsed time is 1.085279 seconds.
which means the second method is even worse.
So I am just wondering if there any effective way to do addition? Assume that I am working on a computer with very large memory and a GTX 1080 (CUDA might be helpful but I don't know whether it's worthy to do so since communication also takes time.)
Thanks for your time! Any reply will be highly appreciated!.
The fastes way is to not use any loops in matlab at all.
In many cases, the internal functions of matlab all well optimized to use SIMD or other acceleration techniques.
An example for using the build in functionalities to create matrices of the desired size is X = rand(sz1,...,szN).
In your explicit case sum(rand(400,400,400),3) should give you then the fastest result.

fread() optimisation matlab

I am reading a 40 MB file with three different ways. But the first one is way faster than the 2 others. Do you guys have an idea why ? I would rather implement condition in loops or whiles to separate data than load everything with the first quick method and separate them then - memory saving -
LL=10000000;
fseek(fid,startbytes, 'bof');
%% Read all at once %%%%%%%%%%%%%%%%%%%%%%
tic
AA(:,1)=int32(fread(fid,LL,'int32'));
toc
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fseek(fid,startbytes,'bof');
%% Read all using WHILE loop %%%%%%%%%%%%%
tic
i=0;
AA2=int32(zeros(LL,1));
while i<LL
i=i+1;
AA2(i,1)=fread(fid,1,'int32');
end
toc
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
fseek(fid,startbytes,'bof');
%% Read all using FOR loop %%%%%%%%%%%%%%%
tic
AA3=int32(zeros(LL,1));
for i=1:LL
AA3(i,1)=fread(fid,1,'int32');
end
toc
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Elapsed time is 0.312916 seconds.
Elapsed time is 138.811520 seconds.
Elapsed time is 116.799286 seconds.
Here are my two cents on this:
Is the JIT accelerator enabled?
Since MATLAB is an interpreted language, for loops can be quite slow. while loops may be even slower, because the termination condition is re-evaluted in each iteration (unlike for loops that iterate a predetermined number of times). Nevertheless, this is not so true with JIT acceleration, which can significantly boost their performance.
I'm not near MATLAB at the moment, so I cannot reproduce this scenario myself, but you can check yourself whether you have JIT acceleration turned on by typing the following in the command window:
feature accel
If the result is 0 it means that it's disabled, and this is probably the reason for the huge reduction in performance.
Too many system calls?
I'm not familiar with the internals of fread, but I can only assume that one fread call to read the entire file invokes less system calls than multiple fread calls. System calls are usually expensive, so this can, to some extent, account for the slowdown.

Measuring Frequency of Square wave in MATLAB using USB 1024HLS

I'm trying to measure the frequency of a square wave which is read through a USB 1024 HLS Daq module through MATLAB. What I've done is create a loop which reads 100 values from the digitial input and that gives me vector of 0's and 1's. There is also a timer in this loop which measures the duration for which the loop runs.
After getting the vector, I then count the number of 1's and then use frequency = num_transitions/time to give me the frequency. However, this doesn't seem to work well :( I keep getting different frequencies for different number of iterations of the loop. Any suggestions?
I would suggest trying the following code:
vec = ...(the 100-element vector of digital values)...
dur = ...(the time required to collect the above vector)...
edges = find(diff(vec)); % Finds the indices of transitions between 0 and 1
period = 2*mean(diff(edges)); % Finds the mean period, in number of samples
frequency = 100/(dur*period);
First, the code finds the indices of the transitions from 0 to 1 or 1 to 0. Next, the differences between these indices are computed and averaged, giving the average duration (in number of samples) for the lengths of zeroes and ones. Multiplying this number by two then gives the average period (in number of samples) of the square wave. This number is then multiplied by dur/100 to get the period in whatever the time units of dur are (i.e. seconds, milliseconds, etc.). Taking the reciprocal then gives the average frequency.
One additional caveat: in order to get a good estimate of the frequency, you might have to make sure the 100 samples you collect contain at least a few repeated periods.
Functions of interest used above: DIFF, FIND, MEAN
First of all, you have to make sure that your 100 samples contain at least one full period of the signal, otherwise you'll get false results. You need a good compromise of sample rate (i.e. the more samples per period you have the better the measurement is) and and number of samples.
To be really precise, you should either have a timestamp associated with every measurement (as you usually can't be sure that you get equidistant time spacing in the for loop) or perhaps it's possible to switch your USB module in some "running" mode which doesn't only get one sample at a time but a complete waveform with fixed samplerate.
Concerning the calculation of the frequency, gnovice already pointed out the right way. If you have individual timestamps (in seconds), the following changes are necessary:
tst = ...(the timestamps associated with every sample)...
period = 2*mean(diff(tst(edges)));
frequency = 1/period;
I can't figure out the problem, but if the boolean vector were v then,
frequency = sum(v)/time_to_give_me_the_frequency
Based on your description, it doesn't sound like a problem with the software, UNLESS you are using the Windows system timer, which is notoriously inaccurate (it is only accurate to about 15 milliseconds).
There are high-resolution timers available in Windows, but I don't know how to use them in Matlab. If you have access to the .NET framework, the Stopwatch class has 1 microsecond accuracy (or better), as does the QueryPerformanceCounter API in Win32.
Other than that, you might have some jitter. There could be something in your signal chain that is causing false triggers, etc.
UPDATE: The following CodeProject article should solve the timing problem, if there is one. You should check the Matlab documentation of your version of Matlab to see if it has a native high-resolution timer. Otherwise, you can use this:
C++/Mex wrapper adds microsecond resolution timer to Matlab under WinXP
http://www.codeproject.com/KB/cpp/Matlab_Microsecond_Timer.aspx
mersenne31:
Thanks everyone for your responses. I have tried the solutions that gnovice and groovingandi mentioned and I'm sure they will work as soon as the timing issue is solved.
The code I've used is shown below:
for i=1:100 tic; value = getvalue(portCH); vector(i) = value(1); tst(i) = toc; % gets an individual time sample end
% to get the total time I put total_time = toc after the for loop
totaltime = sum(tst); edges = find(diff(vec)); % Finds the indices of transitions between 0 and 1 period = 2*mean(diff(edges)); % Finds the mean period, in number of samples frequency = 100/(totaltime*period);
The problem is that measuring the time for one sample doesn't really help because it is nearly the same for all samples. What is needed is, as groovingandi mentioned, some "running" mode which reads 100 samples for 3 seconds.
So something like for(3 seconds) and then we do the data capture. But I can't find anything like this. Is there any function in MATLAB that could do this?
This won't answer your question, but it's what I thought of after reading you question. square waves have infinite frequency. The FFT of a square wave it sin(x)/x, which goes from -inf to +inf.
Also try counting only the rising edges in matlab. You can quantize the signal to just +1 and 0, and then only increment the count when you see [0 1] slice of your vector.
OR
You can quantize, decimate, then just sum. This will only work if the each square pulse is the same length and your sampling frequency is constant. I think this one would be harder to do.

PWM/clock signal generation from a USB-1024HLS DAQ board

Is there an API function call for this board that would allow me to generate a clock signal on an output at 500 kHz while running some other code on the board? Thanks in advance for the advices.
According to the Supported Hardware documentation, version 2.8 or greater of the Data Acquisition Toolbox is needed to support a Measurement Computing USB-1024HLS device. Assuming you have version 2.8 or newer, the following should come close to a solution for you...
The first step would be to get the hardware ID for the device. The function DAQHWINFO should help with this:
deviceInfo = daqhwinfo('mcc');
The hardware ID gotten from the structure deviceInfo can then be used to create a Digital I/O Object (DIO) using the DIGITALIO function:
dio = digitalio('mcc',hardwareID);
Next, you have to add two output lines (for a clock signal and a pulse-width modulation (PWM) signal) using ADDLINE:
addline(dio,0:1,'out');
Then, you have to set a few DIO properties.
set(dio,'TimerPeriod',0.000002); % i.e. 500 kHz
set(dio,'TimerFcn',#update_outputs);
The function update_outputs is called once every timer period and should set the output pins to the appropriate values. The clock signal is simply going to switch back and forth between 0 and 1 every timer period. The PWM signal will likely alternate between 0 and 1 as well, but it will not change every timer period, remaining in each state for a set amount of time based upon the sort of pulse-width modulation you want. Here's what your update_outputs function may end up looking like:
function update_outputs(obj,event)
currentValues = getvalue(obj);
clockValue = ~currentValues(1);
pwmValue = pwm_compute();
putvalue(obj,[clockValue pwmValue]);
end
Note that this uses PUTVALUE and GETVALUE to set/get the values of the output pins. You will have to write the function pwm_compute such that it computes a new PWM value for each time period. Since pwm_compute will likely have to know how many values have been output already (i.e. how many times it has already been called), you can track that using a persistent variable:
function newValue = pwm_compute
persistent nValues;
if isempty(nValues)
nValues = 0;
else
nValues = nValues+1;
end
...
% Compute the new value for the (nValues+1) time period
...
end
This is just one possible solution. You could potentially precompute the PWM signal and pull the value for each timer period from a vector or data file, or you could potentially use the event data structure passed to update_outputs to get the time of the timer event (relative to the DIO timer start, I believe).
Finally, you have to start the DIO:
start(dio);
...and, once you're finished using it, delete it and clear it from memory:
delete(dio);
clear dio;
One potential stumbling block...
Generating a 500 kHz signal could be difficult. It's such a high frequency that you may run into problems, specifically with the 'TimerFcn', which is called once every timer period. If the 'TimerFcn' takes longer than 0.000002 seconds to run, some timer events may not be processed, leading to an output that is actually of a lower frequency. I have a feeling you may have to use a lower signal frequency for things to work properly, but I could be wrong. =)
I found Example: Generating Timer Events in the Data Acquisition Toolbox documentation.
dio = digitalio('nidaq','Dev1');
addline(dio,0:7,'in');
set(dio,'TimerFcn',#daqcallback)
set(dio,'TimerPeriod',5.0)
start(dio)
pause(11)
delete(dio)
clear dio