Fixed-Point ATAN2 using CORDIC

by

 

25 Mar 2008 (Updated )

This demo shows how a fixed-point cordic algorithm to calculate a phase from polar coordinates (arct

Fixed-Point ATAN2 using CORDIC

Fixed-Point ATAN2 using CORDIC

This demo shows how a fixed-point cordic algorithm to calculate a phase from polar coordinates (arctan) can be implemented in MATLAB.

It also shows how:

  • C Code generation is used to speed-up the MATLAB function
  • Embedded MATLAB is used to incorporate this function into Simulink diagrams

Contents

CORDIC

CORDIC (digit-by-digit method, Volder's algorithm) (for COordinate Rotation DIgital Computer) is a simple and efficient algorithm to calculate hyperbolic and trigonometric functions. It is commonly used when no hardware multiplier is available (e.g., simple microcontrollers and FPGAs) as the only operations it requires are addition, subtraction, bitshift and table lookup. (Source: http:\\wikipedia.org\wiki\cordic)

CORDIC can be used to calculate a number of different functions. This explanation shows how to use CORDIC in rotation mode to calculate the angle of a vector (x,y), which equals MATLAB's built-in function ATAN2.

help atan2
 ATAN2  Four quadrant inverse tangent.
    ATAN2(Y,X) is the four quadrant arctangent of the real parts of the
    elements of X and Y.  -pi <= ATAN2(Y,X) <= pi.
 
    See also ATAN.

    Overloaded methods:
       distributed/atan2

    doc atan2
      

We will first assume that the angle of the vector is between 0 and 45 degrees or [0 pi/4]. The inverse algorithm (which works for arctan, arccos and arcsin) will try to rotate the vector towards (1,0). So if the y coordinate of the vector (or the imaginary part) is positive CORDIC will rotate with a negative angle, otherwise it will rotate with a positve angle. By keeping track of how many degrees we have rotated the original vector we know the angle of that vector. That's all!

The power of the CORDIC algorithm is that we pick the angles for rotation in such a way that the corresponding rotation matrix only contains powers of 2, which in fixed-point can be easily implemented using bitshifts (no multiplications needed!)

The rotation matrix R for an angle phi equals:

R = [cos(phi) -sin(phi);
     sin(phi) cos(phi)]

or

R = K*[1        -tan(phi);
       tan(phi) 1         ]

with

K = 1/sqrt(1 + tan(phi)^2)

Because we are only interested in the angle of the vector and not in the magnitude, we can ignore the factor K for now. We have to deal with it later when we determine the appropriate fixed-point data types.

Now we let tan(phi) equal powers of 2:

phi = 2.^-(1:5)
phi =

  Columns 1 through 3

    0.5000    0.2500    0.1250

  Columns 4 through 5

    0.0625    0.0313

This corresponds to the following angles:

atan(phi)
ans =

  Columns 1 through 3

    0.4636    0.2450    0.1244

  Columns 4 through 5

    0.0624    0.0312

or in degrees:

atan(phi)*180/pi
ans =

  Columns 1 through 3

   26.5651   14.0362    7.1250

  Columns 4 through 5

    3.5763    1.7899

It can be proven that you can rotate to any angle between 0 and 45 degrees by rotating clock-wise or counter clock-wise with these angles. The number of iterations (in this case only 5) determines the precicion of the algorithm.

Floating-point

In floating-point the algorithm is astonishing simple. Let's try with an angle of 0.25 (rad).

v = [cos(0.25), sin(0.25)]'; % Starting vector (input)
a = 0;
for n = 1:5;
        a = a + sign(v(2))*phi(n); % Keep track of rotation
        R = [1,                 sign(v(2))*2^(-n);
            -sign(v(2))*2^(-n), 1                   ]; % Rotation matrix
        v = R*v; % Rotate
end

The result after 5 iterations equals:

a
a =

    0.2813

Fixed-point

The input should accomodate the unit circle, making necessary a datatype of 16 bits with a 14 bit fraction length. The range of this fixed-point data type is [-2 2]. This is usefull, because the gain of the CORDIC is max 1.1644. So overflows are not possible in the rotation of the input vector.

Tcar = numerictype(1,16,14)
Fcar = fimath('SumWordLength', 16, 'SumFractionLength', 14, 'SumMode', 'Specify');
 
Tcar =
 

          DataTypeMode: Fixed-point: binary point scaling
                Signed: true
            WordLength: 16
        FractionLength: 14

Because we want to work with bitshifts we have to abandon the matrix notation and work with the seperate real and imaginary parts of the complex vector (or x and y from a the vector (x,y), whatever you prefer).

im = fi(sin(0.25), 'NumericType', Tcar,'FiMath', Fcar);
re = fi(cos(0.25), 'NumericType', Tcar,'FiMath', Fcar);

With regards to the output: the temporary result of the cordic algorithm is always between +pi/4 and -pi/4 which is within the range of 16 bit fixed-point number with a 15 bit fraction length. However, the output of the algorithm is always between -pi and pi, so a fraction length of 13 bits makes more sense, as more precision is lost in the output.

Trad = numerictype(1,16,13);
Frad = fimath('SumWordLength', 16, 'SumFractionLength', 13, 'SumMode', 'Specify');

Now with regards to the number of rotations/iterations: the trick is to find trade-off between fixed-point precision and algorithmic precision. The algorithmic precision is approximately atan(2.^-N). The fixed-point precision is 2^-13. The trade-off is around N = 12.

N = 12;
angles = fi(atan(2.^-(1:N)),'NumericType', Trad, 'FiMath', Frad);

The fixed-point CORDIC including bitshifts can be written as:

reg = fi(0, 'NumericType',Tcar, 'FiMath', Fcar); % Temporary register

a = fi(0,'NumericType', Trad, 'FiMath', Frad); % Starting angle (0 radians)

for i = 1:N,

    if bitget(im,16), % negative angle
        a = a - angles(i);

        reg = re - bitshift(im,-i);
        im = bitshift(re,-i) + im;
        re = reg;

    else
        a = a + angles(i);

        reg = re + bitshift(im,-i);
        im = -bitshift(re,-i) + im;
        re = reg;

    end

end

The fixed-point result after 5 iterations equals:

a
 
a =
 
    0.2501

          DataTypeMode: Fixed-point: binary point scaling
                Signed: true
            WordLength: 16
        FractionLength: 13

             RoundMode: nearest
          OverflowMode: saturate
           ProductMode: FullPrecision
  MaxProductWordLength: 128
               SumMode: SpecifyPrecision
         SumWordLength: 16
     SumFractionLength: 13
         CastBeforeSum: true

For angles between pi/4 and pi/2 we use the following trigonometric equality:

tan(pi/2 - a) == 1/tan(a);

This means that we can simply swap the imaginary and real part of the input vector and then substract the result from pi/2.

For starting vectors in one of the other three quadrants (or angles other than [0 pi/2] we always start in the first quadrant by taking the absolute values of both the imaginary and real part. We then have to substract or add the result of the CORDIC algorithm from/to pi/2 or 0 radians. See the implementation in atan2_fixpt.m.

Fixed-point versus Floating point

Let's test how accurate the fixed-point implementatoin is in comparison with floating-point. Because we are interested in the accuracy of the algorithm we look at how well the CORDIC perform in calculating the angle of a fixed-point input vector:

phis = 2*pi*(rand(1,1e3) -0.5); % Some random angles
phis_fixpt = fi(phis,'NumericType', Trad, 'FiMath', Frad); % Rounded towards fixed-point representation
phis_cordic = phis_fixpt; % Pre-allocate fixed-point output vector.

Floating point:

phis_atan2 = atan2(sin(double(phis_fixpt)),cos(double(phis_fixpt)));

Fixed-point

sinphis = fi(sin(phis), 'NumericType', Tcar, 'FiMath', Fcar);
cosphis = fi(cos(phis), 'NumericType', Tcar, 'FiMath', Fcar);

phis_cordic = atan2_fixpt(sinphis, cosphis);

Now look at the root mean squared error of the algorithm and the histogram:

err_cordic = sqrt(mean((double(phis_cordic)-phis_atan2).^2))
err_cordic =

  1.7811e-004

Accelerate using EMLMEX

The fixed-point toolbox provides the possibility of accelerating fixed-point m-code using EMLMEX, which generates C-MEX code from m-code:

emlmex -eg {sinphis,cosphis} -o atan2_fixpt_mex atan2_fixpt

Now let's compare the speed of the original m-file

tic
   phis_cordic = atan2_fixpt(sinphis, cosphis);
toc
Elapsed time is 8.839059 seconds.

with the MEX file:

tic
    phis_cordic = atan2_fixpt_mex(sinphis, cosphis);
toc
Elapsed time is 0.014633 seconds.

The speed-up is more then 100 times...

Contact us