Mean value of circular quantities without float, sin, cos and stuff - Page 1
Forum section: electronics / general

author: daqq
author_rank: Super Contributor
timestamp: January 23, 2014, 12:36:42 pm
content: Hi guys,For a project I need to calculate the mean of a circular quantity. Basically, I've got a circular buffer of N values I get from a sensor, with a 0-359 deg output and I need the average of these values.Normally I'd use the following:http://en.wikipedia.org/wiki/Mean_of_circular_quantitiesWhich works well, I've tried it, but it's a memory/time hog - float, cos, sin, atan are a bit of a no-no on small processors as far as I am concerned (unless unavoidable). As it is, there's no easy way I can see of doing this without float.So far, the best I can think of is some horribly massive precalculated lookup tables, that do pretty much the same algorithm. Also, as I'm operating with a finite amount of states (0-360), it should be possible to just try all of them and find the one that has the least circular distance according to all of them - that takes care of the memory hogging, but not of the time hogging. Nmax is 64, with 360 angles to try it gives us a scary number. It could be done in steps (find roughly the minimum) then search the area with finer detail, sort of like successive approximation ADCs, but it still seems like a nasty approach.So, do you know how to do an average of a circular value without using floating point calculations?Thanks,David

author: cosmos
author_rank: Regular Contributor
timestamp: January 23, 2014, 12:52:55 pm
content: Compute the rectangular coordinate with CORDIC perhaps?CORDIC is normally done with integers.It is iterative and you do not get the conversion for free but it may be better than what you currently do.(as a bonus: CORDIC is a fun and interesting technique to know)Then you can average as you like on the rectangular results and convert back to a polar vector again with CORDIC.If you want a result per sample placed in the buffer you can keep a running sum (rect. I/Q) and just subtract the value you remove from the buffer and add the new one.One coordinate conversion per sample placed and one to return the averaged polar vector.(edit: the last line assumes you store the rectangular value in the buffer (doubles buffer size) avoiding computing the rectangular value of the value that is replaced)(edit2: spelling)

author: amyk
author_rank: Super Contributor
timestamp: January 23, 2014, 01:23:58 pm
content: Glad to see I wasn't the only one who thought "use CORDIC".But how many CPU cycles/time do you have to do this, it's important to know to find the right algorithm.

author: AndreasF
author_rank: Frequent Contributor
timestamp: January 23, 2014, 01:48:12 pm
content: Instead of floating-point you could use fixed-point arithmetic after the angles have been converted to cartesian coordinates (since they will be in a precisely known and limited range [-1 ... 1]). That would make the additions into simple integer additions. With the right choice of buffer length, division becomes a bit-shift operation. The trickiest part would probably be to re-convert the mean cartesian point back into the angle.

author: Mechatrommer
author_rank: Super Contributor
timestamp: January 23, 2014, 02:02:56 pm
content: Quote from: amyk on January 23, 2014, 01:23:58 pmlooking from the link, the algol requires some memory allocation and some O(n), n will depends on your required precision. there is another way, which is mentioned by the OP in an exageratted way. it requires allocation, the size will be the precision you required, but getting the value will be blazing fast in order of O(1) to say O(10) depending on the way you do it. not mathematically proof but good enough for low end "not a spaceship" engineering/hobby purposes. looking at the allocation made in the CORDIC link i prefer the later unless what i want is a wrist calculator or anything "calculator" or a "spaceship". another trick of doing floats calc using integers is to introduce multiplier concept eg if you want to calc 1.1 X 3.4, you do it with 11 X 34. you know the multiplier is 10 but the right hand answer is 100.

author: tanstaafl
author_rank: Contributor
timestamp: January 23, 2014, 02:08:03 pm
content: Depending on what your input is you could maybe use the Mitsuta method to calculate the mean, mainly used to calculate mean of wind direction and comes with some constraints... particularly that the difference between successive samples must be less than 180 degrees...(6.2.4)http://www.webmet.com/met_monitoring/621.html(pseudo code)http://abelian.org/vlf/bearings.html

