compute the power level of ambiant noise using mems microphones - database

I' m using digital microphone (MEMS) on STM32 discovery board Fto record ambiant noise, and I need to know how to convert the samples amplitude to power level in dB SPL.
the microphone that I 'm using is the MP45DT02, in the datasheet the sensibility of the mic is -26 dBFS and the SNR is 61 dB

What the senstivity means is that a 94 dBSPL sine input will give a -26dBFS peak output level, or a -29 dBFS rms output level. Unless your signal is a sine wave it's probably makes more sense to use rms.
Measure the RMS of the signal.
Convert the RMS value to dBFS (dBFS = 20*log10(rms)))
Convert the dBFS value to dBSPL using the relationship of -29 dBFS = 94 dBSPL. For example, if you measured -50 dBFS then you are 21 dB down from -29. 94-21 = 83 dBSPL and there's your answer.

Related

How can I convert a duration/timer to hex?

I've come across a screenshot of a certain software, showing the a device's life timer like this: "96B80300 <> 000067:44". The software is not open source, it's an unofficial tool for older Nokia devices.
My question is, how do you get from "000067:44" to the "96B80300" hex value?
I am trying to change a similar timer on another device (uses only 7 digits though, like "00000:00"), and this info could be really helpful.
I can give a possible indication. If 67:44 represents hours and minutes, then the number of seconds in hex would be 67 * 60 * 60 + 44 * 60 = 243840 => 0x03B880, which allowing for big/little/whatever endian, means that it is missing 0x96 - 0x80 = 0x16 or 22 seconds. Perhaps the seconds are just not being displayed in decimal.

Arbitrary waveform generator (How does the input of AWG affects the output)

How should one choose the sampled input for AWG so that it gives a waveform similar to the ideal waveform?
I have a data set for voltage signal sampled over time of 1 ns, which I am entering as an input for the AWG function which resamples the data at 0.05 ns and gives a waveform. There is a distortion in the AWG waveform with respect to the ideal one. For AWG, Vout(t) =∑ Vi h(𝑡 - 𝑖 ∆𝑡), where h is the impulse response function of the instrument.
It is given in the question that it is possible to correct for the distortion by transforming input Vi in a special way.

What is the correct method to upsample?

