Ticket #2109: sincos_approx.patch

File sincos_approx.patch, 5.7 KB (added by Thanduel, 11 years ago)

patch to replace current sincos_approx with cordic sincos approx

  • source/maths/Fixed.cpp

     
    143143    return r.str();
    144144}
    145145
     146//angles: 45, 26.565, 14.036, 7.125, 3.576, 1.790, 0.895, 0.448
     147static 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) };
     148//angles 90 and 180
     149static const CFixed_15_16 angle_0_5_pi = CFixed_15_16(102944);
     150static const CFixed_15_16 angle_1_0_pi = CFixed_15_16(205887);
     151static const CFixed_15_16 angle_1_5_pi = CFixed_15_16(308830);
     152static const CFixed_15_16 zero = CFixed_15_16();
     153
    146154// Based on http://www.dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization
    147155CFixed_15_16 atan2_approx(CFixed_15_16 y, CFixed_15_16 x)
    148156{
     
    184192    return CFixed_15_16(205887); // = pi << 16
    185193}
    186194
     195typedef enum
     196{
     197    QUAD1,
     198    QUAD2,
     199    QUAD3,
     200    QUAD4
     201}SINCOS_OUTPUT_STATE;
     202
    187203void sincos_approx(CFixed_15_16 a, CFixed_15_16& sin_out, CFixed_15_16& cos_out)
    188204{
    189     // Based on http://www.coranac.com/2009/07/sines/
     205    CFixed_15_16 angle = CFixed_15_16();
     206    CFixed_15_16 y = CFixed_15_16();
     207    CFixed_15_16 x = CFixed_15_16(39797); //cordic gain for 8 angles
     208    i32 loop_var;
     209    i32 previous_x_val;
     210    i32 new_x_val;
     211    SINCOS_OUTPUT_STATE output_state;
     212    bool is_positive_angle = true;
    190213
    191     // TODO: this could be made a bit more precise by being careful about scaling
     214    if(a < zero)
     215    {
     216        is_positive_angle = false;
     217        a.SetInternalValue(a.GetInternalValue() * -1);
     218    }
    192219
    193     typedef CFixed_15_16 fixed;
     220    if(a < angle_0_5_pi)
     221        output_state = (is_positive_angle? QUAD1: QUAD4);
    194222
    195     fixed c2_pi;
    196     c2_pi.SetInternalValue(41721); // = 2/pi << 16
     223    else if(a < angle_1_0_pi)
     224    {
     225        a -= angle_0_5_pi;
     226        output_state = (is_positive_angle? QUAD2: QUAD3);
     227    }
     228    else if(a < angle_1_5_pi)
     229    {
     230        a -= angle_1_0_pi;
     231        output_state = (is_positive_angle? QUAD3: QUAD2);
     232    }
     233    else
     234    {
     235        a -= angle_1_5_pi;
     236        output_state = (is_positive_angle? QUAD4: QUAD1);
     237    }
    197238
    198     // Map radians onto the range [0, 4)
    199     fixed z = a.Multiply(c2_pi) % fixed::FromInt(4);
     239    for(loop_var = 0; loop_var < 8; ++loop_var)
     240    {
     241        i32 factor;
     242        //these divisions should be optimised by the compiler to shifts because the operations are always division by a power of two
     243        //this makes the cost of each division 1 cycle.
     244        if( angle > a )
     245        {
     246            factor = (1 << loop_var);
     247            previous_x_val = x.GetInternalValue();
     248            x -= (y/factor);
     249            new_x_val = x.GetInternalValue();
     250            x.SetInternalValue(previous_x_val);
     251            y += (x/factor);
     252            x.SetInternalValue(new_x_val);
    200253
    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)
    204     {
    205         sz = z - fixed::FromInt(4);
    206         cz = z - fixed::FromInt(3);
    207     }
    208     else if (z >= fixed::FromInt(2)) // [2, 3)
    209     {
    210         sz = fixed::FromInt(2) - z;
    211         cz = z - fixed::FromInt(3);
    212     }
    213     else if (z >= fixed::FromInt(1)) // [1, 2)
    214     {
    215         sz = fixed::FromInt(2) - z;
    216         cz = fixed::FromInt(1) - z;
    217     }
    218     else // [0, 1)
    219     {
    220         sz = z;
    221         cz = fixed::FromInt(1) - z;
    222     }
     254            angle -= tan_angle_table_rad[loop_var];
     255        }
     256        else
     257        {
     258            factor = (1 << loop_var);
     259            previous_x_val = x.GetInternalValue();
     260            x += (y/factor);
     261            new_x_val = x.GetInternalValue();
     262            x.SetInternalValue(previous_x_val);
     263            y -= (x/factor);
     264            x.SetInternalValue(new_x_val);
    223265
    224     // Third-order (max absolute error ~0.02)
     266            angle += tan_angle_table_rad[loop_var];
     267        }
     268    }
    225269
    226 //  sin_out = (sz / 2).Multiply(fixed::FromInt(3) - sz.Multiply(sz));
    227 //  cos_out = (cz / 2).Multiply(fixed::FromInt(3) - cz.Multiply(cz));
    228 
    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;
     270    //the cases are contiguous therefore this should be optimised to a jump table
     271    switch(output_state)
     272    {
     273        case QUAD1:
     274            sin_out.SetInternalValue(y.GetInternalValue() * -1);
     275            cos_out.SetInternalValue(x.GetInternalValue());
     276            return;
     277        case QUAD2:
     278            sin_out.SetInternalValue(y.GetInternalValue() * -1);
     279            cos_out.SetInternalValue(x.GetInternalValue() * -1);
     280            return;
     281        case QUAD3:
     282            sin_out.SetInternalValue(y.GetInternalValue());
     283            cos_out.SetInternalValue(x.GetInternalValue() * -1);
     284            return;
     285        case QUAD4:
     286            sin_out.SetInternalValue(y.GetInternalValue());
     287            cos_out.SetInternalValue(x.GetInternalValue());
     288            return;
     289        default:
     290            return;
     291    }
    236292}
  • source/maths/Fixed.h

     
    109109private:
    110110    T value;
    111111
    112     explicit CFixed(T v) : value(v) { }
    113 
    114112public:
    115113    enum { fract_bits = fract_bits_ };
    116114
    117115    CFixed() : value(0) { }
     116    explicit CFixed(T v) : value(v) { }
    118117
    119118    static CFixed Zero() { return CFixed(0); }
    120119    static CFixed Epsilon() { return CFixed(1); }