1 /* FreeEMS - the open source engine management system
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!
27 /** @file tableLookup.c
29 * @brief Table access functions
31 * Functions for writing to and reading from all of the different table types.
38 #include "inc/freeEMS.h"
39 #include "inc/commsISRs.h"
40 #include "inc/tableLookup.h"
43 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
44 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
45 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* ******* ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
46 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* WARNING ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
47 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ******* ******* ******* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
48 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
49 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
50 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
51 /*&&&&&&&&&&&&&&&&&&&& These routines rely on the fact that there are no ISRs trying &&&&&&&&&&&&&&&&&&&*/
52 /*&&&&&&&&&&&&&&&&&&&& to access the small tables and other live settings in the ram &&&&&&&&&&&&&&&&&&&*/
53 /*&&&&&&&&&&&&&&&&&&&& window as specified by the RPAGE value. If they are then bad &&&&&&&&&&&&&&&&&&&*/
54 /*&&&&&&&&&&&&&&&&&&&& values WILL be occasionally read from random parts of the big &&&&&&&&&&&&&&&&&&&*/
55 /*&&&&&&&&&&&&&&&&&&&& tables instead of the correct place. If that occurs it WILL &&&&&&&&&&&&&&&&&&&*/
56 /*&&&&&&&&&&&&&&&&&&&& cause unpredictable and VERY hard to find bugs!! &&&&&&&&&&&&&&&&&&&*/
57 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
58 /*&&&&&&&&&&&&&&&&&&&& ******* ******* YOU HAVE BEEN WARNED!!! ******* ******* &&&&&&&&&&&&&&&&&&&*/
59 /*&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&*/
60 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
61 /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
64 /* Yet to be implemented :
66 unsigned char lookup8Bit3dUC(
67 unsigned short lookup16Bit2dUS(
68 unsigned char lookup8Bit2dUC(
69 signed short lookup16Bit3dSS(
70 signed short lookup16Bit3dSS(
71 signed char lookup8Bit3D( */
74 /** @brief Main table read function
76 * Looks up a value from a main table using interpolation.
80 * Take a table with two movable axis sets and two axis lengths,
81 * loop to find which pairs of axis values and indexs we are between,
82 * interpolate two pairs down to two values,
83 * interpolate two values down to one value.
87 * To reduce the table size from 19x24 to something smaller, simply
88 * reduce the RPMLength and LoadLength fields to lower values.
89 * Increasing the size of either axis is not currently possible.
91 * Values outside the table :
93 * Given that the axis lists are in order, a data point outside
94 * the table will give the value adjacent to it, and one outside
95 * one of the four corners will give the corner value. This is a
96 * clean and reasonable behaviour in my opinion.
98 * Reminder : X/RPM is horizontal, Y/Load is vertical
100 * @warning This function relies on the axis values being a sorted
101 * list from low to high. If this is not the case behaviour is
102 * undefined and could include memory corruption and engine damage.
106 * @param Table is a pointer to the table to read from.
107 * @param realRPM is the current RPM for which a table value is required.
108 * @param realLoad is the current load for which a table value is required.
109 * @param RAMPage is the RAM page that the table is stored in.
111 * @return The interpolated value for the location specified.
113 unsigned short lookupPagedMainTableCellValue(mainTable
* Table
, unsigned short realRPM
, unsigned short realLoad
, unsigned char RAMPage
){
115 /* Save the RPAGE value for restoration and switch pages. */
116 unsigned char oldRPage
= RPAGE
;
119 /* Find the bounding axis values and indices for RPM */
120 unsigned char lowRPMIndex
= 0;
121 unsigned char highRPMIndex
= Table
->RPMLength
- 1;
122 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
123 unsigned short lowRPMValue
= Table
->RPM
[0];
124 unsigned short highRPMValue
= Table
->RPM
[Table
->RPMLength
-1];
126 unsigned char RPMIndex
;
127 for(RPMIndex
=0;RPMIndex
<Table
->RPMLength
;RPMIndex
++){
128 if(Table
->RPM
[RPMIndex
] < realRPM
){
129 lowRPMValue
= Table
->RPM
[RPMIndex
];
130 lowRPMIndex
= RPMIndex
;
131 }else if(Table
->RPM
[RPMIndex
] > realRPM
){
132 highRPMValue
= Table
->RPM
[RPMIndex
];
133 highRPMIndex
= RPMIndex
;
135 }else if(Table
->RPM
[RPMIndex
] == realRPM
){
136 lowRPMValue
= Table
->RPM
[RPMIndex
];
137 highRPMValue
= Table
->RPM
[RPMIndex
];
138 lowRPMIndex
= RPMIndex
;
139 highRPMIndex
= RPMIndex
;
144 /* Find the bounding cell values and indices for Load */
145 unsigned char lowLoadIndex
= 0;
146 unsigned char highLoadIndex
= Table
->LoadLength
-1;
147 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
148 unsigned short lowLoadValue
= Table
->Load
[0];
149 unsigned short highLoadValue
= Table
->Load
[Table
->LoadLength
-1];
151 unsigned char LoadIndex
;
152 for(LoadIndex
=0;LoadIndex
<Table
->LoadLength
;LoadIndex
++){
153 if(Table
->Load
[LoadIndex
] < realLoad
){
154 lowLoadValue
= Table
->Load
[LoadIndex
];
155 lowLoadIndex
= LoadIndex
;
156 }else if(Table
->Load
[LoadIndex
] > realLoad
){
157 highLoadValue
= Table
->Load
[LoadIndex
];
158 highLoadIndex
= LoadIndex
;
160 }else if(Table
->Load
[LoadIndex
] == realLoad
){
161 lowLoadValue
= Table
->Load
[LoadIndex
];
162 highLoadValue
= Table
->Load
[LoadIndex
];
163 lowLoadIndex
= LoadIndex
;
164 highLoadIndex
= LoadIndex
;
169 /* Obtain the four corners surrounding the spot of interest */
170 unsigned short lowRPMLowLoad
= Table
->Table
[(Table
->LoadLength
* lowRPMIndex
) + lowLoadIndex
];
171 unsigned short lowRPMHighLoad
= Table
->Table
[(Table
->LoadLength
* lowRPMIndex
) + highLoadIndex
];
172 unsigned short highRPMLowLoad
= Table
->Table
[(Table
->LoadLength
* highRPMIndex
) + lowLoadIndex
];
173 unsigned short highRPMHighLoad
= Table
->Table
[(Table
->LoadLength
* highRPMIndex
) + highLoadIndex
];
175 /* Restore the ram page before doing the math */
178 /* Find the two side values to interpolate between by interpolation */
179 unsigned short lowRPMIntLoad
= lowRPMLowLoad
+ (((signed long)((signed long)lowRPMHighLoad
- lowRPMLowLoad
) * (realLoad
- lowLoadValue
))/ (highLoadValue
- lowLoadValue
));
180 unsigned short highRPMIntLoad
= highRPMLowLoad
+ (((signed long)((signed long)highRPMHighLoad
- highRPMLowLoad
) * (realLoad
- lowLoadValue
))/ (highLoadValue
- lowLoadValue
));
182 /* Interpolate between the two side values and return the result */
183 return lowRPMIntLoad
+ (((signed long)((signed long)highRPMIntLoad
- lowRPMIntLoad
) * (realRPM
- lowRPMValue
))/ (highRPMValue
- lowRPMValue
));
187 /** @brief Two D table read function
189 * Looks up a value from a two D table using interpolation.
193 * @param Table is a pointer to the table to read from.
194 * @param Value is the position value used to lookup the return value.
196 * @return the interpolated value for the position specified
198 unsigned short lookupTwoDTableUS(twoDTableUS
* Table
, unsigned short Value
){
200 /* Find the bounding axis indices, axis values and lookup values */
201 unsigned char lowIndex
= 0;
202 unsigned char highIndex
= 15;
203 /* If never set in the loop, low value will equal high value and will be on the edge of the map */
204 unsigned short lowAxisValue
= Table
->Axis
[0];
205 unsigned short highAxisValue
= Table
->Axis
[15];
206 unsigned short lowLookupValue
= Table
->Values
[0];
207 unsigned short highLookupValue
= Table
->Values
[15];
210 for(Index
=0;Index
<16;Index
++){
211 if(Table
->Axis
[Index
] < Value
){
213 lowAxisValue
= Table
->Axis
[Index
];
214 lowLookupValue
= Table
->Values
[Index
];
215 }else if(Table
->Axis
[Index
] > Value
){
217 highAxisValue
= Table
->Axis
[Index
];
218 highLookupValue
= Table
->Values
[Index
];
220 }else if(Table
->Axis
[Index
] == Value
){
221 return Table
->Values
[Index
]; // If right on, just return the value
226 /* Interpolate and return the value */
227 return lowLookupValue
+ (((signed long)((signed long)highLookupValue
- lowLookupValue
) * (Value
- lowAxisValue
))/ (highAxisValue
- lowAxisValue
));
231 /** @brief Set an axis value
233 * Sets the value of an axis cell in a table. This is used when configuring
234 * a table via a communication interface.
238 * @param index The position of the axis cell to adjust.
239 * @param value The value to set the axis cell to.
240 * @param axis A pointer to the axis array to adjust.
241 * @param length The length of the axis array.
242 * @param errorBase The base value to add error code offsets to.
244 * @return An error code. Zero means success, anything else is a failure.
246 unsigned short setAxisValue(unsigned short index
, unsigned short value
, unsigned short axis
[], unsigned short length
, unsigned short errorBase
){
248 return errorBase
+ invalidAxisIndex
;
251 /* Ensure value isn't lower than the one below */
252 if(axis
[index
- 1] > value
){
253 return errorBase
+ invalidAxisOrder
;
256 if(index
< (length
-1)){
257 /* Ensure value isn't higher than the one above */
258 if(value
> axis
[index
+ 1]){
259 return errorBase
+ invalidAxisOrder
;
264 /* If we got this far all is well, set the value */
270 /** @brief Set a main table cell value
272 * Sets the value of a cell in a main table. This is used when configuring a
273 * table via a communication interface.
277 * @param RPageValue The page of RAM that the table is in.
278 * @param Table A pointer to the table to adjust.
279 * @param RPMIndex The RPM position of the cell to adjust.
280 * @param LoadIndex The load position of the cell to adjust.
281 * @param cellValue The value to set the cell to.
283 * @return An error code. Zero means success, anything else is a failure.
285 unsigned short setPagedMainTableCellValue(unsigned char RPageValue
, mainTable
* Table
, unsigned short RPMIndex
, unsigned short LoadIndex
, unsigned short cellValue
){
286 unsigned char oldRPage
= RPAGE
;
287 unsigned short errorID
= 0;
289 if(RPMIndex
< Table
->RPMLength
){
290 if(LoadIndex
< Table
->LoadLength
){
291 Table
->Table
[(Table
->LoadLength
* RPMIndex
) + LoadIndex
] = cellValue
;
293 errorID
= invalidMainTableLoadIndex
;
296 errorID
= invalidMainTableRPMIndex
;
303 /** @brief Set an RPM axis value
305 * Sets the value of an RPM axis cell in a table. This is used when configuring
306 * the table via a comms interface.
310 * @param RPageValue The page of RAM that the table is in.
311 * @param Table is a pointer to the table to adjust.
312 * @param RPMIndex The RPM position of the cell to adjust.
313 * @param RPMValue The value to set the RPM axis cell to.
315 * @return An error code. Zero means success, anything else is a failure.
317 unsigned short setPagedMainTableRPMValue(unsigned char RPageValue
, mainTable
* Table
, unsigned short RPMIndex
, unsigned short RPMValue
){
318 unsigned char oldRPage
= RPAGE
;
320 unsigned short errorID
= setAxisValue(RPMIndex
, RPMValue
, Table
->RPM
, Table
->RPMLength
, errorBaseMainTableRPM
);
326 /** @brief Set a load axis value
328 * Sets the value of a load axis cell in a table. This is used when configuring
329 * the table via a comms interface.
333 * @param RPageValue The page of RAM that the table is in.
334 * @param Table is a pointer to the table to adjust.
335 * @param LoadIndex The load position of the cell to adjust.
336 * @param LoadValue The value to set the load axis cell to.
338 * @return An error code. Zero means success, anything else is a failure.
340 unsigned short setPagedMainTableLoadValue(unsigned char RPageValue
, mainTable
* Table
, unsigned short LoadIndex
, unsigned short LoadValue
){
341 unsigned char oldRPage
= RPAGE
;
343 unsigned short errorID
= setAxisValue(LoadIndex
, LoadValue
, Table
->Load
, Table
->LoadLength
, errorBaseMainTableLoad
);
349 /** @brief Set a two D table cell value
351 * Sets the value of a cell in a two D table. This is used when configuring the
352 * table via a comms interface.
356 * @param RPageValue The page of RAM that the table is in.
357 * @param Table is a pointer to the table to adjust.
358 * @param cellIndex The position of the cell to adjust.
359 * @param cellValue The value to set the cell to.
361 * @return An error code. Zero means success, anything else is a failure.
363 unsigned short setPagedTwoDTableCellValue(unsigned char RPageValue
, twoDTableUS
* Table
, unsigned short cellIndex
, unsigned short cellValue
){
365 return invalidTwoDTableIndex
;
367 unsigned char oldRPage
= RPAGE
;
369 Table
->Values
[cellIndex
] = cellValue
;
376 /** @brief Set a two D axis value
378 * Sets the value of an axis cell in a table. This is used when configuring
379 * the table via a comms interface.
383 * @param RPageValue The page of RAM that the table is in.
384 * @param Table is a pointer to the table to adjust.
385 * @param axisIndex The position of the axis cell to adjust.
386 * @param axisValue The value to set the axis cell to.
388 * @return An error code. Zero means success, anything else is a failure.
390 unsigned short setPagedTwoDTableAxisValue(unsigned char RPageValue
, twoDTableUS
* Table
, unsigned short axisIndex
, unsigned short axisValue
){
391 unsigned char oldRPage
= RPAGE
;
393 unsigned short errorID
= setAxisValue(axisIndex
, axisValue
, Table
->Axis
, 16, errorBaseTwoDTableAxis
);
399 /** @brief Validate a main table
401 * Check that the configuration of the table is valid. Assumes pages are
402 * correctly set. @todo more detail here....
406 * @param Table is a pointer to the table to be validated.
408 * @return An error code. Zero means success, anything else is a failure.
410 unsigned short validateMainTable(mainTable
* Table
){
411 /* If required and only if required extend this to take r and f pages and check */
412 /* any main table, not just a freshly received untrusted ones in linear space */
414 if(Table
->RPMLength
> MAINTABLE_MAX_RPM_LENGTH
){
415 return invalidMainTableRPMLength
;
416 }else if(Table
->LoadLength
> MAINTABLE_MAX_LOAD_LENGTH
){
417 return invalidMainTableLoadLength
;
418 }else if((Table
->RPMLength
* Table
->LoadLength
) > MAINTABLE_MAX_MAIN_LENGTH
){
419 return invalidMainTableMainLength
;
421 /* Check the order of the RPM axis */
423 for(i
=0;i
<(Table
->RPMLength
- 1);i
++){
424 if(Table
->RPM
[i
] > Table
->RPM
[i
+1]){
425 return invalidMainTableRPMOrder
;
428 /* Check the order of the Load axis */
430 for(j
=0;j
<(Table
->LoadLength
- 1);j
++){
431 if(Table
->Load
[j
] > Table
->Load
[j
+1]){
432 return invalidMainTableLoadOrder
;
435 /* If we made it this far all is well */
441 /** @brief Validate a two D table
443 * Check that the order of the axis values is correct and therefore that the
444 * table is valid too.
448 * @param Table is a pointer to the table to be validated.
450 * @return An error code. Zero means success, anything else is a failure.
452 unsigned short validateTwoDTable(twoDTableUS
* Table
){
453 /* Check the order of the axis */
455 for(i
=0;i
<(TWODTABLEUS_LENGTH
- 1);i
++){
456 if(Table
->Axis
[i
] > Table
->Axis
[i
+1]){
457 return invalidTwoDTableAxisOrder
;