Reformat: use tabs.
[bertos.git] / bertos / algo / ramp_test.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2004, 2008 Develer S.r.l. (http://www.develer.com/)
5  * All Rights Reserved.
6  * -->
7  *
8  * \brief Test for compute, save and load ramps for stepper motors (implementation)
9  *
10  * \version $Id$
11  *
12  * \author Simone Zinanni <s.zinanni@develer.com>
13  * \author Bernie Innocenti <bernie@codewiz.org>
14  * \author Giovanni Bajo <rasky@develer.com>
15  * \author Daniele Basile <asterix@develer.com>
16  *
17  *
18  * The formula used by the ramp is the following:
19  *
20  * <pre>
21  *            a * b
22  * f(t) = -------------
23  *         lerp(a,b,t)
24  * </pre>
25  *
26  * Where <code>a</code> and <code>b</code> are the maximum and minimum speed
27  * respectively (minimum and maximum wavelength respectively), and <code>lerp</code>
28  * is a linear interpolation with a factor:
29  *
30  * <pre>
31  * lerp(a,b,t) =  a + t * (b - a)  =  (a * (1 - t)) + (b * t)
32  * </pre>
33  *
34  * <code>t</code> must be in the [0,1] interval. It is easy to see that the
35  * following holds true:
36  *
37  * <pre>
38  * f(0) = b,   f(1) = a
39  * </pre>
40  *
41  * And that the function is monotonic. So, the function effectively interpolates
42  * between the maximum and minimum speed through its domain ([0,1] -> [b,a]).
43  *
44  * The curve drawn by this function is similar to 1 / (sqrt(n)), so it is slower
45  * than a linear acceleration (which would be 1/n).
46  *
47  * The floating point version uses a slightly modified function which accepts
48  * the parameter in the domain [0, MT] (where MT is maxTime, the length of the
49  * ramp, which is a setup parameter for the ramp). This is done to reduce the
50  * number of operations per step. The formula looks like this:
51  *
52  * <pre>
53  *               a * b * MT
54  * g(t) = ----------------------------
55  *           (a * MT) + t * (b - a)
56  * </pre>
57  *
58  * It can be shown that this <code>g(t) = f(t * MT)</code>. The denominator
59  * is a linear interpolation in the range [b*MT, a*MT], as t moves in the
60  * interval [0, MT]. So the interpolation interval of the function is again
61  * [b, a]. The implementation caches the value of the numerator and parts
62  * of the denominator, so that the formula becomes:
63  *
64  * <pre>
65  * alpha = a * b * MT
66  * beta = a * MT
67  * gamma = b - a
68  *
69  *                alpha
70  * g(t) = ----------------------
71  *           beta + t * gamma
72  * </pre>
73  *
74  * and <code>t</code> is exactly the parameter that ramp_evaluate() gets,
75  * that is the current time (in range [0, MT]). The operations performed
76  * for each step are just an addition, a multiplication and a division.
77  *
78  * The fixed point version of the formula instead transforms the original
79  * function as follows:
80  *
81  * <pre>
82  *                   a * b                         a
83  *  f(t) =  -------------------------  =  --------------------
84  *                 a                         a
85  *           b * ( - * (1 - t) + t )         - * (1 - t) + t
86  *                 b                         b
87  * </pre>
88  *
89  * <code>t</code> must be computed by dividing the current time (24 bit integer)
90  * by the maximum time (24 bit integer). This is done by precomputing the
91  * reciprocal of the maximum time as a 0.32 fixed point number, and multiplying
92  * it to the current time. Multiplication is performed 8-bits a time by
93  * FIX_MULT32(), so that we end up with a 0.16 fixed point number for
94  * <code>t</code> (and <code>1-t</code> is just its twos-complement negation).
95  * <code>a/b</code> is in the range [0,1] (because a is always less than b,
96  * being the minimum wavelength), so it is precomputed as a 0.16 fixed point.
97  * The final step is then computing the denominator and executing the division
98  * (32 cycles using the 1-step division instruction in the DSP).
99  *
100  * The assembly implementation is needed for efficiency, but a C version of it
101  * can be easily written, in case it is needed in the future.
102  *
103  */
104
105 #include "ramp.h"
106 #include <cfg/debug.h>
107 #include <cfg/test.h>
108
109
110 static bool ramp_test_single(uint32_t minFreq, uint32_t maxFreq, uint32_t length)
111 {
112         struct Ramp r;
113         uint16_t cur, old;
114         uint32_t clock;
115         uint32_t oldclock;
116
117         ramp_setup(&r, length, minFreq, maxFreq);
118
119         cur = old = r.clocksMaxWL;
120         clock = 0;
121         oldclock = 0;
122
123         kprintf("testing ramp: (length=%lu, min=%lu, max=%lu)\n", (unsigned long)length, (unsigned long)minFreq, (unsigned long)maxFreq);
124         kprintf("              [length=%lu, max=%04x, min=%04x]\n", (unsigned long)r.clocksRamp, r.clocksMaxWL, r.clocksMinWL);
125
126         int i = 0;
127         int nonbyte = 0;
128
129         while (clock + cur < r.clocksRamp)
130         {
131                 oldclock = clock;
132                 old = cur;
133
134                 clock += cur;
135                 cur = ramp_evaluate(&r, clock);
136
137                 if (old < cur)
138                 {
139                         uint16_t t1 = FIX_MULT32(oldclock >> RAMP_CLOCK_SHIFT_PRECISION, r.precalc.inv_total_time);
140                         uint16_t t2 = FIX_MULT32(clock >> RAMP_CLOCK_SHIFT_PRECISION,    r.precalc.inv_total_time);
141                         uint16_t denom1 = FIX_MULT32((uint16_t)((~t1) + 1), r.precalc.max_div_min) + t1;
142                         uint16_t denom2 = FIX_MULT32((uint16_t)((~t2) + 1), r.precalc.max_div_min) + t2;
143
144                         kprintf("    Failed: %04x @ %lu   -->   %04x @ %lu\n", old, (unsigned long)oldclock, cur, (unsigned long)clock);
145                         kprintf("    T:     %04x -> %04x\n", t1, t2);
146                         kprintf("    DENOM: %04x -> %04x\n", denom1, denom2);
147
148                         cur = ramp_evaluate(&r, clock);
149                         return false;
150                 }
151                 i++;
152                 if ((old-cur) >= 256)
153                         nonbyte++;
154         }
155
156
157
158         kprintf("Test finished: %04x @ %lu [min=%04x, totlen=%lu, numsteps:%d, nonbyte:%d]\n", cur, (unsigned long)clock, r.clocksMinWL, (unsigned long)r.clocksRamp, i, nonbyte);
159
160         return true;
161 }
162
163 int ramp_testSetup(void)
164 {
165         kdbg_init();
166         return 0;
167 }
168
169 int ramp_testTearDown(void)
170 {
171         return 0;
172 }
173
174 int ramp_testRun(void)
175 {
176         #define TEST_RAMP(min, max, len) do { \
177                 if (!ramp_test_single(min, max, len)) \
178                         return -1; \
179         } while(0)
180
181         TEST_RAMP(200,  5000, 3000000);
182         TEST_RAMP(1000, 2000, 1000000);
183
184         return 0;
185 }
186
187 #if UNIT_TEST
188         #include "ramp.c"
189         #include <drv/kdebug.c>
190         #include <mware/formatwr.c>
191         #include <mware/hex.c>
192
193         TEST_MAIN(ramp);
194 #endif
195