How can I read the most recent scans from a data acquisition object during continuous background recording?

14 次查看(过去 30 天)
I’m recording voltage from microphones continuously in the background using an NI PXIe-1085 chassis and PXIe-4464 acquisition cards while changing the frequency, relative amplitude, and relative phase of voltages that are driving an array of loudspeakers. These drive voltages are also running continuously from a separate waveform generator (not part of the NI chassis).
In my code example below, I read "X" milliseconds of scans once during each frequency/amplitude/phase combination. The first read command reads the first acquired scan and each subsequent read command picks up with the scan where the previous left off, no matter how long the processing portion of the code takes. Instead, I’d like each read command to read the most recent “X” milliseconds of scans from the data acquisition object. How can I do that?
% Drive parameters
freq = 100:10:1000; % frequencies
dAmp = 0.1:0.1:1; % relative amplitudes
dPhase = 0:1:180; % relative phases
arb_volts = 0.5; % maxmum drive voltage
% Receive parameters
setting_time = 0.2; % seconds
num_cyc = 20; % number of cycles of the waveform to record
% setup arbitrary function generator and return instrument control object
arb = arbInitialize(arb_volts);
% Setup NI chassis and start acquiring data in the background
d = daq("ni");
ai0 = addinput(d, "PXI1Slot3", "ai0","Voltage");
% ai1 = addinput(...
% etc. for more channels
d.Rate = 200000;
% Start background acquisition
start(d, "continuous")
pause(0.1)
% loop over drive frequencies
for f = length(freq):-1:1
arbSetFreq(arb, freq(f))
record_duration = num_cyc / freq(f); % seconds
% loop over relative amplitudes and phases
for dA = 1:length(dAmp)
for dP = 1:length(dPhase)
arbSetAmpAndPhase(arb, arb_volts, dAmp(dA), dPhase(dP))
% measure voltages from microphones
pause(config.settling_time + record_duration)
data = read(d, seconds(record_duration));
% do some processing
% store outputs
end
% do some processing
end
% do some processing
end
% Write outputs

采纳的回答

Brahmadev
Brahmadev 2024-4-1
As per my understanding, you would like to read only the data from the past 'X' miliseconds no matter when the 'read' function is called. The 'read' function by default continues reading the datastore from the endpoint of the previous call. Suppose the last 'X' miliseconds translate to 'Y' number of samples. If 'Y' is less than the size of 'data', why cant we take a sub-set of the data to do processing on. If 'Y' is bigger than the size of data, we can resuse the previous values by storing it in a different data buffer.
You can refer to the following modified code with the data buffer logic. Please note that the size of the data buffer should be updated according to the size of the data you are receiving.
% Your existing setup code...
desired_duration_ms = 100; % Example: X milliseconds
% Calculate buffer size based on desired duration in milliseconds
buffer_size_samples = (desired_duration_ms / 1000) * d.Rate;
% Initialize buffer
dataBuffer = [];
% Start the loop execution...
% Inside your loop, where you want to read the most recent data
arbSetAmpAndPhase(arb, arb_volts, dAmp(dA), dPhase(dP))
% Measure voltages from microphones
pause(setting_time + record_duration)
newData = read(d, seconds(record_duration), "OutputFormat", "Matrix");
% Update the buffer with new data
dataBuffer = [dataBuffer; newData];
if size(dataBuffer, 1) > buffer_size_samples
% Remove oldest data to maintain buffer size
dataBuffer(1:size(dataBuffer, 1) - buffer_size_samples, :) = [];
end
% Use dataBuffer for processing instead of newData
% Process dataBuffer..
Another approach could be calling the "flush" function before calling "pause". It removes all acquired and queued scans in the input and output buffers of the DataAcquisition interface. You can refer to the following documentation fo more information:
Hope this helps in resolving your query!
  1 个评论
Will S
Will S 2024-5-23
Thank you for this response! I used this logic to get my code working, and I've done some modifications since based on an idea I got from your suggestion to use flush then pause. Matlab's flush command wouldn't work because I was using continuous background recording, but I made my own function that uses the pause+read+dataBuffer idea and operates like I hoped flush would have. What I realized in implementing your code was that reading from data acqusition object seems to erase the scans read from the data acqusition object. Therefore, reading all available data clears all the scans from the data acquisition object.
When implemented as a function below, the pause feature stopped working as a means to allow scans to accumulate for a set amount of time, so I replaced it with the while loop. The if/else statement is there so that if a continuous session is not running in the background when the function is called, it will still work, just duplicating what matlab's read function would do.
function data = readFromBuffer(d, num_samples, record_duration)
% This function pulls the available scans from a continuously running data
% acquisition session, trims and returns the most recent samples, and
% flushes the buffer
%
% Parameters
% ----------
% d : matlab Data Acquisition Object
% The data acqusition object from which to record scans. The data
% acquisition session must be running in the background for scans to be
% available to be read
% num_samples : integer
% The number of samples to record from the buffer, taken from the end
% of the buffer
%
% Returns
% -------
% data : timetable
% A timetable with the data read from the buffer. The number of columns
% will depend on the number of input channels in d
% while d.NumScansAvailable <= num_samples &
% pause(1/d.Rate) % pause one sample period
% end
% If acquisition is already running, take data from the buffer and trim it
% to the requested number of samples. Otherwise, start a temporary acquisition
% session and read the requested duration (seconds) of data.
if d.Running
% pause to allow the requested number of scans to accumulate, aka record
% duration to elapse
scan_baseline = d.NumScansAvailable;
while d.NumScansAvailable <= (scan_baseline + num_samples)
pause(1/d.Rate) % pause one sample period
end
% read available scans from the buffer, clearing the buffer
data = read(d, "all");
% trim the most recent num_samples from the data
data = data(end-num_samples:end, :);
% Shift the time vector from the arbitrary buffer scan time to start at
% zero
data.Time = data.Time - data.Time(1);
else
data = read(d, seconds(record_duration));
end
end

请先登录,再进行评论。

更多回答(0 个)

类别

Help CenterFile Exchange 中查找有关 Counter and Timer Input and Output 的更多信息

产品


版本

R2023b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by