author: T3sl4co1l
author_rank: Super Contributor
timestamp: January 23, 2014, 02:47:56 pm
content: I don't suppose the samples in the buffer tend to be consecutive..? That is, the sensor only reads one degree at a time, so the samples are guaranteed to span 36 degrees.Even if the samples are up to 5 degrees apart, the buffer won't span more than 180 degrees, in which case the arithmetic average won't be off terrifically far (but will be subject to nonuniformity towards the ends of the spread of data).If they are all randomly distributed (or must work for arbitrarily large angles between samples), you won't have much choice.Depending on what hardware you have.... Flash is cheap, you might store all 360 degrees in a table, do a lookup, average, and not have to worry about multiplication or CORDIC at all.The main difference is CORDIC trades a full table (450 points, since sin/cos overlap) and no multiplication, for a short (binary series) table and N multiplications for N bits of angle. Those multiplications will be painful on, like, a PIC without hardware multiply; annoying to useful on, say, an AVR8 with 8x8 multiply; or trivial on something like a MSP430, PIC32 or ARM, with 16 or 32 bit multiply.Tim

author: dannyf
author_rank: Super Contributor
timestamp: January 23, 2014, 03:38:22 pm
content: Would a few look-up tables help?

author: daqq
author_rank: Super Contributor
timestamp: January 24, 2014, 08:50:53 am
content: Thanks guys, the CORDIC stuff seems the way to go! I'll test it and compare it with the rest, but it seems promising.Thanks!

author: IanB
author_rank: Super Contributor
timestamp: January 24, 2014, 09:28:20 am
content: I would suggest treating the angles as linear quantities and then averaging them as such.For instance, suppose you have the following samples:351°, 359°, 3°, 7°, 9°You first of all pick one value as a reference, such as the first one, 351°. Store this value for later.Next, convert all the values to angular differences from this reference value. This gives the following list:0°, 8°, 12°, 16°, 18°(To compute the difference, subtract the first value from the second. If the result is negative, add 360° to it.)Now compute the arithmetic mean of these differences:(0 + 8 + 12 + 16 + 18) / 5 = 10.8Last, add the mean of the differences back to the reference value:351° + 10.8° = 1.8°(As before, if the result is greater than 360° then subtract 360.)This gives the mean of the values. It will be slightly different from the true circular mean, but if the values are sampled close together on the circle it will not likely matter to you. For instance, the true circular mean of the angles above is 1.81°.

author: tanstaafl
author_rank: Contributor
timestamp: January 24, 2014, 10:21:19 am
content: Quote from: Dawgie on January 24, 2014, 09:57:07 amundefined, or maybe (0, 0) if you look at it as a vector... not that uncommon with geomteric problems having undefined cases

author: daqq
author_rank: Super Contributor
timestamp: January 24, 2014, 11:01:02 am
content: I've had a nice idea - calculating the average of two angles is simple - just get their circular distance:QuoteCircularly (%360) add HALF of it to one of them in the direction so it is between them. You've got the average of two angles.If N is even, you can simply do a tree of averages of two angles. Just Add/Substract in very few steps.Of 64 angles in the buffer you get 32 angles, then 16, then 8, then 4, then 2 and finally 1.

author: NiHaoMike
author_rank: Super Contributor
timestamp: January 24, 2014, 08:57:55 pm
content: If you map the angle over the range of 0 to a power of two, you can get rid of a (slow) modulus operation - it can be replaced with a bitwise AND or even implicitly done by the data type.

author: daqq
author_rank: Super Contributor
timestamp: January 27, 2014, 10:50:41 am
content: That could work as well, thanks. Though modulus of an integer is not that expensive on most stuff?

author: NiHaoMike
author_rank: Super Contributor
timestamp: January 27, 2014, 06:59:47 pm
content: http://en.wikipedia.org/wiki/Modulo_operation#Performance_issuesNot a big deal on more advanced processors, but can be significant on a simple microcontroller.

author: zapta
author_rank: Super Contributor
timestamp: February 28, 2014, 04:46:38 am
content: Quote from: daqq on January 23, 2014, 12:36:42 pmYou can take advantage of symmetries and relations between sin and cos to reduce the size of the lookup table. As for not using floats, you can use fixed point (shifted integers). This will also give you the accuracy to avoid errors due to incremental computation of the running window.