I would like to get peak value from STM32 adc samples. I have written the below code and I've managed to get peak value however most of the time this value includes the biggest noise. In order to eliminate noise effects, I have decided to apply averaging method. I would like to get 5 measurements' averages. Then I'd like to compare these averages and use the biggest one(biggest average). Can anybody suggest a code?
Regards,
Umut
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
ADC_raw = HAL_ADC_GetValue(hadc);
Vdd = 3.3 * (ADC_raw)/4095;
if (Vdd > Vmax)
{
Vmax = Vdd;
}
At first, I would remove as much code as possible from the Callback function because it is still inside the interrupt context which should be as short as possible. This is mentioned in a lot of answears here so I will not go into details on how to handle this.
For averaging the measurement, there are multiple ways you can go.
Automatic avarage
Use the ADCs oversampling function. The controller will sample the signal multiple times (configure using OVFS register) and calculate an average value before triggering the interrupt.
Manual average
Using the HAL_ADC_ConvCpltCallback function Store the numer of desired value into an array and calculate the average in the main loop.
Manual average using DMA
Let the DMA store the number of samples you want to use in an array using the function HAL_ADC_Start_DMA. When all samples have been collected you will be notified. This will reduce the processor load because you don't have to shift the data into the array yourself.
You can also combine the oversampling (most of the time a good idea) and one of the other methods depending on your Use-Case.
Related
Is there any way in Modelica, to reduce the sampeling during simulations? I have a DCDC converter with high frequency, consequently generating huge dataset. I am wondering, if there is any way to reduce the size of the dataset during simulation/exportation?
Thanks
Trying to create smaller dataset from models that generate huge ones (models with high frequencies).
Basically when you simulate, do not press the -> press the S on the toolbar and you get several tabs and the ones you care about are General and Output.
In General you can specify the number of intervals to reduce the data stored. It will only be stored at each interval.
In the Output you can say not to store events for example. You can also filter out variables that you are not interested in to reduce the result file size. Note that "Equidistant Time Grid" is activated by default, if not this would generate quite a lot of output maybe even several times per interval.
See more here about the things you have in General/Output:
https://openmodelica.org/doc/OpenModelicaUsersGuide/1.20/omedit.html#simulating-a-model
By including our desired variables in the Variable Filter in Output tab one can reduce the size of the output file without compromising the interval points. Each entry follows a POSIX EXTENDED regular expression format, say:
for a list of x,y,z it is ^x|y|z$. Another best practice could be unchecking the flag Store variables at Events. The best answer for this question is already answered by Adrian Pop.
Given a sequence of numbers that trend overtime, I would like to use Reactive Extensions to give an alert when there is a sudden absolute change spike or drop. i.e 101.2, 102.4, 101.4, 100.9, 95, 93, 85... and then increasing slowly back to 100.
The alert would be triggered on the drop from 100.9 to 95, each would have a timestamp looking for an an alert of the form:
LargeChange
TimeStamp
Distance
Percentage
I believe i need to start with Buffer(60, 1) for a 60 sample moving average (of a minute frequency between samples).
Whilst that would give the average value, I can't assign an arbitrary % to trigger the alert since this could vary from signal to signal - one may have more volatility that the other.
To get volatility I would then take a longer historical time frame Buffer(14, 1) (these would be 14 days of daily averages of the same signal).
I would then calculate the difference between each value in the buffer and the 14 day average, square and add all these deviations, and divide by the number of samples.
My questions are please:
How would I perform the above volatility calculation, or is it better to just do this outside of RX and update the new volatility value once daily external to the observable stream calculation (this may make more sense to avoid me having to run 14 days worth of 1 minute samples through it)?
How would we combine the fast moving average and volatility level (updated once per day) to give alerts? I am seeing Scan and DistinctUntilChanged on posts on SO, but cant work out how to put together.
I would start by breaking this down into steps. (For simplicity I'll assume the original data source is an observable called values.)
Convert values into a moving averages observable (we'll call this averages here).
Combine values and averages into an observable that can watch for "extremes".
For step 1, you may be able to use the built-in Window method that Slugart mentioned in a comment or the similar Buffer method. A Select call after the Window or Buffer can be used to process the array into a single average value object. Something like:
averages = values.Buffer(60, 1)
.Select((buffer) => { /* do average and std dev calcuation here */ });
If you need sliding windows, you may have to implement your own operator, but I could easily be unaware of one that does exist. Scan along with a queue seem like a good basis for such an operator if you need to write it.
For step 2, you will probably want to start with CombineLatest followed by a Where clause. Something like:
extremes = values.CombineLatest(averages, (v, a) => new { Current = v, Average = a })
.Where((value) = { /* check if value.Current is out of deviation from value.Average */ });
The nice part of this approach is that you can choose between having averages be computed directly from values in line like we did here or be some other source of volatility information with minimal effect on the rest of the code.
Note that the CombineLatest call may cause two subscriptions to values, one directly and one indirectly via a subscription to averages. If the underlying implementation of values makes this undesirable, use Publish and RefCount to get around this.
Also note that CombineLatest will output a value each time either values or averages outputs a value. This means that you will get two events every time averages updates, one for the values update and one for the averages update triggered by the value.
If you are using sliding windows, that would mean a double update on every value, and it would probably be better to simply include the current value on the Scan output and skip the CombineLatest altogether. You would have something like this instead:
averages = values.Scan((v) => { /* build sliding window and attach current value */ });
extremes = averages.Where((a) => { /* check if current value is out of deviation for the window */ });
Once you have extremes, you can subscribe to it and trigger your alerts.
I have some data which is time-stamped by a NMEA GPS string that I decode in order to obtain the single data point Year, Month, Day, etcetera.
The problem is is that in few occasions the GPS (probably due to some signal loss) goes boinks and it spits out very very wrong stuff. This generates spikes in the time-stamp data as you can see from the attached picture which plots the vector of Days as outputted by the GPS.
As you can see, the GPS data are generally well behaved, and the days go between 1 and 30/31 each month before falling back to 1 at the next month. In certain moments though, the GPS spits out a random day.
I tried all the standard MATLAB functions for despiking (such as medfilt1 and findpeaks), but either they are not suited to the task, either I do not know how to set them up properly.
My other idea was to loop over differences between adjacent elements, but the vector is so big that the computer cannot really handle it.
Is there any vectorized way to go down such a road and detect those spikes?
Thanks so much!
you need to filter your data using a simple low pass to get rid of the outliers:
windowSize = 5;
b = (1/windowSize)*ones(1,windowSize);
a = 1;
FILTERED_DATA = filter(b,a,YOUR_DATA);
just play a bit with the windowSize until you get the smoothness you want.
I Have a Sensor (Gyro) that connected to my python program (with socket UDP) and send data to python console in real-time but with 200 Hz frequency.
I want to change this frequency of coming data to my console but could not find a good way to do it.
I was thinking about doing it with filters like Mean an waiting for idea?
If you want to have regular updates, use a windowing mechanism. Take the last n values and store the average. Then, discard the next two values and take the last n values again. This example would yield values with a frequency of 200 Hz/2.
If you only want to see events when changes have occured, store the last value, compare the current value with the last one and emit an event if it has changed, updating the stored value. As you're dealing with sensors (and thus, a little fuzziness), you probably want to implement a hysteresis.
You can even raise the frequency by creating extra values in between the received ones through interpolation. For a steady frequency, you would have to take care about your timing though.
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