Revision 1602
Added some test files for fixed point math.
Something definitely broke in cosine when I changed to smaller tables
(which shouldn't have sacrificed any accuracy). Need to investigate...
fp_math.c  

9  9 
#define FP_TWO_PI 411775 
10  10  
11  11 
#define TABLE_LENGTH 64 
12 
#define TABLE_STEP 1621//.160757


13 
#define EXP_MAGICONSTANT 

12 
#define TABLE_STEP 1635//.160757


13 
#define EXP_MAGICONSTANT 726817 //ln(2^16) * 2^16


14  14  
15 
//#define DEBUG 

16  
17 
//TODO This can probably be removed at some point. 

15  18 
static int32_t linspace[TABLE_LENGTH] PROGMEM = { 
16 
0, 1621, 3242, 4863, 6485, 8106, 9727, 11348,


17 
12969, 14590, 16212, 17833, 19454, 21075, 22696, 24317,


18 
25939, 27560, 29181, 30802, 32423, 34044, 35666, 37287,


19 
38908, 40529, 42150, 43771, 45393, 47014, 48635, 50256,


20 
51877, 53498, 55119, 56741, 58362, 59983, 61604, 63225,


21 
64846, 66468, 68089, 69710, 71331, 72952, 74573, 76195,


22 
77816, 79437, 81058, 82679, 84300, 85922, 87543, 89164,


23 
90785, 92406, 94027, 95648, 97270, 98891, 100512, 102133


19 
0, 1634, 3268, 4902, 6536, 8170, 9804, 11438,


20 
13072, 14706, 16340, 17974, 19608, 21242, 22876, 24510,


21 
26144, 27778, 29412, 31047, 32681, 34315, 35949, 37583,


22 
39217, 40851, 42485, 44119, 45753, 47387, 49021, 50655,


23 
52289, 53923, 55557, 57191, 58825, 60459, 62093, 63727,


24 
65361, 66995, 68629, 70263, 71897, 73531, 75165, 76799,


25 
78433, 80067, 81701, 83335, 84969, 86603, 88237, 89871,


26 
91506, 93140, 94774, 96408, 98042, 99676, 101310, 102944,


24  27 
}; 
25  28  
26  29 
static int32_t fp_cos_table[TABLE_LENGTH] PROGMEM = { 
27 
65536, 65516, 65456, 65356, 65215, 65035, 64815, 64556,


28 
64257, 63919, 63541, 63125, 62670, 62176, 61645, 61076,


29 
60470, 59826, 59146, 58430, 57678, 56890, 56068, 55212,


30 
54322, 53398, 52442, 51454, 50434, 49384, 48303, 47193,


31 
46053, 44886, 43691, 42470, 41222, 39949, 38652, 37331,


32 
35988, 34622, 33235, 31828, 30401, 28956, 27492, 26013,


33 
24517, 23006, 21481, 19943, 18393, 16831, 15260, 13679,


34 
12089, 10492, 8889, 7280, 5667, 4050, 2431, 811


30 
65536, 65516, 65455, 65353, 65210, 65027, 64804, 64540,


31 
64237, 63893, 63509, 63087, 62624, 62123, 61584, 61006,


32 
60390, 59736, 59046, 58319, 57555, 56756, 55921, 55052,


33 
54148, 53211, 52241, 51238, 50203, 49138, 48041, 46915,


34 
45760, 44576, 43364, 42126, 40861, 39571, 38256, 36918,


35 
35556, 34173, 32768, 31343, 29898, 28435, 26954, 25456,


36 
23943, 22415, 20872, 19317, 17750, 16171, 14583, 12986,


37 
11380, 9768, 8149, 6525, 4898, 3267, 1634, 0,


35  38 
}; 
36  39  
37  40 
//FIXME Lazy implementations of tangent and sine. 
...  ...  
48  51 
int32_t xsquare = fp_mult(x,x); 
49  52 
int32_t result, i; 
50  53 

51 
if(theta > EXP_MAGICONSTANT) {


54 
if(x > EXP_MAGICONSTANT) {


52  55 
usb_puts("Output value is out of range.\n\r"); 
53  56 
return 1; 
54  57 
} 
55  58 

56  59 
//This is again, massive amounts of lazyness. 
57  60 
//The approximation fails outside this range (approximately). 
58 
if(theta < 17)


61 
if(x < (17l << 16))


59  62 
return 0; 
60  63 

61  64 
result = xsquare; 
62 
for(i= (22 << 16); i > (2 << 16); i= (4 << 16)) {


65 
for(i= (22l << 16); i > (2l << 16); i= (4l << 16)) {


63  66 
result += i; 
64  67 
result = fp_div(xsquare, result); 
65  68 
} 
66  69 

67 
result += (2 << 16)  x; 

68 
return (1 << 16) + fp_div(fp_mult((2 << 16), x), result);


70 
result += (2l << 16)  x;


71 
return (1l << 16) + fp_div(fp_mult((2l << 16), x), result);


69  72  
70  73 
} 
71  74  
...  ...  
76  79 
int32_t x_n, x_np1, x_np2; 
77  80 
int32_t y_n, y_np1, y_np2; 
78  81 
int32_t dd_n, dd_np1, second_dd, result; 
79 


82  
83 
#ifdef DEBUG 

84 
char buf[128]; 

85 
#endif 

86  
80  87 
//Move theta into [0, pi/2] w/ appropriate sign. 
81  88 
theta = ABS(theta) % FP_TWO_PI; 
82  89  
...  ...  
90  97  
91  98 
//Find the nearest table values. FIXME 
92  99 
n = theta / TABLE_STEP; 
93 
while( n < TABLE_LENGTH  1 && (x_np1 = pgm_read_dword(&linspace[n+1]))) 

100 
while( n < TABLE_LENGTH  1 

101 
&& (x_np1 = pgm_read_dword(&linspace[n+1]) < theta)) 

94  102 
n++; 
95  103 

96  104 
//theta is between x_{n} and x_{n+1} 
105  
97  106 
if(n == TABLE_LENGTH  1) { 
98  107 
//Perform linear interpolation, since we're close to zero anyway. 
99  108 
x_n = pgm_read_dword(&linspace[TABLE_LENGTH  1]); 
100  109 
y_n = pgm_read_dword(&fp_cos_table[TABLE_LENGTH  1]); 
101  110  
102 
result = fp_mult(fp_div(FP_PI_OVER_TWO  x_n, 0  y_n), theta  x_n) + y_n; 

111 
result = fp_mult( 

112 
fp_div(FP_PI_OVER_TWO  x_n, 0  y_n), theta  x_n) + y_n; 

113 


103  114 
return negative ? result : result; 
104  115 
} 
105  116  
106  117 
if(n == TABLE_LENGTH) { 
107  118 
//We didn't find a value! Oh no! 
108 
usb_puts("fp_math: We couldn't find surrounding table values! \n\r


109 
This should never happen!!!\n\r\n\r"); 

119 
usb_puts("fp_math: Fatal! We couldn't find surrounding table values! \n\r");


120 


110  121 
return 0; 
111  122 
} 
112 


123  
113  124 
//Address the general case. Quadratic interpolation. 
114  125 
//Load in the necessary values. 
115  126 
x_n = pgm_read_dword(&linspace[n]); 
127 
x_np1 = pgm_read_dword(&linspace[n + 1]); 

116  128 
x_np2 = pgm_read_dword(&linspace[n + 2]); 
117  129 

118  130 
y_n = pgm_read_dword(&fp_cos_table[n]); 
119  131 
y_np1 = pgm_read_dword(&fp_cos_table[n + 1]); 
120  132 
y_np2 = pgm_read_dword(&fp_cos_table[n + 2]); 
121  133  
134 
#ifdef DEBUG 

135 
sprintf(buf, "x_n = %ld, theta = %ld, x_{n+1} = %ld", x_n, theta, x_np1); 

136 
usb_puts(buf); 

137 
#endif 

138  
122  139 
//Calculate first divided differences. 
123  140 
dd_n = fp_div(y_np1  y_n, x_np1  x_n); 
124  141 
dd_np1 = fp_div(y_np2  y_np1, x_np2  x_np1); 
Also available in: Unified diff