Removing empty ISR header, can put back later if required.
[freeems-vanilla.git] / src / tableLookup.c
blob7a617caf44ad19a61f4975df5bc5e22bf94ff7a5
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 /** Looks up a value from a main table using interpolation.
63 * The process :
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.
70 * Table size :
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.
89 * @author Fred Cooke
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;
102 RPAGE = RAMPage;
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;
119 break;
120 }else if(Table->RPM[RPMIndex] == realRPM){
121 lowRPMValue = Table->RPM[RPMIndex];
122 highRPMValue = Table->RPM[RPMIndex];
123 lowRPMIndex = RPMIndex;
124 highRPMIndex = RPMIndex;
125 break;
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;
144 break;
145 }else if(Table->Load[LoadIndex] == realLoad){
146 lowLoadValue = Table->Load[LoadIndex];
147 highLoadValue = Table->Load[LoadIndex];
148 lowLoadIndex = LoadIndex;
149 highLoadIndex = LoadIndex;
150 break;
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 */
161 RPAGE = oldRPage;
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];
183 unsigned char Index;
184 for(Index=0;Index<16;Index++){
185 if(Table->Axis[Index] < Value){
186 lowIndex = Index;
187 lowAxisValue = Table->Axis[Index];
188 lowLookupValue = Table->Values[Index];
189 }else if(Table->Axis[Index] > Value){
190 highIndex = Index;
191 highAxisValue = Table->Axis[Index];
192 highLookupValue = Table->Values[Index];
193 break;
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){
207 if(index >= length){
208 return errorBase + invalidAxisIndex;
209 }else{
210 if(index > 0){
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 */
225 axis[index] = value;
226 return 0;
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;
233 RPAGE = RPageValue;
234 if(RPMIndex < Table->RPMLength){
235 if(LoadIndex < Table->LoadLength){
236 Table->Table[(Table->LoadLength * RPMIndex) + LoadIndex] = cellValue;
237 }else{
238 errorID = invalidMainTableLoadIndex;
240 }else{
241 errorID = invalidMainTableRPMIndex;
243 RPAGE = oldRPage;
244 return errorID;
248 unsigned short setPagedMainTableRPMValue(unsigned char RPageValue, mainTable* Table, unsigned short RPMIndex, unsigned short RPMValue){
249 unsigned char oldRPage = RPAGE;
250 RPAGE = RPageValue;
251 unsigned short errorID = setAxisValue(RPMIndex, RPMValue, Table->RPM, Table->RPMLength, errorBaseMainTableRPM);
252 RPAGE = oldRPage;
253 return errorID;
257 unsigned short setPagedMainTableLoadValue(unsigned char RPageValue, mainTable* Table, unsigned short LoadIndex, unsigned short LoadValue){
258 unsigned char oldRPage = RPAGE;
259 RPAGE = RPageValue;
260 unsigned short errorID = setAxisValue(LoadIndex, LoadValue, Table->Load, Table->LoadLength, errorBaseMainTableLoad);
261 RPAGE = oldRPage;
262 return errorID;
266 unsigned short setPagedTwoDTableCellValue(unsigned char RPageValue, twoDTableUS* Table, unsigned short cellIndex, unsigned short cellValue){
267 if(cellIndex > 15){
268 return invalidTwoDTableIndex;
269 }else{
270 unsigned char oldRPage = RPAGE;
271 RPAGE = RPageValue;
272 Table->Values[cellIndex] = cellValue;
273 RPAGE = oldRPage;
274 return 0;
279 unsigned short setPagedTwoDTableAxisValue(unsigned char RPageValue, twoDTableUS* Table, unsigned short axisIndex, unsigned short axisValue){
280 unsigned char oldRPage = RPAGE;
281 RPAGE = RPageValue;
282 unsigned short errorID = setAxisValue(axisIndex, axisValue, Table->Axis, 16, errorBaseTwoDTableAxis);
283 RPAGE = oldRPage;
284 return errorID;
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;
299 }else{
300 /* Check the order of the RPM axis */
301 unsigned char i;
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 */
308 unsigned char j;
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 */
315 return 0;
320 /* Check that the configuration of the table is valid */
321 unsigned short validateTwoDTable(twoDTableUS* Table){
322 /* Check the order of the axis */
323 unsigned char i;
324 for(i=0;i<(TWODTABLEUS_LENGTH - 1);i++){
325 if(Table->Axis[i] > Table->Axis[i+1]){
326 return invalidTwoDTableAxisOrder;
329 return 0;