Place braces on expressionless loops in utils.
[freeems-vanilla.git] / src / tableLookup.c
blob9f392f878a06ef9f45a2416047b3a8bd10e33c70
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.
33 * @author Fred Cooke
37 #define TABLELOOKUP_C
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.
78 * The process :
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.
85 * Table size :
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.
104 * @author Fred Cooke
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;
117 RPAGE = RAMPage;
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;
134 break;
135 }else if(Table->RPM[RPMIndex] == realRPM){
136 lowRPMValue = Table->RPM[RPMIndex];
137 highRPMValue = Table->RPM[RPMIndex];
138 lowRPMIndex = RPMIndex;
139 highRPMIndex = RPMIndex;
140 break;
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;
159 break;
160 }else if(Table->Load[LoadIndex] == realLoad){
161 lowLoadValue = Table->Load[LoadIndex];
162 highLoadValue = Table->Load[LoadIndex];
163 lowLoadIndex = LoadIndex;
164 highLoadIndex = LoadIndex;
165 break;
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 */
176 RPAGE = oldRPage;
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.
191 * @author Fred Cooke
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];
209 unsigned char Index;
210 for(Index=0;Index<16;Index++){
211 if(Table->Axis[Index] < Value){
212 lowIndex = Index;
213 lowAxisValue = Table->Axis[Index];
214 lowLookupValue = Table->Values[Index];
215 }else if(Table->Axis[Index] > Value){
216 highIndex = Index;
217 highAxisValue = Table->Axis[Index];
218 highLookupValue = Table->Values[Index];
219 break;
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.
236 * @author Fred Cooke
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){
247 if(index >= length){
248 return errorBase + invalidAxisIndex;
249 }else{
250 if(index > 0){
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 */
265 axis[index] = value;
266 return 0;
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.
275 * @author Fred Cooke
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;
288 RPAGE = RPageValue;
289 if(RPMIndex < Table->RPMLength){
290 if(LoadIndex < Table->LoadLength){
291 Table->Table[(Table->LoadLength * RPMIndex) + LoadIndex] = cellValue;
292 }else{
293 errorID = invalidMainTableLoadIndex;
295 }else{
296 errorID = invalidMainTableRPMIndex;
298 RPAGE = oldRPage;
299 return errorID;
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.
308 * @author Fred Cooke
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;
319 RPAGE = RPageValue;
320 unsigned short errorID = setAxisValue(RPMIndex, RPMValue, Table->RPM, Table->RPMLength, errorBaseMainTableRPM);
321 RPAGE = oldRPage;
322 return errorID;
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.
331 * @author Fred Cooke
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;
342 RPAGE = RPageValue;
343 unsigned short errorID = setAxisValue(LoadIndex, LoadValue, Table->Load, Table->LoadLength, errorBaseMainTableLoad);
344 RPAGE = oldRPage;
345 return errorID;
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.
354 * @author Fred Cooke
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){
364 if(cellIndex > 15){
365 return invalidTwoDTableIndex;
366 }else{
367 unsigned char oldRPage = RPAGE;
368 RPAGE = RPageValue;
369 Table->Values[cellIndex] = cellValue;
370 RPAGE = oldRPage;
371 return 0;
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.
381 * @author Fred Cooke
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;
392 RPAGE = RPageValue;
393 unsigned short errorID = setAxisValue(axisIndex, axisValue, Table->Axis, 16, errorBaseTwoDTableAxis);
394 RPAGE = oldRPage;
395 return errorID;
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....
404 * @author Fred Cooke
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;
420 }else{
421 /* Check the order of the RPM axis */
422 unsigned char i;
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 */
429 unsigned char j;
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 */
436 return 0;
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.
446 * @author Fred Cooke
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 */
454 unsigned char i;
455 for(i=0;i<(TWODTABLEUS_LENGTH - 1);i++){
456 if(Table->Axis[i] > Table->Axis[i+1]){
457 return invalidTwoDTableAxisOrder;
460 return 0;