I have an array of samples at 75 Hz, and I want to store them at 128 Hz. If it was 64 Hz and 128 Hz it was very simple, I would just double all samples. But what is the correct way if the samplerates are not a fraction of eachother?
When you want to avoid Filtering then you can:
handle signal as set of joined interpolation cubics curves
but this point is the same as if you use linear interpolation. Without knowing something more about your signal and purpose you can not construct valid coefficients (without damaging signal accuracy) for example of how to construct such cubic look here:
my interpolation cubic
in bullet #3 inside that link are coefficients I use. I think there are sufficient even for your purpose so you can try them. If you want to do custom interpolation look here:
how to construct custom interpolation curve
create function that can return point in your signal given time from start of sampling
so do something like
double signal(double time);
where time is time in [s] from start of sampling. Inside this function compute which 4 samples you need to access.
ix = floor(time*75.0);
gives you curve start point sample index. Cubic need 4 points one before curve and one after ... so for interpolation cubic points p0,p1,p2,p3 use samples ix-1,ix,ix+1,ix+2. Compute cubic coefficients a0,a1,a2,a3 and compute cubic curve parameter t (I use range <0,1>) so
t=(time*75.0); t-=floor(t);
green - actual curve segment
aqua - actual curve segment control points = 75.0 Hz samples
red - curve parametric interpolation parameter t
gray - actual time
sorry I forgot to draw the actual output signal point it should be the intersection of green and gray
simply do for loop through sampled data with time step 1/128 s
something like this:
double time,duration=samples*75.0,dt=1.0/128.0;
double signal128[???];
for (time=0.0,i=0;time<duration;i++,time+=dt)
signal128[i]=signal(time);
samples are the input signal array size in samples sampled by 75.0 Hz
[notes]
for/duration can be done on integers ...
change signal data type to what you need
inside signal(time) you need to handle edge cases (start and end of signal)
because you have no defined points in signal before first sample and after last sample. You can duplicate them or mirror next point (mirror is better).
this whole thing can be changed to process continuously without buffers just need to remember 4 last points in signal so you can do this in RT. Of coarse you will be delayed by 2-3 75.0 Hz samples ... and when you put all this together you will see that this is a FIR filter anyway :)
if you need to preserve more then first derivation add more points ...
You do not need to upsample and then downsample.
Instead, one can interpolate all the new sample points, at the desired spacing in time, using a wide enough low-pass interpolation kernel, such as a windowed Sinc function. This is often done by using a pre-calculated polyphase filter bank, either directly, or with an addition linear interpolation of the filter table. But if performance is not critical, then one can directly calculate each coefficient for each interpolated point.
The easiest way is to upsample to a sample rate which is the LCM of your two sample rates and then downsample - that way you get integer upsample/downsample ratios. In your case there are no common factors in the two sample rates so you would need to upsample by a factor of 128 to 9.6 kHz and then downsample by a factor of 75 to 128 Hz. For the upsampling you insert 127 0 samples in between each sample, then apply a suitable filter (37 Hz LPF, Fs = 9.6 kHz), and then downsample by taking every 75th sample. The filter design is the only tricky part, but there are online tools for taking the hard work out of this.
Alternatively look at third-party libraries which handle resampling, e.g. sox.
You need to upsample and downsample with an intermediate sampling frequency, as #Paul mentioned. In addition, it is needed to filter the signal after each transformation, which can be achieved by linear interpolation as:
% Parameters
F = 2;
Fs1 = 75;
Fs3 = 128;
Fs2 = lcm(Fs1,Fs3);
% Original signal
t1 = 0:1/Fs1:1;
y1 = sin(2*pi*F*t1);
% Up-sampled signal
t2 = 0:1/Fs2:1;
y2 = interp1(t1,y1,t2);
% Down-sampled signal
t3 = 0:1/Fs3:1;
y3 = interp1(t2,y2,t3);
figure;
subplot(3,1,1);
plot(t1,y1,'b*-');
title(['Signal with sampling frequency of ', num2str(Fs1), 'Hz']);
subplot(3,1,2);
plot(t2,y2,'b*-');
title(['Signal with sampling frequency of ', num2str(Fs2), 'Hz']);
subplot(3,1,3);
plot(t3,y3,'b*-');
title(['Signal with sampling frequency of ', num2str(Fs3), 'Hz']);

Tilted magnetometer output -pitch and roll tilt compensation - i am lost

