Ticket #2109: cordic_patch.patch

File cordic_patch.patch, 7.8 KB (added by Thanduel, 9 years ago)

play tested patch including cordic atan2 and sincos

  • source/maths/Fixed.cpp

     
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    143143    return r.str();
    144144}
    145145
    146 // Based on http://www.dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization
    147 CFixed_15_16 atan2_approx(CFixed_15_16 y, CFixed_15_16 x)
     146typedef enum
    148147{
    149     CFixed_15_16 zero;
     148    QUAD1,
     149    QUAD2,
     150    QUAD3,
     151    QUAD4
     152}CORDIC_OUTPUT_STATE;
    150153
    151     // Special case to avoid division-by-zero
    152     if (x.IsZero() && y.IsZero())
    153         return zero;
     154//angles: 45, 26.565, 14.036, 7.125, 3.576, 1.790, 0.895, 0.448
     155static const CFixed_15_16 tan_angle_table_rad[8] = { CFixed_15_16(51472), CFixed_15_16(30386), CFixed_15_16(16055), CFixed_15_16(8150), CFixed_15_16(4091), CFixed_15_16(2047), CFixed_15_16(1042), CFixed_15_16(512) };
     156//angles 90 and 180
     157static const CFixed_15_16 angle_0_5_pi = CFixed_15_16(102944);
     158static const CFixed_15_16 angle_1_0_pi = CFixed_15_16(205887);
     159static const CFixed_15_16 angle_1_5_pi = CFixed_15_16(308831);
     160static const CFixed_15_16 angle_2_0_pi = CFixed_15_16(411775);
     161static const CFixed_15_16 zero = CFixed_15_16();
    154162
    155     CFixed_15_16 c1;
    156     c1.SetInternalValue(51472); // pi/4 << 16
     163CFixed_15_16 atan2_approx(CFixed_15_16 y, CFixed_15_16 x)
     164{
     165    CFixed_15_16 angle;
     166    int loop_var;
     167    int previous_x_val;
     168    int new_x_val;
     169    CORDIC_OUTPUT_STATE output_state = QUAD1;
    157170
    158     CFixed_15_16 c2;
    159     c2.SetInternalValue(154415); // 3*pi/4 << 16
     171    if (y == zero)
     172    {
     173        if (x >= zero)
     174            return zero;
     175        else
     176            return angle_1_0_pi;
     177    }
    160178
    161     CFixed_15_16 abs_y = y.Absolute();
     179    //quadrant 2
     180    else if (x < zero && y > zero)
     181    {
     182        x.SetInternalValue(x.GetInternalValue()* -1);
     183        output_state = QUAD2;
     184    }
    162185
    163     CFixed_15_16 angle;
    164     if (x >= zero)
     186    //quadrant 4
     187    else if (x > zero && y < zero)
    165188    {
    166         CFixed_15_16 r = (x - abs_y) / (x + abs_y);
    167         angle = c1 - c1.Multiply(r);
     189        y.SetInternalValue(y.GetInternalValue()* -1);
     190        output_state = QUAD4;
    168191    }
    169     else
     192
     193    //quadrant 3
     194    else if (x < zero && y < zero)
    170195    {
    171         CFixed_15_16 r = (x + abs_y) / (abs_y - x);
    172         angle = c2 - c1.Multiply(r);
     196        x.SetInternalValue(x.GetInternalValue()* -1);
     197        y.SetInternalValue(y.GetInternalValue()* -1);
     198        output_state = QUAD3;
    173199    }
    174200
    175     if (y < zero)
    176         return -angle;
    177     else
     201
     202    for (loop_var = 0; loop_var < 5; ++loop_var)
     203    {
     204        int factor;
     205        //these divisions should be optimised by the compiler to shifts because the operations are always division by a power of two
     206        //this makes the cost of each division 1 cycle.
     207        if (y < zero)
     208        {
     209            factor = (1 << loop_var);
     210            previous_x_val = x.GetInternalValue();
     211            x -= (y / factor);
     212            new_x_val = x.GetInternalValue();
     213            x.SetInternalValue(previous_x_val);
     214            y += (x / factor);
     215            x.SetInternalValue(new_x_val);
     216
     217            angle -= tan_angle_table_rad[loop_var];
     218        }
     219        else
     220        {
     221            factor = (1 << loop_var);
     222            previous_x_val = x.GetInternalValue();
     223            x += (y / factor);
     224            new_x_val = x.GetInternalValue();
     225            x.SetInternalValue(previous_x_val);
     226            y -= (x / factor);
     227            x.SetInternalValue(new_x_val);
     228
     229            angle += tan_angle_table_rad[loop_var];
     230        }
     231    }
     232
     233    //this should be optimised to a jump table
     234    switch (output_state)
     235    {
     236    case QUAD1:
    178237        return angle;
     238    case QUAD2:
     239        return (angle_1_0_pi - angle);
     240    case QUAD3:
     241        return (angle_1_0_pi - angle) * -1;
     242    case QUAD4:
     243        return angle * -1;
     244    default:
     245        return angle;
     246    }
    179247}
    180248
    181249template<>
     
    186254
    187255void sincos_approx(CFixed_15_16 a, CFixed_15_16& sin_out, CFixed_15_16& cos_out)
    188256{
    189     // Based on http://www.coranac.com/2009/07/sines/
     257    CFixed_15_16 angle = CFixed_15_16();
     258    CFixed_15_16 y = CFixed_15_16();
     259    CFixed_15_16 x = CFixed_15_16(39797); //cordic gain for 8 angles
     260    int loop_var;
     261    int previous_x_val;
     262    int new_x_val;
     263    CORDIC_OUTPUT_STATE output_state;
     264    bool is_positive_angle = true;
    190265
    191     // TODO: this could be made a bit more precise by being careful about scaling
     266    if (a < zero)
     267    {
     268        is_positive_angle = false;
     269        a.SetInternalValue(a.GetInternalValue() * -1);
     270    }
    192271
    193     typedef CFixed_15_16 fixed;
     272    if (a < angle_0_5_pi)
     273        output_state = (is_positive_angle ? QUAD1 : QUAD4);
    194274
    195     fixed c2_pi;
    196     c2_pi.SetInternalValue(41721); // = 2/pi << 16
    197 
    198     // Map radians onto the range [0, 4)
    199     fixed z = a.Multiply(c2_pi) % fixed::FromInt(4);
    200 
    201     // Map z onto the range [-1, +1] for sin, and the same with z+1 to compute cos
    202     fixed sz, cz;
    203     if (z >= fixed::FromInt(3)) // [3, 4)
     275    else if (a < angle_1_0_pi)
    204276    {
    205         sz = z - fixed::FromInt(4);
    206         cz = z - fixed::FromInt(3);
     277        a -= angle_0_5_pi;
     278        output_state = (is_positive_angle ? QUAD2 : QUAD3);
    207279    }
    208     else if (z >= fixed::FromInt(2)) // [2, 3)
     280    else if (a < angle_1_5_pi)
    209281    {
    210         sz = fixed::FromInt(2) - z;
    211         cz = z - fixed::FromInt(3);
     282        a -= angle_1_0_pi;
     283        output_state = (is_positive_angle ? QUAD3 : QUAD2);
    212284    }
    213     else if (z >= fixed::FromInt(1)) // [1, 2)
     285    else
    214286    {
    215         sz = fixed::FromInt(2) - z;
    216         cz = fixed::FromInt(1) - z;
     287        a -= angle_1_5_pi;
     288        output_state = (is_positive_angle ? QUAD4 : QUAD1);
    217289    }
    218     else // [0, 1)
     290
     291    for (loop_var = 0; loop_var < 8; ++loop_var)
    219292    {
    220         sz = z;
    221         cz = fixed::FromInt(1) - z;
    222     }
     293        int factor;
     294        //these divisions should be optimised by the compiler to shifts because the operations are always division by a power of two
     295        //this makes the cost of each division 1 cycle.
     296        if (angle > a)
     297        {
     298            factor = (1 << loop_var);
     299            previous_x_val = x.GetInternalValue();
     300            x -= (y / factor);
     301            new_x_val = x.GetInternalValue();
     302            x.SetInternalValue(previous_x_val);
     303            y += (x / factor);
     304            x.SetInternalValue(new_x_val);
    223305
    224     // Third-order (max absolute error ~0.02)
     306            angle -= tan_angle_table_rad[loop_var];
     307        }
     308        else
     309        {
     310            factor = (1 << loop_var);
     311            previous_x_val = x.GetInternalValue();
     312            x += (y / factor);
     313            new_x_val = x.GetInternalValue();
     314            x.SetInternalValue(previous_x_val);
     315            y -= (x / factor);
     316            x.SetInternalValue(new_x_val);
    225317
    226 //  sin_out = (sz / 2).Multiply(fixed::FromInt(3) - sz.Multiply(sz));
    227 //  cos_out = (cz / 2).Multiply(fixed::FromInt(3) - cz.Multiply(cz));
     318            angle += tan_angle_table_rad[loop_var];
     319        }
     320    }
    228321
    229     // Fifth-order (max absolute error ~0.0005)
    230 
    231     fixed sz2 = sz.Multiply(sz);
    232     sin_out = sz.Multiply(fixed::Pi() - sz2.Multiply(fixed::Pi()*2 - fixed::FromInt(5) - sz2.Multiply(fixed::Pi() - fixed::FromInt(3)))) / 2;
    233 
    234     fixed cz2 = cz.Multiply(cz);
    235     cos_out = cz.Multiply(fixed::Pi() - cz2.Multiply(fixed::Pi()*2 - fixed::FromInt(5) - cz2.Multiply(fixed::Pi() - fixed::FromInt(3)))) / 2;
     322    //the cases are contiguous therefore this should be optimised to a jump table
     323    switch (output_state)
     324    {
     325    case QUAD1:
     326        sin_out.SetInternalValue(y.GetInternalValue() * -1);
     327        cos_out.SetInternalValue(x.GetInternalValue());
     328        return;
     329    case QUAD2:
     330        sin_out.SetInternalValue(y.GetInternalValue() * -1);
     331        cos_out.SetInternalValue(x.GetInternalValue() * -1);
     332        return;
     333    case QUAD3:
     334        sin_out.SetInternalValue(y.GetInternalValue());
     335        cos_out.SetInternalValue(x.GetInternalValue() * -1);
     336        return;
     337    case QUAD4:
     338        sin_out.SetInternalValue(y.GetInternalValue());
     339        cos_out.SetInternalValue(x.GetInternalValue());
     340        return;
     341    default:
     342        return;
     343    }
    236344}
  • source/maths/Fixed.h

     
    1 /* Copyright (C) 2013 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    117117private:
    118118    T value;
    119119
     120public:
     121
    120122    explicit CFixed(T v) : value(v) { }
    121 
    122 public:
    123123    enum { fract_bits = fract_bits_ };
    124124
    125125    CFixed() : value(0) { }