I'm having trouble getting consistent results with the code I am using. I want to run my Arduino for a specific amount of time (say 20 seconds) and collect data from the analog pin with a specific sampling rate (say four samples a second). The code is as follows.
a_pin = 0;
tic;
i = 0;
while toc < 20
i = i + 1;
time(i) = toc;
v(i) = a.analogRead(a_pin);
pause(.25);
end
Is there a way to set the loop to run a specific time and then in the loop sample at a different rate?
You can try this:
a_pin = 0;
fs = 4; % sampling frequency (samplings per second)
mt = 20; % time for measurements
ind = 1;
nind = 1;
last_beep = 0;
tic;
while toc < mt
time(ind) = toc;
v(ind) = a.analogRead(a_pin);
% wait for appropriate time for next measurement
while( nind == ind )
nind = floor(toc*fs) + 1;
end
ind = nind;
% beep every second
if (ceil(toc) > last_beep)
beep(); % don't know if this one exist, check docs
last_beep = ceil(toc);
end
end
Maximal sampling time for a single Arduino analog read command is around 0.04 s, in practice I'd go minimally 0.05. Adding two read operations is in the order of 2*0.04, in practice more like 0.1 s. I think it is mainly limited by the USB communication speeds.
I am also new at arduino, but having implemented a real time analysis for EEG using it, on practice, I was able to sample 2 analog channels with a samplinf frequency between 57 and 108Hz. It was very variable (calculated through tic/toc), but it is still proper for realtime processing in my case.
My code uses a While loop, a series of memory updates, digital pin manipulations, plot of trace (drawnow) and seems to run smoothly enough
My answer is simply here : 0.0283 sec for sampling 2 analog inputs in my case.
Cheers
Related
I'm attempting to create a reverb function in MATLAB and would like to filter the repeats to make them sound more natural. I have the signal processing toolbox however I get an error telling me that I cannot use the lowpass() function on single samples and MATLAB wigs out. The idea is to filter the signal every time it enters the feedback loop to simulate the absorption of high frequencies every time the sound reflects off a surface. My code looks like this:
[x,Fs] = audioread('Clap.mp3');
%Calculate delay length
secOfDelay = 0.03; %units of seconds
samplesOfDelay = secOfDelay * Fs; %sec * samples/sec = samples
%Gain for wet mix
g = 0.9;
for n = 1:length(x)
if (n - samplesOfDelay) < 1 %For no past samples, don't add the delay yet.
out(n,1) = x(n,1);
else
a(n,1) = x(n,1);
b(n,1) = x(n,1) + (g * b(n-samplesOfDelay,1));
end
end
%Add wet mix and dry mix together
out = a+b;
sound(out,Fs);
Any help would be greatly appreciated!
JDB
I am working on a project where i read real time current signal on an Arduino using a Split Core CT. I am able to read exact AC Current and replicate it in Arduino Serial Plotter using the code below.
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.println( (double)(analogRead(A5) - analogRead(A0))*0.009765625 );
}
But I have to do further calculations on it like FFT and THD, so I am sending that data to MATLAB over serial communication. Below is my Matlab Script which reads 1000 data samples, stores it in an array and performs calculations and finally plots it.
clc; close all;
if ~isempty(instrfind)
fclose(instrfind);
delete(instrfind);
end
s1=serial('COM5','Baudrate',115200);
fopen(s1);
Fs=1000;
LoS = 100;
T = 1/Fs;
t = (0:LoS-1)*T;
sig = zeros(1,LoS-1);
str='';
sen=0;
for j = 1:LoS
str=fscanf(s1);
sen=str2double(str);
sig(j)=sen;
end
subplot(2,1,1);
axis([0 LoS -4 4]);
plot(t,sig);
xlabel('Counts');
ylabel('Magnitude');
title('Signal');
Y=fft(sig);
P2 = abs(Y/LoS);
P1 = P2(1:LoS/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(LoS/2))/LoS;
subplot(2,1,2);
axis([0 100 0 10]);
plot(f,P1);
title('FFT(Signal)');
xlabel('f (Hz)');
ylabel('|Power(f)|');
fclose(s1);
delete(s1);
clear s1;
The issue is the frequency of actual signal is 60Hz, but my code outputs a peak at 31Hz. I checked the same code on matlab simulated sinusoids, it gives exact results. But on real data its miscalculating. I implemented the same logic on LABView as well the result remains 31Hz. Can anyone pinpoint my mistake? I am really stuck at this.
Thanks for your time.
You should fix the Arduino sample rate to 1000 samples per second.
As Daniel commented, the Arduino is working as fast as possible, instead of sampling at 1KHz (instead of sampling 1000 samples per second).
You can fix your Arduino loop to sample at 1KHz by making sure each iteration takes 1000 micro-seconds.
The loop below reads the time at the beginning and at the end of each iteration.
At the end of each iteration, there is a delay that completes the duration to 1000us.
void loop() {
unsigned long start_time_us, delta_time_us;
//Returns the number of microseconds since the Arduino board began running the current program
//https://www.arduino.cc/reference/en/language/functions/time/micros/
start_time_us = micros();
Serial.println( (double)(analogRead(A5) - analogRead(A0))*0.009765625 );
//Passed time in microseconds.
delta_time_us = micros() - start_time_us;
if (delta_time_us < 1000)
{
//Delay the remaining time - complete to 1000us (keep sample rate at 1000 samples per second).
//https://www.arduino.cc/reference/en/language/functions/time/delaymicroseconds/
delayMicroseconds((unsigned long)1000 - delta_time_us);
}
}
Please note: I could not verify the solution because I don't have an Arduino board (or simulator).
Remark: sending samples as text strings is inefficient, and may saturate the serial port.
I suggest you do the following:
Send binary data (using Serial.write instead of Serial.println).
Instead of converting the sample to double before sending it from Arduino, send the sample in short format (two bytes): Send the value (short)(analogRead(A5) - analogRead(A0)).
In the MATLAB side, read 1000 binary samples: sig = fread(s1, 1000, 'int16');
Perform the conversion to double and scale samples in MATLAB: sig = double(sig) * 0.009765625.
Arduino code:
//Serial.println( (double)(analogRead(A5) - analogRead(A0))*0.009765625 );
short analog_read = (short)(analogRead(A5) - analogRead(A0));
Serial.write((uint8_t*)&analog_read, 2); //Send two bytes in int16 binary format
MATLAB code:
% for j = 1:LoS
% str=fscanf(s1);
% sen=str2double(str);
% sig(j)=sen;
% end
sig = fread(s1, 1000, 'int16'); % Read 1000 analog samples (each sample is int16).
sig = double(sig) * 0.009765625; % Convert 1000 samples to physical values in double format
The above code is more efficient for both Arduino and MATLAB.
Remark: Keep in mind that I didn't test the code - it's just for demonstrating the concept.
I need to get 3 input audio stream to matlab simultaneously using 3 USB mics and find out the highest amplitude in real time.
this is the mics initialization
mic1= dsp.AudioRecorder('DeviceName','ÇáãíßÑæÝæä (10- USB PnP Sound Device)', 'SampleRate', 48000, 'NumChannels', 1);
mic2= dsp.AudioRecorder('DeviceName','ÇáãíßÑæÝæä (9- USB PnP Sound Device)', 'SampleRate', 48000, 'NumChannels', 1);
mic3= dsp.AudioRecorder('DeviceName','ÇáãíßÑæÝæä (8- USB PnP Sound Device)', 'SampleRate', 48000, 'NumChannels', 1);
frame1=step(mic1);
frame2=step(mic2);
frame3=step(mic3);
what are the next steps?
By examining your code, you have three audio signals with one channel each. If I understand what you want correctly, you want to find the highest sound made by any one signal overall over time. However, if I understand you correctly, you can't do this in real time as step for the AudioWriter can only capture one frame at a time. Because you're specifically trying to capture from all three audio devices, and the only way for you to capture audio is with step, you'll have to serially call step for each signal that you have.
Therefore, you'll need to capture all three audio signals separately, and then do your analysis. You'll also want to clip the sound after a certain point, so perhaps something like 5 seconds. Therefore, you'd do something like:
time_end = 5;
%// Capture audio signal 1
tic;
frame1 = [];
while toc < time_end
audio_in = step(mic1);
frame1 = [frame1; audio_in(:)];
end
%// Capture audio signal 2
tic;
frame2 = [];
while toc < time_end
audio_in = step(mic2);
frame2 = [frame2; audio_in(:)];
end
%// Capture audio signal 3
tic;
frame3 = [];
while toc < time_end
audio_in = step(mic3);
frame3 = [frame3; audio_in(:)];
end
After this point, because the sounds will probably all be uneven length, you'll want to zero pad all of them so they all match the same length. After this, it's a matter of first finding the maximum amplitude for each sample for all three signals, and then finding the maximum out of all of this.
I'm not quite sure how the signals are shaped... if they are row or column vectors, so let's just make sure they're all column vectors. Then, use max and operate along the columns and find the maximum for each point in time, then find the maximum out of all of these.
Therefore:
%// Find lengths for all three signals
l1 = numel(frame1);
l2 = numel(frame2);
l3 = numel(frame3);
max_length = max([l1, l2, l3]);
%// Zero pad signals to make same length
frame1_pad = zeros(max_length,1);
frame2_pad = zeros(max_length,1);
frame2_pad = zeros(max_length,1);
frame1_pad(1:l1) = frame1;
frame2_pad(1:l2) = frame2;
frame3_pad(1:l3) = frame3;
%// Find maximum among each sample
max_signal = max([frame1_pad, frame2_pad, frame3_pad], [], 1);
%// Find the maximum amplitude overall and location
[max_amplitude, loc] = max(max_signal);
max_amplitude will contain the highest point overall at a particular time point for each of the three signals, and loc will tell you the location in the array of where it was found. If you want to find the actual time it occured, simply take loc and multiply by your sampling time (1/48000). Bear in mind that loc will be 1-indexed instead of 0-indexed, and so you need to subtract by 1 before multiplying by the sampling rate.
Therefore:
time_it_happened = (loc-1)*(1/48000);
time_it_happened will contain that time which the highest amplitude happened.
Good luck!
Suppose you are collecting cards - your album is made of n_cards cards. Each pack you buy contains cards_in_pack cards, each card has the same probability to be extracted. How many packs do you need to buy in order to collect all the cards if you can't trade your doubles? Suppose you want to simulate the process. This is an obvious way to do it:
n_cards = 100; n_experiments = 1e4; cards_in_pack = 5;
cards = randi([1 n_cards], ceil(sqrt(n_cards)) * n_experiments * n_cards, 1, 'uint16');
tic
n_packs = zeros(n_experiments, 1);
ctrl1 = 1;
i_f = 0;
n = 0;
while ctrl1
ctrl2 = 1;
i1 = 0;
while ctrl2
i1 = i1 + 1;
ctrl2 = numel(unique(cards((cards_in_pack * i_f + 1):(cards_in_pack * (i_f + i1))))) ~= n_cards;
end
n = n + 1;
n_packs(n) = i1;
i_f = i_f + i1;
ctrl1 = n ~= n_experiments;
end
toc
% Average number of packs:
mean(n_packs)
% Distribution of the number of packs necessary to complete the album
hist(n_packs, 50)
% Number of cards needed in the experiments:
sum(n_packs) * cards_in_pack
This is very slow - is there a faster way to do it? Specifically: is there a fast way to calculate the cumulative count of unique values in Matlab?
The simulation can be vectorized across experiments. So the experiment loop is removed, and simulation time is greatly reduced.
Since each experiment may finish at different times (different number of packs required), an experiment can be in two states: ongoing or finished. The code maintains a vector of ongoing experiments (exps_ongoing) and a 0-1 matrix of obtained cards in each experiment (cards_obtained).
For each ongoing experiment, a new pack is generated and the cards contained in that pack are (over)written on cards_obtained. When all cards have been obtained for an ongoing experiment, that experiment is removed from exps_ongoing. The code ends when all experiments have finished.
n_cards = 100;
cards_in_pack = 5;
n_experiments = 1e4;
cards_obtained = zeros(n_cards,n_experiments);
%// will contain cards obtained in each experiment
exps_ongoing = 1:n_experiments; %// list of which experiments are ongoing
n_packs = zeros(1,n_experiments); %// will record how many packs have been used
while ~isempty(exps_ongoing)
n_packs(exps_ongoing) = n_packs(exps_ongoing) + 1;
%// pick one pack for each ongoing experiment
new_cards = randi(n_cards,cards_in_pack,numel(exps_ongoing));
%// generate pack contents for each ongoing experiment
cards_obtained(new_cards + repmat(((exps_ongoing)-1)*n_cards,cards_in_pack,1)) = true;
%// take note of obtained cards in each ongoing experiment. Linear indexing is used here
exps_ongoing = setdiff(exps_ongoing,exps_ongoing(all(cards_obtained(:,exps_ongoing))));
%// ongoing experiments for which all cards have been obtained are removed
end
disp(mean(n_packs))
For your input data, this reduces time by a factor of 50 on my computer (104.36 seconds
versus 1.89 seconds, measured with tic, toc).
OK, in this instance, it's pretty simple to simulate because the constraints are in our favour - all we need to know is when there are no cards left that we don't have. Thus we can dump the explicit uniqueness test and just count...
I'd do it something like this:
n_packs = zeros(n_experiments, 1, 'uint32');
for i=1:n_experiments
collection = zeros(n_cards, 1, 'uint32');
while nnz(collection) < n_cards
n_packs(i) = n_packs(i) + 1;
pack = randi(n_cards, cards_in_pack, 1, 'uint32');
collection(pack) = collection(pack) + 1;
end
end
Now I can't guarantee that that will be faster (I don't have Matlab with me to test it - there may be a bug or two as well), but It's about the simplest algorithm I can come up with, and simple code tends to be fast code. For maximum speed tweaking have a play with the data types - uint32 may not be optimal for everything due to Matlab's internals.
If all cards have equal probability, you can use this simple approach I took for a similar problem: open lots and lots of packs at once, and then check how many you would have needed to open.
n_cards = 100; n_experiments = 1e4; cards_in_pack = 5;
nPacks=zeros(n_experiments);
for i=1:n_experiments,
%# assume it is never going to take you >1500000 cards
r=randi(n_cards,1500000,1);
%# since R2013a, unique returns the first occurrence
%# for earlier versions, take the minimum of x
%# and subtract it from the total array length
[~,x]=unique(r);
nPacks(i,1)=ceil(max(x)/cards_in_pack);
end
%-------------------------------------------------------------------
% Function to Generate ECG of 1 heart beat signal
function [Heartbeat,t1] = ECG_Gen (HR,pulse_width,Amp)
Fs = 48000;
delay = ((60/HR)/2)-(0.5*pulse_width);
t1 = -delay:(1/Fs):delay;
Heartbeat = Amp*tripuls (t1,pulse_width);
%-------------------------------------------------------------------
%Test Circuit configuration
function [FECG_MECG,Mixed_ECG,fastTime] = Test_Circuit (FHR,MHR)
Fs = 48000;
%FHR = 150;
%MHR = 60;
Fpulse_width = 30e-3;
Mpulse_width = 60e-3;
FAmp = 0.2;
MAmp = 0.5;
% Fetal ECG Gen
%------------------------------------------------
[FECG,FHR_Delay]= ECG_Gen (FHR,Fpulse_width,FAmp);
% Maternal ECG Gen
%------------------------------------------------
[MECG,MHR_Delay]= ECG_Gen (MHR,Mpulse_width,MAmp);
% Composite signal implementation
%------------------------------------------------
% Set parameters for Composite signal Algorithms
if length (MECG) > length (FECG) % Check for time sequences for both ECG signal
slowECG = FECG; % Set interpolation to slower rate
fastECG = MECG;
timeSeg = length(MECG);
fastTime = MHR_Delay; % Set sampling times
slowTime = FHR_Delay;
else
slowECG = MECG;
fastECG = FECG;
timeSeg = length(FECG);
fastTime = FHR_Delay;
slowTime = MHR_Delay;
end
FECG_MECG = zeros (timeSeg,2); % To hold stereo output
FECG_MECG(:,2) = fastECG(1,:); % Assign higher rate signal to one channel
% Interpolation on the slower rater sampled ECG
slowECGInterp = interp1 (slowTime,slowECG,fastTime);
slowECG = num2cell(slowECGInterp); % Conversion to cell Array in order to remove NaN
slowECG(cellfun(#(slowECG) any(isnan(slowECG)),slowECG)) = [];
slowECG = cell2mat(slowECG);
j = 1;
for i = 1:timeSeg
FECG_MECG(i,1) = slowECG(1,j);
if j == length(slowECG)
j = 0;
end
j = j+1;
end
Mixed_ECG = FECG_MECG(:,1) + FECG_MECG(:,2); % to hold mono output
%while (1)
%sound(Mixed_ECG ,Fs);
%end
%-------------------------------------------------------------------
% Test Wave script
clear all
%clc
clc
Fs = 48000;
%for i = 1:3
%toc
MHR = 60;
FHR = 200;
% Obtain ECG interpolated signal and composite
[FECG_MECG,Mixed_ECG,fastTime] = Test_Circuit (FHR,MHR);
% for test purposes
[MECG,MHR_Delay]= ECG_Gen (60,60e-3,0.5);
%t = timer ('TimerFcn','stat=false','Period',2.0);
wavwrite (FECG_MECG(:,2),Fs,'ECGwav.wav');
i = 0;
a = 1;
tic
while (1)
while (toc < 20*a)
sound (MECG,Fs);
end
toc
a = a+1;
[MECG,MHR_Delay]= ECG_Gen (60*a,60e-3,0.5);
if a > 4
break
end
end
%start(t)
%tic
%t = cputime;
%y = wavread('ECGwav.wav');
%while (1)
% sound(y,Fs);
%end
%toc
Hey Thank you very much for getting back to me, I have made use of your interpolation but still have minor problems from the reading obtained from the Monitor. Fist of all, say I have a constant signal with fixed time period say, 0.8s and I want to add composite signal of say 0.3s, I have managed to use your interpolation method to sample the 0.3s signal at the rate of my 0.8s signal. I think I have solved this issue. Second issue deals with how to combine the two signals which I have managed to do somehow but when I use a while loop in order to repeat the composite signal say over 20s, the signals obtained from the sound output isn't quite what I expected since its sounding the array of stored composite signal which contain ( signal with 0.8s = slowInterp signal of 0.3s ). I have include all the codes and functions. Basically, I need the sound output in while loop to sync with the composite signal for example: if I have a signal that repeats every 1s, I would expect to hear a beep in a while loop that runs for 10s to produce 10 beeps, etc
It is hard to tell exactly what you need, but probably doing interpolation on the sequence that is sampled at the slower rate would work for your application.
If t1s are your times from your faster sequence, and t2s are times from your slower sequence, and slow is your slower sequence then do:
slowInterp = interp1(t2s, slow, t1s);
now you will have the sequency slow sampled at the faster rate.
This is only useful for displaying the time series. If you are doing any spectral analysis, this will introduce artifacts, but that is a more advanced topic.
Using the resample function in the signal processing toolbox could also be useful.