Refactored fixed confs 1 into modular chunks and added to llookup function to make...
[freeems-vanilla.git] / src / tableLookup.c
blobe8aa8726c5bcedc17cb95409af1720d0c97911e6
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 email them upstream to
21 us at admin(at)diyefi(dot)org or, even better, fork the code on github.com!
23 Thank you for choosing FreeEMS to run your engine! */
26 #define TABLELOOKUP_C
27 #include "inc/freeEMS.h"
28 #include "inc/commsISRs.h"
29 #include "inc/tableLookup.h"
32 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
33 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
34 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* ******* ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
35 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* WARNING ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
36 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* ******* ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
37 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
38 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
39 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
40 /*&&&&&&&&&&&&&&&&&&&& These routines rely on the fact that there are no ISRs trying &&&&&&&&&&&&&&&&&&&*/
41 /*&&&&&&&&&&&&&&&&&&&& to access the small tables and other live settings in the ram &&&&&&&&&&&&&&&&&&&*/
42 /*&&&&&&&&&&&&&&&&&&&& window as specified by the RPAGE value. If they are then bad &&&&&&&&&&&&&&&&&&&*/
43 /*&&&&&&&&&&&&&&&&&&&& values WILL be occasionally read from random parts of the big &&&&&&&&&&&&&&&&&&&*/
44 /*&&&&&&&&&&&&&&&&&&&& tables instead of the correct place. If that occurs it WILL &&&&&&&&&&&&&&&&&&&*/
45 /*&&&&&&&&&&&&&&&&&&&& cause unpredictable and VERY hard to find bugs!! &&&&&&&&&&&&&&&&&&&*/
46 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
47 /*&&&&&&&&&&&&&&&&&&&& ******* ******* YOU HAVE BEEN WARNED!!! ******* ******* &&&&&&&&&&&&&&&&&&&*/
48 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
49 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
50 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
53 /* Yet to be implemented :
55 unsigned char lookup8Bit3dUC(
56 unsigned short lookup16Bit2dUS(
57 unsigned char lookup8Bit2dUC(
58 signed short lookup16Bit3dSS(
59 signed short lookup16Bit3dSS(
60 signed char lookup8Bit3D( */
63 /** Looks up a value from a main table using interpolation.
65 * The process :
67 * Take a table with two movable axis sets and two axis lengths,
68 * loop to find which pairs of axis values and indexs we are between,
69 * interpolate two pairs down to two values,
70 * interpolate two values down to one value.
72 * Table size :
74 * To reduce the table size from 19x24 to something smaller, simply
75 * reduce the RPMLength and LoadLength fields to lower values.
76 * Increasing the size of either axis is not currently possible.
78 * Values outside the table :
80 * Given that the axis lists are in order, a data point outside
81 * the table will give the value adjacent to it, and one outside
82 * one of the four corners will give the corner value. This is a
83 * clean and reasonable behaviour in my opinion.
85 * Reminder : X/RPM is horizontal, Y/Load is vertical
87 * @warning This function relies on the axis values being a sorted
88 * list from low to high. If this is not the case behaviour is
89 * undefined and could include memory corruption and engine damage.
91 * @author Fred Cooke
93 * @param Table is a pointer to the mainTable struct that we need to read from.
94 * @param realRPM is the current RPM for which a table value is required.
95 * @param realLoad is the current load for which a table value is required.
96 * @param RAMPage is the RAM page that the table is stored in.
98 * @return The interpolated value for the location specified.
100 unsigned short lookupPagedMainTableCellValue(mainTable* Table, unsigned short realRPM, unsigned short realLoad, unsigned char RAMPage){
102 /* Save the RPAGE value for restoration and switch pages. */
103 unsigned char oldRPage = RPAGE;
104 RPAGE = RAMPage;
106 /* Find the bounding axis values and indices for RPM */
107 unsigned char lowRPMIndex = 0;
108 unsigned char highRPMIndex = Table->RPMLength - 1;
109 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
110 unsigned short lowRPMValue = Table->RPM[0];
111 unsigned short highRPMValue = Table->RPM[Table->RPMLength -1];
113 unsigned char RPMIndex;
114 for(RPMIndex=0;RPMIndex<Table->RPMLength;RPMIndex++){
115 if(Table->RPM[RPMIndex] < realRPM){
116 lowRPMValue = Table->RPM[RPMIndex];
117 lowRPMIndex = RPMIndex;
118 }else if(Table->RPM[RPMIndex] > realRPM){
119 highRPMValue = Table->RPM[RPMIndex];
120 highRPMIndex = RPMIndex;
121 break;
122 }else if(Table->RPM[RPMIndex] == realRPM){
123 lowRPMValue = Table->RPM[RPMIndex];
124 highRPMValue = Table->RPM[RPMIndex];
125 lowRPMIndex = RPMIndex;
126 highRPMIndex = RPMIndex;
127 break;
131 /* Find the bounding cell values and indices for Load */
132 unsigned char lowLoadIndex = 0;
133 unsigned char highLoadIndex = Table->LoadLength -1;
134 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
135 unsigned short lowLoadValue = Table->Load[0];
136 unsigned short highLoadValue = Table->Load[Table->LoadLength -1];
138 unsigned char LoadIndex;
139 for(LoadIndex=0;LoadIndex<Table->LoadLength;LoadIndex++){
140 if(Table->Load[LoadIndex] < realLoad){
141 lowLoadValue = Table->Load[LoadIndex];
142 lowLoadIndex = LoadIndex;
143 }else if(Table->Load[LoadIndex] > realLoad){
144 highLoadValue = Table->Load[LoadIndex];
145 highLoadIndex = LoadIndex;
146 break;
147 }else if(Table->Load[LoadIndex] == realLoad){
148 lowLoadValue = Table->Load[LoadIndex];
149 highLoadValue = Table->Load[LoadIndex];
150 lowLoadIndex = LoadIndex;
151 highLoadIndex = LoadIndex;
152 break;
156 /* Obtain the four corners surrounding the spot of interest */
157 unsigned short lowRPMLowLoad = Table->Table[(Table->LoadLength * lowRPMIndex) + lowLoadIndex];
158 unsigned short lowRPMHighLoad = Table->Table[(Table->LoadLength * lowRPMIndex) + highLoadIndex];
159 unsigned short highRPMLowLoad = Table->Table[(Table->LoadLength * highRPMIndex) + lowLoadIndex];
160 unsigned short highRPMHighLoad = Table->Table[(Table->LoadLength * highRPMIndex) + highLoadIndex];
162 /* Restore the ram page before doing the math */
163 RPAGE = oldRPage;
165 /* Find the two side values to interpolate between by interpolation */
166 unsigned short lowRPMIntLoad = lowRPMLowLoad + (((signed long)((signed long)lowRPMHighLoad - lowRPMLowLoad) * (realLoad - lowLoadValue))/ (highLoadValue - lowLoadValue));
167 unsigned short highRPMIntLoad = highRPMLowLoad + (((signed long)((signed long)highRPMHighLoad - highRPMLowLoad) * (realLoad - lowLoadValue))/ (highLoadValue - lowLoadValue));
169 /* Interpolate between the two side values and return the result */
170 return lowRPMIntLoad + (((signed long)((signed long)highRPMIntLoad - lowRPMIntLoad) * (realRPM - lowRPMValue))/ (highRPMValue - lowRPMValue));
174 unsigned short lookupTwoDTableUS(twoDTableUS * Table, unsigned short Value){
176 /* Find the bounding axis indices, axis values and lookup values */
177 unsigned char lowIndex = 0;
178 unsigned char highIndex = 15;
179 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
180 unsigned short lowAxisValue = Table->Axis[0];
181 unsigned short highAxisValue = Table->Axis[15];
182 unsigned short lowLookupValue = Table->Values[0];
183 unsigned short highLookupValue = Table->Values[15];
185 unsigned char Index;
186 for(Index=0;Index<16;Index++){
187 if(Table->Axis[Index] < Value){
188 lowIndex = Index;
189 lowAxisValue = Table->Axis[Index];
190 lowLookupValue = Table->Values[Index];
191 }else if(Table->Axis[Index] > Value){
192 highIndex = Index;
193 highAxisValue = Table->Axis[Index];
194 highLookupValue = Table->Values[Index];
195 break;
196 }else if(Table->Axis[Index] == Value){
197 return Table->Values[Index]; // If right on, just return the value
202 /* Interpolate and return the value */
203 return lowLookupValue + (((signed long)((signed long)highLookupValue - lowLookupValue) * (Value - lowAxisValue))/ (highAxisValue - lowAxisValue));
207 /* Returns 0 on success and error code otherwise */
208 unsigned short setAxisValue(unsigned short index, unsigned short value, unsigned short axis[], unsigned short length, unsigned short errorBase){
209 if(index >= length){
210 return errorBase + invalidAxisIndex;
211 }else{
212 if(index > 0){
213 /* Ensure value isn't lower than the one below */
214 if(axis[index - 1] > value){
215 return errorBase + invalidAxisOrder;
218 if(index < (length -1)){
219 /* Ensure value isn't higher than the one above */
220 if(value > axis[index + 1]){
221 return errorBase + invalidAxisOrder;
226 /* If we got this far all is well, set the value */
227 axis[index] = value;
228 return 0;
232 unsigned short setPagedMainTableCellValue(unsigned char RPageValue, mainTable* Table, unsigned short RPMIndex, unsigned short LoadIndex, unsigned short cellValue){
233 unsigned char oldRPage = RPAGE;
234 unsigned short errorID = 0;
235 RPAGE = RPageValue;
236 if(RPMIndex < Table->RPMLength){
237 if(LoadIndex < Table->LoadLength){
238 Table->Table[(Table->LoadLength * RPMIndex) + LoadIndex] = cellValue;
239 }else{
240 errorID = invalidMainTableLoadIndex;
242 }else{
243 errorID = invalidMainTableRPMIndex;
245 RPAGE = oldRPage;
246 return errorID;
250 unsigned short setPagedMainTableRPMValue(unsigned char RPageValue, mainTable* Table, unsigned short RPMIndex, unsigned short RPMValue){
251 unsigned char oldRPage = RPAGE;
252 RPAGE = RPageValue;
253 unsigned short errorID = setAxisValue(RPMIndex, RPMValue, Table->RPM, Table->RPMLength, errorBaseMainTableRPM);
254 RPAGE = oldRPage;
255 return errorID;
259 unsigned short setPagedMainTableLoadValue(unsigned char RPageValue, mainTable* Table, unsigned short LoadIndex, unsigned short LoadValue){
260 unsigned char oldRPage = RPAGE;
261 RPAGE = RPageValue;
262 unsigned short errorID = setAxisValue(LoadIndex, LoadValue, Table->Load, Table->LoadLength, errorBaseMainTableLoad);
263 RPAGE = oldRPage;
264 return errorID;
268 unsigned short setPagedTwoDTableCellValue(unsigned char RPageValue, twoDTableUS* Table, unsigned short cellIndex, unsigned short cellValue){
269 if(cellIndex > 15){
270 return invalidTwoDTableIndex;
271 }else{
272 unsigned char oldRPage = RPAGE;
273 RPAGE = RPageValue;
274 Table->Values[cellIndex] = cellValue;
275 RPAGE = oldRPage;
276 return 0;
281 unsigned short setPagedTwoDTableAxisValue(unsigned char RPageValue, twoDTableUS* Table, unsigned short axisIndex, unsigned short axisValue){
282 unsigned char oldRPage = RPAGE;
283 RPAGE = RPageValue;
284 unsigned short errorID = setAxisValue(axisIndex, axisValue, Table->Axis, 16, errorBaseTwoDTableAxis);
285 RPAGE = oldRPage;
286 return errorID;
290 /* Check that the configuration of the table is valid. Assumes pages are correctly set. */
291 unsigned short validateMainTable(mainTable* Table){
292 /* If required and only if required extend this to take r and f pages and check */
293 /* any main table, not just a freshly received untrusted ones in linear space */
295 if(Table->RPMLength > MAINTABLE_MAX_RPM_LENGTH){
296 return invalidMainTableRPMLength;
297 }else if(Table->LoadLength > MAINTABLE_MAX_LOAD_LENGTH){
298 return invalidMainTableLoadLength;
299 }else if((Table->RPMLength * Table->LoadLength) > MAINTABLE_MAX_MAIN_LENGTH){
300 return invalidMainTableMainLength;
301 }else{
302 /* Check the order of the RPM axis */
303 unsigned char i;
304 for(i=0;i<(Table->RPMLength - 1);i++){
305 if(Table->RPM[i] > Table->RPM[i+1]){
306 return invalidMainTableRPMOrder;
309 /* Check the order of the Load axis */
310 unsigned char j;
311 for(j=0;j<(Table->LoadLength - 1);j++){
312 if(Table->Load[j] > Table->Load[j+1]){
313 return invalidMainTableLoadOrder;
316 /* If we made it this far all is well */
317 return 0;
322 /* Check that the configuration of the table is valid */
323 unsigned short validateTwoDTable(twoDTableUS* Table){
324 /* Check the order of the axis */
325 unsigned char i;
326 for(i=0;i<(TWODTABLEUS_LENGTH - 1);i++){
327 if(Table->Axis[i] > Table->Axis[i+1]){
328 return invalidTwoDTableAxisOrder;
331 return 0;