3 Copyright 2008 Fred Cooke
5 This file is part of the FreeEMS project.
7 FreeEMS software is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 FreeEMS software is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with any FreeEMS software. If not, see <http://www.gnu.org/licenses/>.
20 We ask that if you make any changes to this file you send them upstream to us at admin@diyefi.org
22 Thank you for choosing FreeEMS to run your engine! */
25 #include "inc/freeEMS.h"
26 #include "inc/commsISRs.h"
27 #include "inc/tableLookup.h"
30 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
31 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
32 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* ******* ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
33 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* WARNING ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
34 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* ******* ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
35 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
36 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
37 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
38 /*&&&&&&&&&&&&&&&&&&&& These routines rely on the fact that there are no ISRs trying &&&&&&&&&&&&&&&&&&&*/
39 /*&&&&&&&&&&&&&&&&&&&& to access the small tables and other live settings in the ram &&&&&&&&&&&&&&&&&&&*/
40 /*&&&&&&&&&&&&&&&&&&&& window as specified by the RPAGE value. If they are then bad &&&&&&&&&&&&&&&&&&&*/
41 /*&&&&&&&&&&&&&&&&&&&& values WILL be occasionally read from random parts of the big &&&&&&&&&&&&&&&&&&&*/
42 /*&&&&&&&&&&&&&&&&&&&& tables instead of the correct place. If that occurs it WILL &&&&&&&&&&&&&&&&&&&*/
43 /*&&&&&&&&&&&&&&&&&&&& cause unpredictable and VERY hard to find bugs!! &&&&&&&&&&&&&&&&&&&*/
44 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
45 /*&&&&&&&&&&&&&&&&&&&& ******* ******* YOU HAVE BEEN WARNED!!! ******* ******* &&&&&&&&&&&&&&&&&&&*/
46 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
47 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
48 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
51 /* Yet to be implemented :
53 unsigned char lookup8Bit3dUC(
54 unsigned short lookup16Bit2dUS(
55 unsigned char lookup8Bit2dUC(
56 signed short lookup16Bit3dSS(
57 signed short lookup16Bit3dSS(
58 signed char lookup8Bit3D( */
61 /* Take a table with two movable axis sets and two axis lengths,
62 * loop to find which pairs of axis values and indexs we are between,
63 * interpolate two pairs down to two values,
64 * interpolate two values down to one value.
66 * Warning : This function relies on the axis values being a sorted list from low to high. If this is not the case behaviour is undefined and could include memory corruption and engine damage.
68 * To reduce the table size from 19x24 to something smaller, simply reduce the RPMLength and LoadLength fields to lower values. Increasing the size of either axis is not currently possible.
70 * Given that the axis lists are in order, a data point outside the table will give the value adjacent to it, and one outside one of the four corners will give the corner value. This is a clean and reasonable behaviour in my opinion.
72 * Reminder : X/RPM is horizontal, Y/Load is vertical
74 * Save the page value to a variable, change the page value, read the table, change the page back and return the value */
75 unsigned short lookupPagedMainTableCellValue(mainTable
*Table
, unsigned short realRPM
, unsigned short realLoad
, unsigned char RPageValue
){
77 /* Save the RPAGE value for restoration and switch pages. */
78 unsigned char oldRPage
= RPAGE
;
81 /* Find the bounding axis values and indices for RPM */
82 unsigned char lowRPMIndex
= 0;
83 unsigned char highRPMIndex
= Table
->RPMLength
- 1;
84 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
85 unsigned short lowRPMValue
= Table
->RPM
[0];
86 unsigned short highRPMValue
= Table
->RPM
[Table
->RPMLength
-1];
88 unsigned char RPMIndex
;
89 for(RPMIndex
=0;RPMIndex
<Table
->RPMLength
;RPMIndex
++){
90 if(Table
->RPM
[RPMIndex
] < realRPM
){
91 lowRPMValue
= Table
->RPM
[RPMIndex
];
92 lowRPMIndex
= RPMIndex
;
93 }else if(Table
->RPM
[RPMIndex
] > realRPM
){
94 highRPMValue
= Table
->RPM
[RPMIndex
];
95 highRPMIndex
= RPMIndex
;
97 }else if(Table
->RPM
[RPMIndex
] == realRPM
){
98 lowRPMValue
= Table
->RPM
[RPMIndex
];
99 highRPMValue
= Table
->RPM
[RPMIndex
];
100 lowRPMIndex
= RPMIndex
;
101 highRPMIndex
= RPMIndex
;
107 /* Find the bounding cell values and indices for Load */
108 unsigned char lowLoadIndex
= 0;
109 unsigned char highLoadIndex
= Table
->LoadLength
-1;
110 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
111 unsigned short lowLoadValue
= Table
->Load
[0];
112 unsigned short highLoadValue
= Table
->Load
[Table
->LoadLength
-1];
114 unsigned char LoadIndex
;
115 for(LoadIndex
=0;LoadIndex
<Table
->LoadLength
;LoadIndex
++){
116 if(Table
->Load
[LoadIndex
] < realLoad
){
117 lowLoadValue
= Table
->Load
[LoadIndex
];
118 lowLoadIndex
= LoadIndex
;
119 }else if(Table
->Load
[LoadIndex
] > realLoad
){
120 highLoadValue
= Table
->Load
[LoadIndex
];
121 highLoadIndex
= LoadIndex
;
123 }else if(Table
->Load
[LoadIndex
] == realLoad
){
124 lowLoadValue
= Table
->Load
[LoadIndex
];
125 highLoadValue
= Table
->Load
[LoadIndex
];
126 lowLoadIndex
= LoadIndex
;
127 highLoadIndex
= LoadIndex
;
133 /* Obtain the four corners surrounding the spot of interest */
134 unsigned short lowRPMLowLoad
= Table
->Table
[(Table
->LoadLength
* lowRPMIndex
) + lowLoadIndex
];
135 unsigned short lowRPMHighLoad
= Table
->Table
[(Table
->LoadLength
* lowRPMIndex
) + highLoadIndex
];
136 unsigned short highRPMLowLoad
= Table
->Table
[(Table
->LoadLength
* highRPMIndex
) + lowLoadIndex
];
137 unsigned short highRPMHighLoad
= Table
->Table
[(Table
->LoadLength
* highRPMIndex
) + highLoadIndex
];
140 /* Restore the ram page before doing the math */
144 /* Find the two side values to interpolate between by interpolation */
145 unsigned short lowRPMIntLoad
= lowRPMLowLoad
+ (((signed long)((signed long)lowRPMHighLoad
- lowRPMLowLoad
) * (realLoad
- lowLoadValue
))/ (highLoadValue
- lowLoadValue
));
146 unsigned short highRPMIntLoad
= highRPMLowLoad
+ (((signed long)((signed long)highRPMHighLoad
- highRPMLowLoad
) * (realLoad
- lowLoadValue
))/ (highLoadValue
- lowLoadValue
));
149 /* Interpolate between the two side values and return the result */
150 return lowRPMIntLoad
+ (((signed long)((signed long)highRPMIntLoad
- lowRPMIntLoad
) * (realRPM
- lowRPMValue
))/ (highRPMValue
- lowRPMValue
));
154 unsigned short lookupTwoDTableUS(twoDTableUS
* Table
, unsigned short Value
){
156 /* Find the bounding axis indices, axis values and lookup values */
157 unsigned char lowIndex
= 0;
158 unsigned char highIndex
= 15;
159 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
160 unsigned short lowAxisValue
= Table
->Axis
[0];
161 unsigned short highAxisValue
= Table
->Axis
[15];
162 unsigned short lowLookupValue
= Table
->Values
[0];
163 unsigned short highLookupValue
= Table
->Values
[15];
166 for(Index
=0;Index
<16;Index
++){
167 if(Table
->Axis
[Index
] < Value
){
169 lowAxisValue
= Table
->Axis
[Index
];
170 lowLookupValue
= Table
->Values
[Index
];
171 }else if(Table
->Axis
[Index
] > Value
){
173 highAxisValue
= Table
->Axis
[Index
];
174 highLookupValue
= Table
->Values
[Index
];
176 }else if(Table
->Axis
[Index
] == Value
){
177 return Table
->Values
[Index
]; // If right on, just return the value
182 /* Interpolate and return the value */
183 return lowLookupValue
+ (((signed long)((signed long)highLookupValue
- lowLookupValue
) * (Value
- lowAxisValue
))/ (highAxisValue
- lowAxisValue
));
187 /* Returns 0 on success and error code otherwise */
188 unsigned short setAxisValue(unsigned short index
, unsigned short value
, unsigned short axis
[], unsigned short length
, unsigned short errorBase
){
190 return errorBase
+ invalidAxisIndex
;
193 /* Ensure value isn't lower than the one below */
194 if(axis
[index
- 1] > value
){
195 return errorBase
+ invalidAxisOrder
;
198 if(index
< (length
-1)){
199 /* Ensure value isn't higher than the one above */
200 if(value
> axis
[index
+ 1]){
201 return errorBase
+ invalidAxisOrder
;
206 /* If we got this far all is well, set the value */
212 unsigned short setPagedMainTableCellValue(unsigned char RPageValue
, mainTable
* Table
, unsigned short RPMIndex
, unsigned short LoadIndex
, unsigned short cellValue
){
213 unsigned char oldRPage
= RPAGE
;
214 unsigned short errorID
= 0;
216 if(RPMIndex
< Table
->RPMLength
){
217 if(LoadIndex
< Table
->LoadLength
){
218 Table
->Table
[(Table
->LoadLength
* RPMIndex
) + LoadIndex
] = cellValue
;
220 errorID
= invalidMainTableLoadIndex
;
223 errorID
= invalidMainTableRPMIndex
;
230 unsigned short setPagedMainTableRPMValue(unsigned char RPageValue
, mainTable
* Table
, unsigned short RPMIndex
, unsigned short RPMValue
){
231 unsigned char oldRPage
= RPAGE
;
233 unsigned short errorID
= setAxisValue(RPMIndex
, RPMValue
, Table
->RPM
, Table
->RPMLength
, errorBaseMainTableRPM
);
239 unsigned short setPagedMainTableLoadValue(unsigned char RPageValue
, mainTable
* Table
, unsigned short LoadIndex
, unsigned short LoadValue
){
240 unsigned char oldRPage
= RPAGE
;
242 unsigned short errorID
= setAxisValue(LoadIndex
, LoadValue
, Table
->Load
, Table
->LoadLength
, errorBaseMainTableLoad
);
248 unsigned short setPagedTwoDTableCellValue(unsigned char RPageValue
, twoDTableUS
* Table
, unsigned short cellIndex
, unsigned short cellValue
){
250 return invalidTwoDTableIndex
;
252 unsigned char oldRPage
= RPAGE
;
254 Table
->Values
[cellIndex
] = cellValue
;
261 unsigned short setPagedTwoDTableAxisValue(unsigned char RPageValue
, twoDTableUS
* Table
, unsigned short axisIndex
, unsigned short axisValue
){
262 unsigned char oldRPage
= RPAGE
;
264 unsigned short errorID
= setAxisValue(axisIndex
, axisValue
, Table
->Axis
, 16, errorBaseTwoDTableAxis
);
270 /* Check that the configuration of the table is valid. Assumes pages are correctly set. */
271 unsigned short validateMainTable(mainTable
* Table
){
272 /* If required and only if required extend this to take r and f pages and check */
273 /* any main table, not just a freshly received untrusted ones in linear space */
275 if(Table
->RPMLength
> MAINTABLE_MAX_RPM_LENGTH
){
276 return invalidMainTableRPMLength
;
277 }else if(Table
->LoadLength
> MAINTABLE_MAX_LOAD_LENGTH
){
278 return invalidMainTableLoadLength
;
279 }else if((Table
->RPMLength
* Table
->LoadLength
) > MAINTABLE_MAX_MAIN_LENGTH
){
280 return invalidMainTableMainLength
;
282 /* Check the order of the RPM axis */
284 for(i
=0;i
<(Table
->RPMLength
- 1);i
++){
285 if(Table
->RPM
[i
] > Table
->RPM
[i
+1]){
286 return invalidMainTableRPMOrder
;
289 /* Check the order of the Load axis */
291 for(j
=0;j
<(Table
->LoadLength
- 1);j
++){
292 if(Table
->Load
[j
] > Table
->Load
[j
+1]){
293 return invalidMainTableLoadOrder
;
296 /* If we made it this far all is well */
302 /* Check that the configuration of the table is valid */
303 unsigned short validateTwoDTable(twoDTableUS
* Table
){
304 /* Check the order of the axis */
306 for(i
=0;i
<(TWODTABLEUS_LENGTH
- 1);i
++){
307 if(Table
->Axis
[i
] > Table
->Axis
[i
+1]){
308 return invalidTwoDTableAxisOrder
;