#include <cstdint>
#include <assert.h>
#include "libIgnTiming/timing.h"
#if !defined WRITABLE_TABLE
#define CONST_ATTR constexpr
#endif
#if defined __cplusplus
extern "C"
{
#endif
namespace
{
int8_t timingAdjust = 0 * TIMING_SCALE; // in TIMING_SCALE
unsigned constexpr INTERP_SCALE = 256;
int constexpr TimingScale = TIMING_SCALE;
int16_t constexpr NO_DATA = -1;
int16_t constexpr MAX_ADVANCE = 50 * TIMING_SCALE;
int16_t constexpr MIN_ADVANCE = 7 * TIMING_SCALE;
// array of column headings
int16_t CONST_ATTR rpmMap[MAX_RPM_POINTS] = {400, 750, 1000, 1500, 2500, 3500, 4500, 6000};
// column of row values - in 1000-pressure
int16_t CONST_ATTR vacuumMap[MAX_VACUUM_POINTS] = {0, 166, 225, 300, 700, 1000, NO_DATA, NO_DATA};
uint8_t CONST_ATTR mapping[MAX_VACUUM_POINTS][MAX_RPM_POINTS] = {
/* Table in degrees. */
/* row for 0mb = centrifugal only */
{12, 7, 7, 19, 25, 29, 29, 22},
/* row for 166 mB*/
{12, 7, 7, 21, 27, 31, 31, 24},
/* row for 225 mB */
{12, 7, 7, 25, 31, 35, 35, 28},
/* row for 300 mB*/
{12, 7, 7, 29, 35, 39, 39, 33},
/* row for 700 mB*/
{12, 7, 7, 19, 25, 29, 29, 22},
/* row for 1000 mB - used when pressure drops off the scale */
{7, 7, 7, 7, 7, 7, 7, 7},
/* unused */
{0, 0, 0, 0, 0, 0, 0, 0},
/* unused */
{0, 0, 0, 0, 0, 0, 0, 0},
/* unused */
};
}
uint8_t getTimingAdjust() { return timingAdjust; };
void setTimingAdjust(int8_t adjust) { timingAdjust = adjust; }
int16_t getRpmMap(unsigned int i)
{
if (i >= 0 && i < MAX_RPM_POINTS)
return rpmMap[i];
else
return 0;
}
void setRpmMap(unsigned int i, uint16_t val)
{
#if WRITABLE_TABLE
if (i >= 0 && i < MAX_RPM_POINTS)
rpmMap[i] = val;
#endif
}
uint16_t getVacuumMap(unsigned int i)
{
if (i >= 0 && i < MAX_VACUUM_POINTS)
return vacuumMap[i];
else
return 0;
}
void setVacuumMap(unsigned int i, uint16_t val)
{
#if WRITABLE_TABLE
if (i >= 0 && i < MAX_VACUUM_POINTS)
vacuumMap[i] = val;
#endif
}
void setTiming(unsigned int vacuumIndex, unsigned int rpmIndex, uint8_t value)
{
#if WRITABLE_TABLE
if (vacuumIndex < 0 && vacuumIndex >= MAX_VACUUM_POINTS)
return;
if (rpmIndex < 0 && rpmIndex >= MAX_RPM_POINTS)
return;
mapping[vacuumIndex][rpmIndex] = value;
#endif
}
uint8_t getTiming(unsigned int vacuumIndex, unsigned int rpmIndex)
{
if (vacuumIndex < 0 && vacuumIndex >= MAX_VACUUM_POINTS)
return 0;
if (rpmIndex < 0 && rpmIndex >= MAX_RPM_POINTS)
return 0;
return mapping[vacuumIndex][rpmIndex];
}
/// @brief Lookup a point in a 1 dimensional array -
/// @param point Value to lookup
/// @param curve Lookup table
/// @param size Size of lookup table
/// @param [out] frac fraction of distance from first point in array
/// @return index in array or NO_DATA if operations fail
int lookup(int point, int16_t const curve[], int size, int16_t *frac)
{
// check lower bounds
if (point < curve[0])
{
*frac = 0;
return 0;
}
// check upper bounds
// find the upper boundary by looking for non -1 points
int upper = size - 1;
while (upper != 0 && curve[upper] == NO_DATA)
upper--;
if (point >= curve[upper])
{
*frac = 0;
return upper;
}
for (int pt = 1; pt <= upper; pt++)
{
if ((point >= curve[pt - 1]) && (point < curve[pt]))
{
int range1 = curve[pt] - curve[pt - 1];
if (range1 == 0)
{
*frac = 0;
return pt - 1;
}
// how far along axis ?
int offset = point - curve[pt - 1];
int range2 = INTERP_SCALE;
*frac = ((offset * range2) / range1);
return pt - 1;
}
}
*frac = 0;
return NO_DATA; // give up.
};
extern "C"
{
int mapTiming(int rpm, int vacuumMb)
{
int angle = 0;
/* lookup the interpolated RPM point */
int16_t rpm_frac = 0;
int rpm_index = lookup(rpm, rpmMap, MAX_RPM_POINTS, &rpm_frac);
if (rpm_index == NO_DATA)
return timingAdjust + MIN_ADVANCE;
/* lookup the interpolated vacuum point */
int16_t vacuum_frac = 0;
int vacuum_index = lookup(vacuumMb, vacuumMap, MAX_VACUUM_POINTS, &vacuum_frac);
/* if there is a problem, bail out */
if (vacuum_index == NO_DATA)
return timingAdjust + MIN_ADVANCE;
/* perform a bilinear mapping */
int top_advance;
// we now have a position between two points in X and Y
if (rpm_frac == 0)
top_advance = mapping[vacuum_index][rpm_index] * INTERP_SCALE;
// if fractional part then interpolate points off the map
else
top_advance = mapping[vacuum_index][rpm_index] * (INTERP_SCALE - rpm_frac) + mapping[vacuum_index][rpm_index + 1] * rpm_frac;
int bottom_advance;
// if no fractional part, then the top and bottom advance point is the same
if (vacuum_frac == 0)
{
angle = top_advance * TimingScale / INTERP_SCALE;
}
else
{
bottom_advance = mapping[vacuum_index + 1][rpm_index] * (INTERP_SCALE - rpm_frac) + mapping[vacuum_index + 1][rpm_index + 1] * rpm_frac;
/* interpolate down Y axis this time */
int advance = top_advance * (INTERP_SCALE - vacuum_frac) + bottom_advance * vacuum_frac;
/* point is scaled by two multiplications */
angle = advance * TimingScale / (INTERP_SCALE * INTERP_SCALE);
}
if (angle < MIN_ADVANCE)
angle = MIN_ADVANCE;
if (angle > MAX_ADVANCE)
angle = MAX_ADVANCE;
return angle + timingAdjust;
}
}
#if defined __cplusplus
}
#endif