I have wasted 2 days (and nights) on this specific issue and completely failed in tilt compensating my magnetometer output.
I tried about everything I could read on Google and in open source examples, nothing guided me to a proper tilt compensation for both (roll and pitch). .
The Setup:
I use calibrated values but not unity values.
I have a fused gravity vector which is working properly and exact.
The sensor is a 9dof BMX055 (http://www.mouser.com/ds/2/621/BST-BMX055-DS000-01-274824.pdf)
The magnetometer min/max are +- 512 on each axis (with small differences but all axes are zero'd out).
The hardware is Sam3X8e (Cortex M3), all written in C.
Floating point and trigonometry can be done quite fast, so that's no problem.
The BMX055 chip is aligned so that the pins 20,19,18 point to the front.
Datasheet page 159-161 show the orientation.
Pitch rises when I rise the front.
Roll rises when I rise the left side.
Example values:
Pointing at a direction my algorithm calls 305deg when leveled horizontal
Pitch: 0 , Roll 0 : MAG cal: x 132 y 93 z -364
Pitch: +24, Roll 0 : MAG cal: x -109 y 93 z -397
Pitch: +46, Roll 0 : MAG cal: x -303 y 89 z -351
Pitch: 0 , Roll -44 : MAG cal: x 151 y 352 z -235
Pitch: 0 , Roll +36 : MAG cal: x 130 y -140 z -328
Pitch: 78 , Roll -2 : MAG cal: x -503 y 93 z -199
Pitch: 7 , Roll -53 : MAG cal: x 135 y 424 z -180
The alignment should always have been around 305 degree (as good as I could do it), maybe +- 5 degree.
The formula: (same one used about everywhere)
uint16_t compass_tilt_compensation(float roll_radians, float pitch_radians,float mag_y, float mag_x, float mag_z)
{
float tilt_compensated_heading;
float MAG_X;
float MAG_Y;
float cos_roll;
float sin_roll;
float cos_pitch;
float sin_pitch;
int16_t heading;
//pitch_radians =roll_radians;
//roll_radians *= -1;
//mag_x *= -1;
//roll_radians=0.0f;
//pitch_radians=0;v
//pitch_radians *=-1;
cos_roll = cos(roll_radians);
sin_roll = sin(roll_radians);
cos_pitch = cos(pitch_radians);
sin_pitch = sin(pitch_radians);
#if 0
MAG_X = mag_x*cos_pitch+mag_y*sin_roll*sin_pitch+mag_z*cos_roll*sin_pitch;
MAG_Y = mag_y*cos_roll-mag_z*sin_roll;
tilt_compensated_heading = atan2f(-MAG_Y,MAG_X);
#else
MAG_X = mag_x * cos_pitch + mag_z * sin_pitch;
MAG_Y = mag_x * sin_roll * sin_pitch + mag_y * cos_roll - mag_z * sin_roll * cos_pitch;
tilt_compensated_heading = atan2f(-MAG_Y,MAG_X);
//tilt_compensated_heading = atan2f(-mag_y,mag_x); // works fine when leveled
#endif
//convert to degree from 0-3599
heading = tilt_compensated_heading * RAD_TO_DEG * 10;
if ( heading < 0 )
{
heading += 3600;
}
return heading;
}
I tried various combinations, I tried to only fix one axis and leave one always at 0, exchanging X/Y pitch/roll, *-1 on any of the inputs.
The results are always completely wrong.
Sometimes (depending on where I try to invert or not invert a value by trial/error) the values seem to be off nearly linear. Sometimes one axis is compensated in positive areas.
However rolling and pitching always causes 'random' jumps and changes of the heading.
Math has never been my favourite, now I regret it.
My personal guess is that the formula is right in principe, the mag is working in principe (after all I get proper degrees when leveled) but I am somehow feeding something wrong.
(Like Y and X need to be exchanged, z*-1, pitch needs to be *-1)
Could someone who is good in this subject maybe take a look and guide me how to get a proper heading ?
Would be great to have a few hours sleep this night without having to dream about a dysfunctional algorithm again :)
Update:
The tilt compensation here works for negative roll when pointing at the 305deg heading.
It is also used here: http://www.emcu.it/MEMS/Compass/MEMS_Compass_A_RTM1v3.pdf
3 days of work, finally I found the issues I had and tilt compensation is working !
I read quite a few people had such issues in various forums, so here is what I did:
I explain what I did and why I did, step by step. Maybe someone with a similar issue will find a solution that way.
Playing around with the values can help (maybe just pitch or roll has to be inverted, X or Y has to be exchanged).
However, there are quite a lot of values and the amount of combinations is too high if your problem is more than a tiny one.
The formula posted works fine (the one which is in the active #if branch.
If you have a magnetometer and the atan2(-y,x) gives you a proper heading when leveled flat, then this formula will work for you too.
What I did was to completely go through all my sensors and vectors beginning from the I2C binary logic (and the datasheet).
Important in this secial case (BMX055), the Datasheet orientation page is WRONG!
There are multiple bugs regarding orientation of axes (x,y) as well as when a rotation is positive or negative. Sometimes right hand rule applies, sometimes not and the drawings are misleading (wrong). Bosch did a bad job documenting this chip (and the previous one).
They do not seem to want people to understand it properly, they write about a fusion API several times with optimized fixed point arithmetiks and advanced calibration but it's not available for the public.
What I needed to do was to make a proper body reference system.
Decide yourself where X is and then make it so all your sensors properly change the X axis when pitched/rolled into the same direction (positive/negative).
Do this for pitch,roll and gravity/magnetic field.
Once all of them play together nicely I started all over again.
The heading formula was still dysfunctional but now I trusted the vextors the first time.
I added a vector rotation matrix function and rotated the magnetic vector back using roll and pitch and yaw=0.
Surprise: the magnetic vector was tilt compensated.
Now I knew it CAN be done :)
I went back to the original formula, replaced X with Y (because I had exchanged them to match the body reference system (X and Y of gyro/mag).
Now tilt compensation worked for pitch but not for roll.
So I inverted roll_radians and suddenly it's perfectly tilt compensated.
I have two solutions now. One is a rotation matrix solution and the other one the standard solution everyone is using. I will see if one of them performs better and maybe give a last update here if the results are worth it.
First, verify that it is indeed a software problem. Your equations seem to be correct. Just in case, generate a table populated with test data to pass to your procedure and compare the output of your function with the expected values if the code was correct.
You are using a magnetometer and these are very sensitive devices. The first thing I would check is whether there are any large metal structures near the sensor. If you have access to an oscilloscope, probe the chip's power rails and see if power going into it is stable. Adding a 1uF decoupling cap could solve power issues. I would also suggest getting a graph while having a sampling frequency larger than 100Hz and see if the jumps are periodic. If the signal is periodic with a 50Hz frequency, assuming the sensor is not being moved, that would indicate interference from your mains supply. Performing FFT analysis over the data can't hurt. We had similar problems caused by power cables running underneath our lab floor. If the sensor keeps jumping around randomly, the chip is probably dead. Did you use proper ESD protection while handling it?
Hope this helps.

Microcontroller Peak Detection in C using slope

I am making a finger plethysmograph(FP) using an LED and a receiver. The sensor produces an analog pulse waveform that is filtered, amplified and fed into a microcontroller input with a range of 3.3-0V. This signal is converted into its digital form.
Smapling rate is 8MHz, Processor frequency is 26MHz, Precision is 10 or 8 bit.
I am having problems coming up with a robust method for peak detection. I want to be able to detect heart pulses from the finger plethysmograph. I have managed to produce an accurate measurement of heart rate using a threshold method. However, the FP is extremely sensitive to movement and the offset of the signal can change based on movement. However, the peaks of the signal will still show up but with varying voltage offset.
Therefore, I am proposing a peak detection method that uses the slope to detect peaks. In example, if a peak is produced, the slope before and after the maximum point will be positive and negative respectively.
How feasible do you think this method is? Is there an easier way to perform peak detection using a microcontroller?
You can still introduce detection of false peaks when the device is moved. This will be present whether you are timing average peak duration or applying an FFT (fast Fourier Transform).
With an FFT you should be able to ignore peaks outside the range of frequencies you are considering (ie those < 30 bpm and > 300 bpm, say).
As Kenny suggests, 8MHz might overwhelm a 26MHz chip. Any particular reason for such a high sampling rate?
Like some of the comments, I would also recommend lowering your sample rate since you only care about pulse (i.e. heart rate) for now. So, assuming you're going to be looking at resting heart rate, you'll be in the sub-1Hz to 2Hz range (60 BPM = 1Hz), depending on subject health, age, etc.
In order to isolate the frequency range of interest, I would also recommend a simple, low-order digital filter. If you have access to Matlab, you can play around with Digital Filter Design using its Filter Design and Analysis Tool (Introduction to the FDATool). As you'll find out, Digital Filtering (wiki) is not computationally expensive since it is a matter of multiplication and addition.
To answer the detection part of your question, YES, it is certainly feasible to implement peak detection on the plethysmograph waveform within a microcontroller. Taking your example, a slope-based peak detection algorithm would operate on your waveform data, searching for changes in slope, essentially where the slope waveform crosses zero.
Here are a few other things to consider about your application:
Calculating slope can have a "spread" (i.e. do you find the slope between adjacent samples, or samples which are a few samples apart?)
What if your peak detection algorithm locates peaks that are too close together, or too far apart, in a physiological sense?
A Pulse Oximeter (wiki) often utilizes LEDs which emit Red and Infrared light. How does the frequency of the LED affect the plethysmograph? (HINT: It may not be significant, but I believe you'll find one wavelength to yield greater amplitudes in your frequency range of interest.)
Of course you'll find a variety of potential algorithms if you do a literature search but I think slope-based detection is great for its simplicity. Hope it helps.
If you can detect the period using zero crossing, even at 10x oversampling of 10 Hz, you can use a line fit of the quick-n-dirty-edge to find the exact period, and then subtract the new wave's samples in that period with the previous, and get a DC offset. The period measurement will have the precision of your sample rate. Doing operations on the time and amplitude-normalized data will be much easier.
This idea is computationally light compared to FFT, which still needs additional data processing.

Resources