Rainer Gratias wrote:
> How do I correctly average angles, keeping the periodicity in mind?
> The only (more or less) correct way I found is derived from averaging
> complex numbers, using the transformation from polar to cartesian
> coordinates:
> <ang>=arctan(<sin(ang_i)>/<cos(ang_i)>)
> (in Fortran: atan2(<sin(ang_i)>,<cos(ang_i)>))
To remove the "periodicity" of angles, apply the "modulus" function,
which is being invisibly applied by the SINE and COSINE library
functions. For positive m,n, you can compute modulus in this way:
m modulus n = m - n*FLOOR(m/n)
Thus, an angle a in the real numbers can be brought to the
half-open interval [0,2*pi) by the following:
If a is not negative:
aprime = a - 2*pi * FLOOR(a/(2*pi))
If a is negative:
aprime = 2*pi - (-a - 2*pi*FLOOR(-a/(2*pi)))
where FLOOR(x) is the largest integer less than or equal to x.
Note, in the above, that FLOOR will never be given a negative argument.
As a general rule, explicit trigonometric functions ought to be avoided
whenever possible, for reasons of accuracy and efficiency. The
single-argument atan has an additional limitation because the
mathematical "inverse tangent" has a range of only (-pi/2,pi/2) rather
than the range (-pi,pi] typically supplied by the two-argument form.
Regarding the "strange behavior" of averaging angles:
The results appear consistent to me, except for the -90 given for
{0,0,180,180} which is an implementation artifact; the answer could have
been reasonably given as 90. But clearly, the average angle for any two
angles separated by 180 degrees is "no angle", and so any number of
opposing angles contribute nothing to the average. However, each of the
values do indeed affect the result, as can easily be seen by slightly
changing one of them.
The special case when the angles, as it were, cancel each other out,
requires explicit handling. This is easily approached by converting the
angles to unit vectors (as Dr. Gratias noted), averaging them, and
reporting back the average. Then, the special case is easily detected as
the null vector (0,0), which is directionless. The pseudocode is:
SUMX = 0
SUMY = 0
N =0
FOR each angle a (in radians) DO
SUMX = SUMX + COS(a)
SUMY = SUMY + SIN(a)
N = N + 1
ENDFOR
AVGX = SUMX/N
AVGY = SUMY/N
IF (AVGX,AVGY) is "too close" to (0,0) THEN
report directionless, NULL VECTOR
ELSE
AVERAGEANGLE = ARCTAN(AVGY,AVGX)
ENDIF
I hope this helps.
Peter J. Floriani, Ph.D.
floriani at epix.net
----------------------------------------------------
"I deny that biology can destroy the sense of truth,
which alone can even desire biology. No truth which
I can find can deny that I am seeking the truth.
My mind cannot find anything which denies my mind."
G. K. Chesterton, "The Long Bow" in Collected Works 14:96