5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
26 int8_t * curveEnd
[MAX_CURVES
];
29 bool showWarning
= false;
30 int8_t * tmp
= g_model
.points
;
31 for (int i
=0; i
<MAX_CURVES
; i
++) {
32 switch (g_model
.curves
[i
].type
) {
33 case CURVE_TYPE_STANDARD
:
34 tmp
+= 5+g_model
.curves
[i
].points
;
36 case CURVE_TYPE_CUSTOM
:
37 tmp
+= 8+2*g_model
.curves
[i
].points
;
40 TRACE("Wrong curve type! Fixing...");
41 g_model
.curves
[i
].type
= CURVE_TYPE_STANDARD
;
42 tmp
+= 5+g_model
.curves
[i
].points
;
45 // Older version did not check if we exceeded the array
46 int8_t * maxend
= &g_model
.points
[MAX_CURVE_POINTS
- 2*(MAX_CURVES
-i
-1)];
49 g_model
.curves
[i
].type
=CURVE_TYPE_STANDARD
;
50 g_model
.curves
[i
].points
=-3;
57 POPUP_WARNING("Invalid curve data repaired");
58 const char * w
= "check your curves, logic switches";
59 SET_WARNING_INFO(w
, strlen(w
), 0);
63 int8_t * curveAddress(uint8_t idx
)
65 return idx
==0 ? g_model
.points
: curveEnd
[idx
-1];
68 bool moveCurve(uint8_t index
, int8_t shift
)
70 if (curveEnd
[MAX_CURVES
-1] + shift
> g_model
.points
+ sizeof(g_model
.points
)) {
75 int8_t * nextCrv
= curveAddress(index
+1);
76 memmove(nextCrv
+shift
, nextCrv
, 5*(MAX_CURVES
-index
-1)+curveEnd
[MAX_CURVES
-1]-curveEnd
[index
]);
77 if (shift
< 0) memclear(&g_model
.points
[MAX_CURVE_POINTS
-1] + shift
, -shift
);
78 while (index
<MAX_CURVES
) {
79 curveEnd
[index
++] += shift
;
82 storageDirty(EE_MODEL
);
86 int8_t getCurveX(int noPoints
, int point
)
88 return -100 + div_and_round((point
*2000) / (noPoints
-1), 10);
91 void resetCustomCurveX(int8_t * points
, int noPoints
)
93 for (int i
=0; i
<noPoints
-2; i
++) {
94 points
[noPoints
+i
] = getCurveX(noPoints
, i
+1);
98 int8_t * curveAddress(uint8_t idx
)
100 return &g_model
.points
[idx
==0 ? 0 : 5*idx
+g_model
.curves
[idx
-1]];
103 CurveInfo
curveInfo(uint8_t idx
)
106 result
.crv
= curveAddress(idx
);
107 int8_t *next
= curveAddress(idx
+1);
108 uint8_t size
= next
- result
.crv
;
109 if ((size
& 1) == 0) {
110 result
.points
= (size
/ 2) + 1;
111 result
.custom
= true;
114 result
.points
= size
;
115 result
.custom
= false;
120 bool moveCurve(uint8_t index
, int8_t shift
, int8_t custom
) // TODO move?
122 if (g_model
.curves
[MAX_CURVES
-1] + shift
> MAX_CURVE_POINTS
-5*MAX_CURVES
) {
127 int8_t * crv
= curveAddress(index
);
129 for (uint8_t i
=0; i
<custom
; i
++)
133 int8_t * nextCrv
= curveAddress(index
+1);
134 memmove(nextCrv
+shift
, nextCrv
, 5*(MAX_CURVES
-index
-1)+g_model
.curves
[MAX_CURVES
-1]-g_model
.curves
[index
]);
135 if (shift
< 0) memclear(&g_model
.points
[MAX_CURVE_POINTS
-1] + shift
, -shift
);
136 while (index
<MAX_CURVES
) {
137 g_model
.curves
[index
++] += shift
;
140 for (uint8_t i
=0; i
<custom
-2; i
++) {
141 crv
[custom
+ i
] = -100 + ((200 * (i
+ 1) + custom
/ 2) / (custom
- 1));
144 storageDirty(EE_MODEL
);
150 #define CUSTOM_POINT_X(points, count, idx) ((idx)==0 ? -100 : (((idx)==(count)-1) ? 100 : points[(count)+(idx)-1]))
151 int32_t compute_tangent(CurveInfo
* crv
, int8_t * points
, int i
)
154 uint8_t num_points
= crv
->points
+ 5;
157 //linear interpolation between 1st 2 points
158 //keep 3 decimal-places for m
159 if (crv
->type
== CURVE_TYPE_CUSTOM
) {
160 int8_t x0
= CUSTOM_POINT_X(points
, num_points
, 0);
161 int8_t x1
= CUSTOM_POINT_X(points
, num_points
, 1);
162 if (x1
> x0
) m
= (MMULT
* (points
[1] - points
[0])) / (x1
- x0
);
165 int32_t delta
= (2 * 100) / (num_points
- 1);
166 m
= (MMULT
* (points
[1] - points
[0])) / delta
;
169 else if (i
== num_points
- 1) {
170 //linear interpolation between last 2 points
171 //keep 3 decimal-places for m
172 if (crv
->type
== CURVE_TYPE_CUSTOM
) {
173 int8_t x0
= CUSTOM_POINT_X(points
, num_points
, num_points
-2);
174 int8_t x1
= CUSTOM_POINT_X(points
, num_points
, num_points
-1);
175 if (x1
> x0
) m
= (MMULT
* (points
[num_points
-1] - points
[num_points
-2])) / (x1
- x0
);
178 int32_t delta
= (2 * 100) / (num_points
- 1);
179 m
= (MMULT
* (points
[num_points
-1] - points
[num_points
-2])) / delta
;
183 //apply monotone rules from
184 //http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
185 //1) compute slopes of secant lines
187 if (crv
->type
== CURVE_TYPE_CUSTOM
) {
188 int8_t x0
= CUSTOM_POINT_X(points
, num_points
, i
-1);
189 int8_t x1
= CUSTOM_POINT_X(points
, num_points
, i
);
190 int8_t x2
= CUSTOM_POINT_X(points
, num_points
, i
+1);
191 if (x1
> x0
) d0
= (MMULT
* (points
[i
] - points
[i
-1])) / (x1
- x0
);
192 if (x2
> x1
) d1
= (MMULT
* (points
[i
+1] - points
[i
])) / (x2
- x1
);
195 int32_t delta
= (2 * 100) / (num_points
- 1);
196 d0
= (MMULT
* (points
[i
] - points
[i
-1])) / (delta
);
197 d1
= (MMULT
* (points
[i
+1] - points
[i
])) / (delta
);
199 //2) compute initial average tangent
201 //3 check for horizontal lines
202 if (d0
== 0 || d1
== 0 || (d0
> 0 && d1
< 0) || (d0
< 0 && d1
> 0)) {
205 else if (MMULT
* m
/ d0
> 3 * MMULT
) {
208 else if (MMULT
* m
/ d1
> 3 * MMULT
) {
215 /* The following is a hermite cubic spline.
216 The basis functions can be found here:
217 http://en.wikipedia.org/wiki/Cubic_Hermite_spline
218 The tangents are computed via the 'cubic monotone' rules (allowing for local-maxima)
220 int16_t hermite_spline(int16_t x
, uint8_t idx
)
222 CurveInfo
&crv
= g_model
.curves
[idx
];
223 int8_t *points
= curveAddress(idx
);
224 uint8_t count
= crv
.points
+5;
225 bool custom
= (crv
.type
== CURVE_TYPE_CUSTOM
);
232 for (int i
=0; i
<count
-1; i
++) {
235 p0x
= (i
>0 ? calc100toRESX(points
[count
+i
-1]) : -RESX
);
236 p3x
= (i
<count
-2 ? calc100toRESX(points
[count
+i
]) : RESX
);
239 p0x
= -RESX
+ (i
*2*RESX
)/(count
-1);
240 p3x
= -RESX
+ ((i
+1)*2*RESX
)/(count
-1);
243 if (x
>= p0x
&& x
<= p3x
) {
244 int32_t p0y
= calc100toRESX(points
[i
]);
245 int32_t p3y
= calc100toRESX(points
[i
+1]);
246 int32_t m0
= compute_tangent(&crv
, points
, i
);
247 int32_t m3
= compute_tangent(&crv
, points
, i
+1);
249 int32_t h
= p3x
- p0x
;
250 int32_t t
= (h
> 0 ? (MMULT
* (x
- p0x
)) / h
: 0);
251 int32_t t2
= t
* t
/ MMULT
;
252 int32_t t3
= t2
* t
/ MMULT
;
253 int32_t h00
= 2*t3
- 3*t2
+ MMULT
;
254 int32_t h10
= t3
- 2*t2
+ t
;
255 int32_t h01
= -2*t3
+ 3*t2
;
256 int32_t h11
= t3
- t2
;
257 y
= p0y
* h00
+ h
* (m0
* h10
/ MMULT
) + p3y
* h01
+ h
* (m3
* h11
/ MMULT
);
266 int intpol(int x
, uint8_t idx
) // -100, -75, -50, -25, 0 ,25 ,50, 75, 100
269 CurveInfo
& crv
= g_model
.curves
[idx
];
270 int8_t * points
= curveAddress(idx
);
271 uint8_t count
= crv
.points
+5;
272 bool custom
= (crv
.type
== CURVE_TYPE_CUSTOM
);
274 CurveInfo crv
= curveInfo(idx
);
275 int8_t * points
= crv
.crv
;
276 uint8_t count
= crv
.points
;
277 bool custom
= crv
.custom
;
284 erg
= (int16_t)points
[0] * (RESX
/4);
286 else if (x
>= (RESX
*2)) {
287 erg
= (int16_t)points
[count
-1] * (RESX
/4);
293 for (i
=0; i
<count
-1; i
++) {
295 b
= (i
==count
-2 ? 2*RESX
: RESX
+ calc100toRESX(points
[count
+i
]));
296 if ((uint16_t)x
<=b
) break;
300 uint16_t d
= (RESX
* 2) / (count
-1);
305 erg
= (int16_t)points
[i
]*(RESX
/4) + ((int32_t)(x
-a
) * (points
[i
+1]-points
[i
]) * (RESX
/4)) / ((b
-a
));
308 return erg
/ 25; // 100*D5/RESX;
312 int applyCurve(int x
, CurveRef
& curve
)
314 switch (curve
.type
) {
317 int curveParam
= GET_GVAR_PREC1(curve
.value
, -100, 100, mixerCurrentFlightMode
);
318 if (curveParam
> 0 && x
< 0)
319 x
= (x
* (1000 - curveParam
)) / 1000;
320 else if (curveParam
< 0 && x
> 0)
321 x
= (x
* (1000 + curveParam
)) / 1000;
327 int curveParam
= GET_GVAR_PREC1(curve
.value
, -100, 100, mixerCurrentFlightMode
) / 10;
328 return expo(x
, curveParam
);
332 switch (curve
.value
) {
334 if (x
< 0) x
= 0; //x|x>0
337 if (x
> 0) x
= 0; //x|x<0
339 case CURVE_ABS_X
: // x|abs(x)
341 case CURVE_F_GT0
: //f|f>0
342 return x
> 0 ? RESX
: 0;
343 case CURVE_F_LT0
: //f|f<0
344 return x
< 0 ? -RESX
: 0;
345 case CURVE_ABS_F
: //f|abs(f)
346 return x
> 0 ? RESX
: -RESX
;
350 case CURVE_REF_CUSTOM
:
352 int curveParam
= curve
.value
;
353 if (curveParam
< 0) {
355 curveParam
= -curveParam
;
357 if (curveParam
> 0 && curveParam
<= MAX_CURVES
) {
358 return applyCustomCurve(x
, curveParam
- 1);
367 int applyCustomCurve(int x
, uint8_t idx
)
369 if (idx
>= MAX_CURVES
)
372 CurveInfo
& crv
= g_model
.curves
[idx
];
374 return hermite_spline(x
, idx
);
376 return intpol(x
, idx
);
379 int applyCurve(int x
, int8_t idx
)
381 /* already tried to have only one return at the end */
386 if (x
< 0) x
= 0; //x|x>0
389 if (x
> 0) x
= 0; //x|x<0
391 case CURVE_ABS_X
: // x|abs(x)
393 case CURVE_F_GT0
: //f|f>0
394 return x
> 0 ? RESX
: 0;
395 case CURVE_F_LT0
: //f|f<0
396 return x
< 0 ? -RESX
: 0;
397 case CURVE_ABS_F
: //f|abs(f)
398 return x
> 0 ? RESX
: -RESX
;
402 idx
= -idx
+ CURVE_BASE
- 1;
404 return applyCustomCurve(x
, idx
- CURVE_BASE
);
408 point_t
getPoint(uint8_t i
)
410 point_t result
= {0, 0};
412 CurveInfo
& crv
= g_model
.curves
[s_curveChan
];
413 int8_t * points
= curveAddress(s_curveChan
);
414 bool custom
= (crv
.type
== CURVE_TYPE_CUSTOM
);
415 uint8_t count
= 5+crv
.points
;
417 CurveInfo crv
= curveInfo(s_curveChan
);
418 int8_t * points
= crv
.crv
;
419 bool custom
= crv
.custom
;
420 uint8_t count
= crv
.points
;
423 result
.x
= CURVE_CENTER_X
-1-CURVE_SIDE_WIDTH
+ i
*CURVE_SIDE_WIDTH
*2/(count
-1);
424 result
.y
= CURVE_CENTER_Y
- (points
[i
]) * (CURVE_SIDE_WIDTH
-1) / 100;
425 if (custom
&& i
>0 && i
<count
-1) {
426 result
.x
= CURVE_CENTER_X
- 1 - CURVE_SIDE_WIDTH
+ (100 + (100 + points
[count
+ i
- 1]) * (2 * CURVE_SIDE_WIDTH
)) / 200;
432 int applyCurrentCurve(int x
)
434 return applyCustomCurve(x
, s_curveChan
);