Initial commit of 0.0.18 just as it was released!
[freeems-vanilla.git] / src / tableLookup.c
blob5bc88e95e7053afc40e9b9bdb48ecb3762359d1d
1 /* tableLookup.c
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! */
24 #define TABLELOOKUP_C
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;
79 RPAGE = RPageValue;
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;
96 break;
97 }else if(Table->RPM[RPMIndex] == realRPM){
98 lowRPMValue = Table->RPM[RPMIndex];
99 highRPMValue = Table->RPM[RPMIndex];
100 lowRPMIndex = RPMIndex;
101 highRPMIndex = RPMIndex;
102 break;
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;
122 break;
123 }else if(Table->Load[LoadIndex] == realLoad){
124 lowLoadValue = Table->Load[LoadIndex];
125 highLoadValue = Table->Load[LoadIndex];
126 lowLoadIndex = LoadIndex;
127 highLoadIndex = LoadIndex;
128 break;
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 */
141 RPAGE = oldRPage;
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];
165 unsigned char Index;
166 for(Index=0;Index<16;Index++){
167 if(Table->Axis[Index] < Value){
168 lowIndex = Index;
169 lowAxisValue = Table->Axis[Index];
170 lowLookupValue = Table->Values[Index];
171 }else if(Table->Axis[Index] > Value){
172 highIndex = Index;
173 highAxisValue = Table->Axis[Index];
174 highLookupValue = Table->Values[Index];
175 break;
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){
189 if(index >= length){
190 return errorBase + invalidAxisIndex;
191 }else{
192 if(index > 0){
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 */
207 axis[index] = value;
208 return 0;
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;
215 RPAGE = RPageValue;
216 if(RPMIndex < Table->RPMLength){
217 if(LoadIndex < Table->LoadLength){
218 Table->Table[(Table->LoadLength * RPMIndex) + LoadIndex] = cellValue;
219 }else{
220 errorID = invalidMainTableLoadIndex;
222 }else{
223 errorID = invalidMainTableRPMIndex;
225 RPAGE = oldRPage;
226 return errorID;
230 unsigned short setPagedMainTableRPMValue(unsigned char RPageValue, mainTable* Table, unsigned short RPMIndex, unsigned short RPMValue){
231 unsigned char oldRPage = RPAGE;
232 RPAGE = RPageValue;
233 unsigned short errorID = setAxisValue(RPMIndex, RPMValue, Table->RPM, Table->RPMLength, errorBaseMainTableRPM);
234 RPAGE = oldRPage;
235 return errorID;
239 unsigned short setPagedMainTableLoadValue(unsigned char RPageValue, mainTable* Table, unsigned short LoadIndex, unsigned short LoadValue){
240 unsigned char oldRPage = RPAGE;
241 RPAGE = RPageValue;
242 unsigned short errorID = setAxisValue(LoadIndex, LoadValue, Table->Load, Table->LoadLength, errorBaseMainTableLoad);
243 RPAGE = oldRPage;
244 return errorID;
248 unsigned short setPagedTwoDTableCellValue(unsigned char RPageValue, twoDTableUS* Table, unsigned short cellIndex, unsigned short cellValue){
249 if(cellIndex > 15){
250 return invalidTwoDTableIndex;
251 }else{
252 unsigned char oldRPage = RPAGE;
253 RPAGE = RPageValue;
254 Table->Values[cellIndex] = cellValue;
255 RPAGE = oldRPage;
256 return 0;
261 unsigned short setPagedTwoDTableAxisValue(unsigned char RPageValue, twoDTableUS* Table, unsigned short axisIndex, unsigned short axisValue){
262 unsigned char oldRPage = RPAGE;
263 RPAGE = RPageValue;
264 unsigned short errorID = setAxisValue(axisIndex, axisValue, Table->Axis, 16, errorBaseTwoDTableAxis);
265 RPAGE = oldRPage;
266 return errorID;
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;
281 }else{
282 /* Check the order of the RPM axis */
283 unsigned char i;
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 */
290 unsigned char j;
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 */
297 return 0;
302 /* Check that the configuration of the table is valid */
303 unsigned short validateTwoDTable(twoDTableUS* Table){
304 /* Check the order of the axis */
305 unsigned char i;
306 for(i=0;i<(TWODTABLEUS_LENGTH - 1);i++){
307 if(Table->Axis[i] > Table->Axis[i+1]){
308 return invalidTwoDTableAxisOrder;
311 return 0;