Ticket #2109: cordic_patch.patch
File cordic_patch.patch, 7.8 KB (added by , 9 years ago) |
---|
-
source/maths/Fixed.cpp
1 /* Copyright (C) 201 0Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 143 143 return r.str(); 144 144 } 145 145 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) 146 typedef enum 148 147 { 149 CFixed_15_16 zero; 148 QUAD1, 149 QUAD2, 150 QUAD3, 151 QUAD4 152 }CORDIC_OUTPUT_STATE; 150 153 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 155 static 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 157 static const CFixed_15_16 angle_0_5_pi = CFixed_15_16(102944); 158 static const CFixed_15_16 angle_1_0_pi = CFixed_15_16(205887); 159 static const CFixed_15_16 angle_1_5_pi = CFixed_15_16(308831); 160 static const CFixed_15_16 angle_2_0_pi = CFixed_15_16(411775); 161 static const CFixed_15_16 zero = CFixed_15_16(); 154 162 155 CFixed_15_16 c1; 156 c1.SetInternalValue(51472); // pi/4 << 16 163 CFixed_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; 157 170 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 } 160 178 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 } 162 185 163 CFixed_15_16 angle;164 if (x >=zero)186 //quadrant 4 187 else if (x > zero && y < zero) 165 188 { 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; 168 191 } 169 else 192 193 //quadrant 3 194 else if (x < zero && y < zero) 170 195 { 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; 173 199 } 174 200 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: 178 237 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 } 179 247 } 180 248 181 249 template<> … … 186 254 187 255 void sincos_approx(CFixed_15_16 a, CFixed_15_16& sin_out, CFixed_15_16& cos_out) 188 256 { 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; 190 265 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 } 192 271 193 typedef CFixed_15_16 fixed; 272 if (a < angle_0_5_pi) 273 output_state = (is_positive_angle ? QUAD1 : QUAD4); 194 274 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) 204 276 { 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); 207 279 } 208 else if ( z >= fixed::FromInt(2)) // [2, 3)280 else if (a < angle_1_5_pi) 209 281 { 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); 212 284 } 213 else if (z >= fixed::FromInt(1)) // [1, 2)285 else 214 286 { 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); 217 289 } 218 else // [0, 1) 290 291 for (loop_var = 0; loop_var < 8; ++loop_var) 219 292 { 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); 223 305 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); 225 317 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 } 228 321 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 } 236 344 } -
source/maths/Fixed.h
1 /* Copyright (C) 201 3Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 117 117 private: 118 118 T value; 119 119 120 public: 121 120 122 explicit CFixed(T v) : value(v) { } 121 122 public:123 123 enum { fract_bits = fract_bits_ }; 124 124 125 125 CFixed() : value(0) { }