FreeEMS  0.2.0-SNAPSHOT-282-g9efc524
tableLookup.c
Go to the documentation of this file.
1 /* FreeEMS - the open source engine management system
2  *
3  * Copyright 2008-2013 Fred Cooke
4  *
5  * This file is part of the FreeEMS project.
6  *
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.
11  *
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.
16  *
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/
19  *
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!
22  *
23  * Thank you for choosing FreeEMS to run your engine!
24  */
25 
26 
27 /** @file
28  *
29  * @brief Table access functions
30  *
31  * Functions for writing to and reading from all of the different table types.
32  */
33 
34 
35 #define TABLELOOKUP_C
36 #include "inc/freeEMS.h"
37 #include "inc/commsISRs.h"
38 #include "inc/tableLookup.h"
39 #include "inc/blockDetailsLookup.h"
40 
41 
42 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
43 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
44 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* ******* ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
45 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* WARNING ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
46 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* ******* ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
47 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
48 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
49 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
50 /*&&&&&&&&&&&&&&&&&&&& These routines rely on the fact that there are no ISRs trying &&&&&&&&&&&&&&&&&&&*/
51 /*&&&&&&&&&&&&&&&&&&&& to access the small tables and other live settings in the RAM &&&&&&&&&&&&&&&&&&&*/
52 /*&&&&&&&&&&&&&&&&&&&& window as specified by the RPAGE value. If they are then bad &&&&&&&&&&&&&&&&&&&*/
53 /*&&&&&&&&&&&&&&&&&&&& values WILL be occasionally read from random parts of the big &&&&&&&&&&&&&&&&&&&*/
54 /*&&&&&&&&&&&&&&&&&&&& tables instead of the correct place. If that occurs it WILL &&&&&&&&&&&&&&&&&&&*/
55 /*&&&&&&&&&&&&&&&&&&&& cause unpredictable and VERY hard to find bugs!! &&&&&&&&&&&&&&&&&&&*/
56 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
57 /*&&&&&&&&&&&&&&&&&&&& ******* ******* YOU HAVE BEEN WARNED!!! ******* ******* &&&&&&&&&&&&&&&&&&&*/
58 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
59 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
60 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
61 
62 
63 /* Yet to be implemented :
64 
65 unsigned char lookup8Bit3dUC(
66 unsigned char lookup8Bit2dUC(
67 signed short lookup16Bit3dSS(
68 signed short lookup16Bit3dSS(
69 signed char lookup8Bit3D( */
70 
71 
72 /** @brief Main table read function
73  *
74  * Looks up a value from a main table using interpolation.
75  *
76  * The process :
77  *
78  * Take a table with two movable axis sets and two axis lengths,
79  * loop to find which pairs of axis values and indexs we are between,
80  * interpolate two pairs down to two values,
81  * interpolate two values down to one value.
82  *
83  * Table size :
84  *
85  * To reduce the table size from 19x24 to something smaller, simply
86  * reduce the RPMLength and LoadLength fields to lower values.
87  * Increasing the size of either axis is not currently possible.
88  *
89  * Values outside the table :
90  *
91  * Given that the axis lists are in order, a data point outside
92  * the table will give the value adjacent to it, and one outside
93  * one of the four corners will give the corner value. This is a
94  * clean and reasonable behaviour in my opinion.
95  *
96  * Reminder : X/RPM is horizontal, Y/Load is vertical
97  *
98  * @warning This function relies on the axis values being a sorted
99  * list from low to high. If this is not the case behaviour is
100  * undefined and could include memory corruption and engine damage.
101  *
102  * @param realRPM is the current RPM for which a table value is required.
103  * @param realLoad is the current load for which a table value is required.
104  * @param locationID the ID to use to get the memory information to look up the table.
105  *
106  * May go back to these:
107  * old param RAMPage is the RAM page that the table is stored in.
108  * old param Table is a pointer to the table to read from.
109  *
110  * @return The interpolated value for the location specified.
111  */
112 unsigned short lookupMainTable(unsigned short realRPM, unsigned short realLoad, unsigned short locationID){
113  blockDetails mainTableDetails;
114  lookupBlockDetails(locationID, &mainTableDetails);
115  if(!(mainTableDetails.flags & block_is_main_table)){
116  return 0; // Safe value, always means no fuel or TDC timing.
117  }
118 
119  // Leave the rest of the code untouched and keep it more concise
120  mainTable* Table = (mainTable*)mainTableDetails.RAMAddress;
121 
122  /* Save the RPAGE value for restoration and switch pages. */
123  unsigned char oldRPage = RPAGE;
124  RPAGE = mainTableDetails.RAMPage;
125 
126  /* Find the bounding axis values and indices for RPM */
127  unsigned char lowRPMIndex = 0;
128  unsigned char highRPMIndex = Table->RPMLength - 1;
129  /* If never set in the loop, low value will equal high value and will be on the edge of the map */
130  unsigned short lowRPMValue = Table->RPM[0];
131  unsigned short highRPMValue = Table->RPM[Table->RPMLength -1];
132 
133  unsigned char RPMIndex;
134  for(RPMIndex=0;RPMIndex<Table->RPMLength;RPMIndex++){
135  if(Table->RPM[RPMIndex] < realRPM){
136  lowRPMValue = Table->RPM[RPMIndex];
137  lowRPMIndex = RPMIndex;
138  }else if(Table->RPM[RPMIndex] > realRPM){
139  highRPMValue = Table->RPM[RPMIndex];
140  highRPMIndex = RPMIndex;
141  break;
142  }else if(Table->RPM[RPMIndex] == realRPM){
143  lowRPMValue = Table->RPM[RPMIndex];
144  highRPMValue = Table->RPM[RPMIndex];
145  lowRPMIndex = RPMIndex;
146  highRPMIndex = RPMIndex;
147  break;
148  }
149  }
150 
151  /* Find the bounding cell values and indices for Load */
152  unsigned char lowLoadIndex = 0;
153  unsigned char highLoadIndex = Table->LoadLength -1;
154  /* If never set in the loop, low value will equal high value and will be on the edge of the map */
155  unsigned short lowLoadValue = Table->Load[0];
156  unsigned short highLoadValue = Table->Load[Table->LoadLength -1];
157 
158  unsigned char LoadIndex;
159  for(LoadIndex=0;LoadIndex<Table->LoadLength;LoadIndex++){
160  if(Table->Load[LoadIndex] < realLoad){
161  lowLoadValue = Table->Load[LoadIndex];
162  lowLoadIndex = LoadIndex;
163  }else if(Table->Load[LoadIndex] > realLoad){
164  highLoadValue = Table->Load[LoadIndex];
165  highLoadIndex = LoadIndex;
166  break;
167  }else if(Table->Load[LoadIndex] == realLoad){
168  lowLoadValue = Table->Load[LoadIndex];
169  highLoadValue = Table->Load[LoadIndex];
170  lowLoadIndex = LoadIndex;
171  highLoadIndex = LoadIndex;
172  break;
173  }
174  }
175 
176  /* Obtain the four corners surrounding the spot of interest */
177  unsigned short lowRPMLowLoad = Table->Table[(Table->RPMLength * lowLoadIndex) + lowRPMIndex];
178  unsigned short lowRPMHighLoad = Table->Table[(Table->RPMLength * highLoadIndex) + lowRPMIndex];
179  unsigned short highRPMLowLoad = Table->Table[(Table->RPMLength * lowLoadIndex) + highRPMIndex];
180  unsigned short highRPMHighLoad = Table->Table[(Table->RPMLength * highLoadIndex) + highRPMIndex];
181 
182  /* Restore the RAM page before doing the math */
183  RPAGE = oldRPage;
184 
185  /* Find the two side values to interpolate between by interpolation */
186  unsigned short lowRPMIntLoad = lowRPMLowLoad + (((signed long)((signed long)lowRPMHighLoad - lowRPMLowLoad) * (realLoad - lowLoadValue))/ (highLoadValue - lowLoadValue));
187  unsigned short highRPMIntLoad = highRPMLowLoad + (((signed long)((signed long)highRPMHighLoad - highRPMLowLoad) * (realLoad - lowLoadValue))/ (highLoadValue - lowLoadValue));
188 
189  /* Interpolate between the two side values and return the result */
190  return lowRPMIntLoad + (((signed long)((signed long)highRPMIntLoad - lowRPMIntLoad) * (realRPM - lowRPMValue))/ (highRPMValue - lowRPMValue));
191 }
192 
193 
194 /** @brief Two D table read function
195  *
196  * Looks up a value from a two D table using interpolation.
197  *
198  * @param Table is a pointer to the table to read from.
199  * @param Value is the position value used to lookup the return value.
200  *
201  * @return the interpolated value for the position specified
202  */
203 unsigned short lookupTwoDTableUS(twoDTableUS * Table, unsigned short Value){
204 
205  /* If never set in the loop, low value will equal high value and will be on the edge of the map */
206  unsigned short lowAxisValue = Table->Axis[0];
207  unsigned short highAxisValue = Table->Axis[15];
208  unsigned short lowLookupValue = Table->Values[0];
209  unsigned short highLookupValue = Table->Values[15];
210 
211  /* Find the bounding axis values and lookup values */
212  unsigned char Index;
213  for(Index=0;Index<16;Index++){
214  if(Table->Axis[Index] < Value){
215  lowAxisValue = Table->Axis[Index];
216  lowLookupValue = Table->Values[Index];
217  }else if(Table->Axis[Index] > Value){
218  highAxisValue = Table->Axis[Index];
219  highLookupValue = Table->Values[Index];
220  break;
221  }else if(Table->Axis[Index] == Value){
222  return Table->Values[Index]; // If right on, just return the value
223  }
224  }
225 
226  /* Interpolate and return the value */
227  return lowLookupValue + (((signed long)((signed long)highLookupValue - lowLookupValue) * (Value - lowAxisValue))/ (highAxisValue - lowAxisValue));
228 }
229 
230 
231 /** @brief Validate a main table
232  *
233  * Check that the configuration of the table is valid. Assumes pages are
234  * correctly set. @todo more detail here....
235  *
236  * @param Table is a pointer to the table to be validated.
237  *
238  * @return An error code. Zero means success, anything else is a failure.
239  */
240 unsigned short validateMainTable(mainTable* Table){
241  /* If required and only if required extend this to take r and f pages and check */
242  /* any main table, not just a freshly received untrusted ones in linear space */
243 
244  if(Table->RPMLength > MAINTABLE_MAX_RPM_LENGTH){
246  }else if(Table->LoadLength > MAINTABLE_MAX_LOAD_LENGTH){
248  }else if((Table->RPMLength * Table->LoadLength) > MAINTABLE_MAX_MAIN_LENGTH){
250  }else{
251  /* Check the order of the RPM axis */
252  unsigned char i;
253  for(i=0;i<(Table->RPMLength - 1);i++){
254  if(Table->RPM[i] >= Table->RPM[i+1]){
256  }
257  }
258  /* Check the order of the Load axis */
259  unsigned char j;
260  for(j=0;j<(Table->LoadLength - 1);j++){
261  if(Table->Load[j] >= Table->Load[j+1]){
263  }
264  }
265  /* If we made it this far all is well */
266  return 0;
267  }
268 }
269 
270 
271 /** @brief Validate a two D table
272  *
273  * Check that the order of the axis values is correct and therefore that the
274  * table is valid too.
275  *
276  * @param Table is a pointer to the table to be validated.
277  *
278  * @return An error code. Zero means success, anything else is a failure.
279  */
280 unsigned short validateTwoDTable(twoDTableUS* Table){
281  /* Check the order of the axis */
282  unsigned char i;
283  for(i=0;i<(TWODTABLEUS_LENGTH - 1);i++){
284  if(Table->Axis[i] >= Table->Axis[i+1]){
286  }
287  }
288  return 0;
289 }