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 /** Looks up a value from a main table using interpolation.
65 * Take a table with two movable axis sets and two axis lengths,
66 * loop to find which pairs of axis values and indexs we are between,
67 * interpolate two pairs down to two values,
68 * interpolate two values down to one value.
72 * To reduce the table size from 19x24 to something smaller, simply
73 * reduce the RPMLength and LoadLength fields to lower values.
74 * Increasing the size of either axis is not currently possible.
76 * Values outside the table :
78 * Given that the axis lists are in order, a data point outside
79 * the table will give the value adjacent to it, and one outside
80 * one of the four corners will give the corner value. This is a
81 * clean and reasonable behaviour in my opinion.
83 * Reminder : X/RPM is horizontal, Y/Load is vertical
85 * @warning This function relies on the axis values being a sorted
86 * list from low to high. If this is not the case behaviour is
87 * undefined and could include memory corruption and engine damage.
91 * @param Table is a pointer to the mainTable struct that we need to read from.
92 * @param realRPM is the current RPM for which a table value is required.
93 * @param realLoad is the current load for which a table value is required.
94 * @param RAMPage is the RAM page that the table is stored in.
96 * @return The interpolated value for the location specified.
98 unsigned short lookupPagedMainTableCellValue(mainTable
* Table
, unsigned short realRPM
, unsigned short realLoad
, unsigned char RAMPage
){
100 /* Save the RPAGE value for restoration and switch pages. */
101 unsigned char oldRPage
= RPAGE
;
104 /* Find the bounding axis values and indices for RPM */
105 unsigned char lowRPMIndex
= 0;
106 unsigned char highRPMIndex
= Table
->RPMLength
- 1;
107 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
108 unsigned short lowRPMValue
= Table
->RPM
[0];
109 unsigned short highRPMValue
= Table
->RPM
[Table
->RPMLength
-1];
111 unsigned char RPMIndex
;
112 for(RPMIndex
=0;RPMIndex
<Table
->RPMLength
;RPMIndex
++){
113 if(Table
->RPM
[RPMIndex
] < realRPM
){
114 lowRPMValue
= Table
->RPM
[RPMIndex
];
115 lowRPMIndex
= RPMIndex
;
116 }else if(Table
->RPM
[RPMIndex
] > realRPM
){
117 highRPMValue
= Table
->RPM
[RPMIndex
];
118 highRPMIndex
= RPMIndex
;
120 }else if(Table
->RPM
[RPMIndex
] == realRPM
){
121 lowRPMValue
= Table
->RPM
[RPMIndex
];
122 highRPMValue
= Table
->RPM
[RPMIndex
];
123 lowRPMIndex
= RPMIndex
;
124 highRPMIndex
= RPMIndex
;
129 /* Find the bounding cell values and indices for Load */
130 unsigned char lowLoadIndex
= 0;
131 unsigned char highLoadIndex
= Table
->LoadLength
-1;
132 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
133 unsigned short lowLoadValue
= Table
->Load
[0];
134 unsigned short highLoadValue
= Table
->Load
[Table
->LoadLength
-1];
136 unsigned char LoadIndex
;
137 for(LoadIndex
=0;LoadIndex
<Table
->LoadLength
;LoadIndex
++){
138 if(Table
->Load
[LoadIndex
] < realLoad
){
139 lowLoadValue
= Table
->Load
[LoadIndex
];
140 lowLoadIndex
= LoadIndex
;
141 }else if(Table
->Load
[LoadIndex
] > realLoad
){
142 highLoadValue
= Table
->Load
[LoadIndex
];
143 highLoadIndex
= LoadIndex
;
145 }else if(Table
->Load
[LoadIndex
] == realLoad
){
146 lowLoadValue
= Table
->Load
[LoadIndex
];
147 highLoadValue
= Table
->Load
[LoadIndex
];
148 lowLoadIndex
= LoadIndex
;
149 highLoadIndex
= LoadIndex
;
154 /* Obtain the four corners surrounding the spot of interest */
155 unsigned short lowRPMLowLoad
= Table
->Table
[(Table
->LoadLength
* lowRPMIndex
) + lowLoadIndex
];
156 unsigned short lowRPMHighLoad
= Table
->Table
[(Table
->LoadLength
* lowRPMIndex
) + highLoadIndex
];
157 unsigned short highRPMLowLoad
= Table
->Table
[(Table
->LoadLength
* highRPMIndex
) + lowLoadIndex
];
158 unsigned short highRPMHighLoad
= Table
->Table
[(Table
->LoadLength
* highRPMIndex
) + highLoadIndex
];
160 /* Restore the ram page before doing the math */
163 /* Find the two side values to interpolate between by interpolation */
164 unsigned short lowRPMIntLoad
= lowRPMLowLoad
+ (((signed long)((signed long)lowRPMHighLoad
- lowRPMLowLoad
) * (realLoad
- lowLoadValue
))/ (highLoadValue
- lowLoadValue
));
165 unsigned short highRPMIntLoad
= highRPMLowLoad
+ (((signed long)((signed long)highRPMHighLoad
- highRPMLowLoad
) * (realLoad
- lowLoadValue
))/ (highLoadValue
- lowLoadValue
));
167 /* Interpolate between the two side values and return the result */
168 return lowRPMIntLoad
+ (((signed long)((signed long)highRPMIntLoad
- lowRPMIntLoad
) * (realRPM
- lowRPMValue
))/ (highRPMValue
- lowRPMValue
));
172 unsigned short lookupTwoDTableUS(twoDTableUS
* Table
, unsigned short Value
){
174 /* Find the bounding axis indices, axis values and lookup values */
175 unsigned char lowIndex
= 0;
176 unsigned char highIndex
= 15;
177 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
178 unsigned short lowAxisValue
= Table
->Axis
[0];
179 unsigned short highAxisValue
= Table
->Axis
[15];
180 unsigned short lowLookupValue
= Table
->Values
[0];
181 unsigned short highLookupValue
= Table
->Values
[15];
184 for(Index
=0;Index
<16;Index
++){
185 if(Table
->Axis
[Index
] < Value
){
187 lowAxisValue
= Table
->Axis
[Index
];
188 lowLookupValue
= Table
->Values
[Index
];
189 }else if(Table
->Axis
[Index
] > Value
){
191 highAxisValue
= Table
->Axis
[Index
];
192 highLookupValue
= Table
->Values
[Index
];
194 }else if(Table
->Axis
[Index
] == Value
){
195 return Table
->Values
[Index
]; // If right on, just return the value
200 /* Interpolate and return the value */
201 return lowLookupValue
+ (((signed long)((signed long)highLookupValue
- lowLookupValue
) * (Value
- lowAxisValue
))/ (highAxisValue
- lowAxisValue
));
205 /* Returns 0 on success and error code otherwise */
206 unsigned short setAxisValue(unsigned short index
, unsigned short value
, unsigned short axis
[], unsigned short length
, unsigned short errorBase
){
208 return errorBase
+ invalidAxisIndex
;
211 /* Ensure value isn't lower than the one below */
212 if(axis
[index
- 1] > value
){
213 return errorBase
+ invalidAxisOrder
;
216 if(index
< (length
-1)){
217 /* Ensure value isn't higher than the one above */
218 if(value
> axis
[index
+ 1]){
219 return errorBase
+ invalidAxisOrder
;
224 /* If we got this far all is well, set the value */
230 unsigned short setPagedMainTableCellValue(unsigned char RPageValue
, mainTable
* Table
, unsigned short RPMIndex
, unsigned short LoadIndex
, unsigned short cellValue
){
231 unsigned char oldRPage
= RPAGE
;
232 unsigned short errorID
= 0;
234 if(RPMIndex
< Table
->RPMLength
){
235 if(LoadIndex
< Table
->LoadLength
){
236 Table
->Table
[(Table
->LoadLength
* RPMIndex
) + LoadIndex
] = cellValue
;
238 errorID
= invalidMainTableLoadIndex
;
241 errorID
= invalidMainTableRPMIndex
;
248 unsigned short setPagedMainTableRPMValue(unsigned char RPageValue
, mainTable
* Table
, unsigned short RPMIndex
, unsigned short RPMValue
){
249 unsigned char oldRPage
= RPAGE
;
251 unsigned short errorID
= setAxisValue(RPMIndex
, RPMValue
, Table
->RPM
, Table
->RPMLength
, errorBaseMainTableRPM
);
257 unsigned short setPagedMainTableLoadValue(unsigned char RPageValue
, mainTable
* Table
, unsigned short LoadIndex
, unsigned short LoadValue
){
258 unsigned char oldRPage
= RPAGE
;
260 unsigned short errorID
= setAxisValue(LoadIndex
, LoadValue
, Table
->Load
, Table
->LoadLength
, errorBaseMainTableLoad
);
266 unsigned short setPagedTwoDTableCellValue(unsigned char RPageValue
, twoDTableUS
* Table
, unsigned short cellIndex
, unsigned short cellValue
){
268 return invalidTwoDTableIndex
;
270 unsigned char oldRPage
= RPAGE
;
272 Table
->Values
[cellIndex
] = cellValue
;
279 unsigned short setPagedTwoDTableAxisValue(unsigned char RPageValue
, twoDTableUS
* Table
, unsigned short axisIndex
, unsigned short axisValue
){
280 unsigned char oldRPage
= RPAGE
;
282 unsigned short errorID
= setAxisValue(axisIndex
, axisValue
, Table
->Axis
, 16, errorBaseTwoDTableAxis
);
288 /* Check that the configuration of the table is valid. Assumes pages are correctly set. */
289 unsigned short validateMainTable(mainTable
* Table
){
290 /* If required and only if required extend this to take r and f pages and check */
291 /* any main table, not just a freshly received untrusted ones in linear space */
293 if(Table
->RPMLength
> MAINTABLE_MAX_RPM_LENGTH
){
294 return invalidMainTableRPMLength
;
295 }else if(Table
->LoadLength
> MAINTABLE_MAX_LOAD_LENGTH
){
296 return invalidMainTableLoadLength
;
297 }else if((Table
->RPMLength
* Table
->LoadLength
) > MAINTABLE_MAX_MAIN_LENGTH
){
298 return invalidMainTableMainLength
;
300 /* Check the order of the RPM axis */
302 for(i
=0;i
<(Table
->RPMLength
- 1);i
++){
303 if(Table
->RPM
[i
] > Table
->RPM
[i
+1]){
304 return invalidMainTableRPMOrder
;
307 /* Check the order of the Load axis */
309 for(j
=0;j
<(Table
->LoadLength
- 1);j
++){
310 if(Table
->Load
[j
] > Table
->Load
[j
+1]){
311 return invalidMainTableLoadOrder
;
314 /* If we made it this far all is well */
320 /* Check that the configuration of the table is valid */
321 unsigned short validateTwoDTable(twoDTableUS
* Table
){
322 /* Check the order of the axis */
324 for(i
=0;i
<(TWODTABLEUS_LENGTH
- 1);i
++){
325 if(Table
->Axis
[i
] > Table
->Axis
[i
+1]){
326 return invalidTwoDTableAxisOrder
;