Low Pass Filter Question
Greg LaPolla
Posts: 323
in Propeller 2
I am porting this from Arduino. I have watched at least 20 You tube videos about the subject and I am still a little confused. I am not getting the correct data. I think that the coefficients are wrong for use with the propeller as it runs way faster than the Arduino. I don't have mathlab so I can't really figure out the coefficients.
On setting the return I have tried >> 15, SAR 15 and divide by 32768. None seem to work.
PUB lowPassFIRFilter(din): result | z, i cbuf[offset] := din z := mul16(FIRCoeffs[11], cbuf[(offset - 11) & $1F]) i:= 0 repeat while i < 11 z += mul16(FIRCoeffs[i], cbuf[(offset - i) & $1F] + cbuf[(offset - 22 + i) & $1F]) i++ offset++ offset //= 32 'Wrap condition return result := (z >> 15) ' Integer multiplier PUB mul16(x, y): result return result := (x * y) DAT FIRCoeffs long 172, 321, 579, 927, 1360, 1858, 2390, 2916, 3391, 3768, 4012, 4096
Comments
Your offset use looks fishy to me. The increment and wrap indicate it’s a ringbuffer (which makes sense in this context), but within your loop you happily index into forbidden territories by doing index arithmetic. Basically for every offset based access you need to wrap it around before actually accessing the buffer. Like (untested)
cbuf[(offset - i) // 32]
I am pretty sure the & $1F Takes care of that. I haven't seen the offset go above 31 which is what it is supposed to do.
I can confirm that
is the same as
The former is faster because it doesn't involve division.
@"Greg LaPolla" 🤦♂️You’re right. I somehow read that as applied to the value, not the index.
What does the original Arduino code look like? Have a link?
Most of the stuff I see on this is using floating point...
BTW: There's a super simple way to implement the digital form of a low pass RC filter, in case that doesn't work out...
http://www.dsplog.com/2007/12/02/digital-implementation-of-rc-low-pass-filter/
SAR 15 or divide by 32768 should work. The ">>" operator is unsigned, and it will give you the wrong answer.
How is cbuf defined? It should be defined as an array of 32 longs. So either as "long cbuf[32]" in a VAR section or "cbuf long 0[32]" in a DAT section. If you define it as a byte or word you will need to do sign extension since these are unsigned variables.
Here is the Arduino code
@"Dave Hein"
It is long cbuf[32]
I think @"Dave Hein" is right... That "return result := (z >> 15)" looks wrong for signed integers...
Are all your variables declared as long? It looks like your code should work if you use divide or SAR.
All variables are longs. I switched them all to longs because only longs can have signs. It doesn't matter if I use >>, SAR or divide, the output is still incorrect.
Can you post all your code? The problem must be in some other area of the code.
There is not much more but here it is
I haven't used Spin on the P2, but if it works like the P1 then it's going to run the start method and then stop. So I'm assuming this isn't all of the code, which means you are calling this code from other objects.
Your variables are declared in a VAR section. This means that each object that calls a method in the code you posted will use a different copy of the variables. Make sure you are calling all the methods from the same object. If not, then you should declare your variables in a DAT section so that you only have one set of variables.
This is an object. It is used in conjunction with a chip driver object. The LowPassFilter is not outputting the correct data.
It is supposed to detect positive zero crossing on the rising edge. It never happens at this point. I have narrowed the issue to the code in this method.
It will be called from another method and only called from that method.
There is no issues with the variables. This entire object only returns a true or false back to the calling method. There is no shared variables to get crossed. It is passed a value from the sensor and if it detects a heart beat it returns true if not it returns false. Thats it.
I think you can write this filter much simpler, without the mul16 methode:
I have not looked at the Arduino code, just rewritten your FIR methode, so I don't know if the filter is ported correct.
Andy
OK, so you are only referencing the object from one other object, correct? As written, it won't work if you reference it from multiple objects.
There's an error in the following line:
if ((IR_AC_Max - IR_AC_Min) > 20 & (IR_AC_Max - IR_AC_Min) < 1000)
In Spin1 the "&" has precedence over the ">" operator. So this line is the same as:
if (((IR_AC_Max - IR_AC_Min) > (20 & (IR_AC_Max - IR_AC_Min))) < 1000)
The result is always be TRUE. In Spin1 a TRUE value was always -1. I don't know what it is in Spin2. Also, you probably don't want to use the "&" operator, which is a bitwise and. You probably want the logical and, which in Spin1 is AND. I don't know what it is in Spin2.
There is also a problem with the line:
return result := (z >> 15)
In Spin1 this should be
return result := (z ~> 15)
In Spin2 it should be
return result := (z SAR 15)
Also, do you even need the return. Can't you just do
result := (z SAR 15)
and it will automatically return the value of result?
[RANT] Too many arbitrary differences between Spin1 and Spin2. The differences are unnecessary! Program in C instead.[/RANT]
@"Dave Hein" Nice catches. I guess there are two main things tripping people up going between Spin2 and C; operator precedence and signed/unsigned integers (maybe mostly right shifting of these when less than 32 bits).
Personally, I like Spin2 much better than Spin1, mainly because >= is the right way (the way you say it) and functions requiring parenthesis makes code clearer. But, the C standard is hard to ignore.
I checked the Spin2 documentation, and the logical and operator is either AND or &&. So the IF statement could be written as
if ((IR_AC_Max - IR_AC_Min) > 20 AND (IR_AC_Max - IR_AC_Min) < 1000)
In this case the parentheses are not needed since the ">" operator will be evaluated before the AND operator.
I changed all the & to AND still not getting the right data out of the lowPassFilter.
@"Greg LaPolla" , try the attached file to see if that works.
@"Dave Hein" Thanks for the help!
Couple things I figured out today. Can use SAR 15 on the averageDCEstimator. I don't know why but it goes negative and the output of the sensor (sample) is never negative so I just use >> 15.
I am still pretty sure the coefficients need to change because in order to detect a heartbeat the current lowPassFIRFilter output has the be greater than or equal to zero and the previous output has to be less than zero. Holding my finger on the sensor for 1 minute I will see 1 maybe 2 hits.
Using your modified code IR_AC_Signal_Current and IR_AC_Signal_Previous never go negative when my finger is on the sensor.
Using my code the majority of the output is negative
From what I can gather the output should be positive and negative under 10 if I understand everything.
It seems like you are getting an overflow in the averageDCEstimator routine. The input value is scaled up by 32768, so to prevent overflow the input value must be less than 65536. Does the input ever exceed that value?
I also notice that the FIR filter has a gain of 1.45. If the input to the FIR filter was greater than 45,197 for 23 samples it would overflow. However, since the DC value is subtracted from the input I think this would prevent an overflow in the FIR filter.
Finger on reading is 97,000 to 101,000
Finger off reading 1100 to 1200
I think the lighting has something to do with the fluctuation
the estimator is spot on all the time. it runs slightly behind the sample usually by 20-30.
There's your problem. The input is exceeding the value of 65536. When averageDCEstimator shifts this up by 15 it overflows into a negative value. It looks like you are getting a lot of hum from the AC line.
Add the following line to the beginning of the checkForBeat method:
sample >>= 2
That should help. If that doesn't work try shifting down by 3 or 4 instead. You may also want to clip the maximum value of sample, as follows:
if (sample > 65535)
sample := 65535
That will ensure that averageDCEstimator doesn't overflow.