Apologies for the delay in posting this update, was away for a few days in Norway. An awesome place to visit – but pricey (not helped by the grim GBP exchange rate).

**Calibration from PowerTap data**

f(x) = a*x*x + b*x + c Multiple Data Fitting Results level_1 48.37 a:0.031647 b:3.0117 c:-6.2383 level_12 51.19 a:0.032029 b:3.3858 c:-5.8462 level_23 42.64 a:0.042916 b:4.0801 c:-14.053 level_34 44.03 a:0.052877 b:5.368 c:-19.749 level_45 34.98 a:0.08673 b:6.3456 c:-14.823 level_56 30.46 a:0.0332 b:11.926 c:-53.106 level_67 33.22 a:0.17617 b:9.3606 c:-30.054 level_78 37.14 a:0.18789 b:11.523 c:-36.988 level_89 22.27 a:0.24039 b:13.578 c:-42.477 level_100 21.4 a:0.1618 b:16.801 c:-59.99

To get more accurate power data, I collected a couple of hours of data, riding at 10 different resistance levels to match the 1-10 standard resistance lever positions. This data was then fed into the xcrvfit curve fitting software to produce the above quadratic equations for power against speed at each resistance level. (These seem to be a better fit than the linear equations derived from the tacx graphs).

To populate the lookup table, I also wanted to plot power against resistance at steady speeds. In order to get this, I filtered the original data by speed in a spreadsheet, and performed another curve fitting against these new data series. I’m not entirely happy with the outcome from this 2nd curve fitting (more details below), so I may perform this step again with some additional ride data.

**Lookup table**

Using the data collected above, I was able to calculate the resistance levels required to populate a 12×12 lookup table of speed vs power points.

The actual values in the lookup table have been trivially encoded (by adding 50 to the original 1-100 values). This allows the use of estimated values outside of this range to maintain the slope at the limits of the resistance adjustment, while remaining within a uint8_t datatype.

uint8_t lookup_table_1d[POWER_ROWS * SPEED_COLS] = {/* 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60,*//* 25,*/51, 45, 40, 20, 25, 20, 20, 35, 40, 40, 49, 51,/* 50,*/130, 91, 65, 20, 25, 20, 20, 35, 40, 40, 49, 51,/* 75,*/170, 111, 83, 65, 25, 20, 20, 35, 40, 40, 49, 51,/* 100,*/170, 130, 95, 78, 63, 20, 20, 35, 40, 40, 49, 51,/* 150,*/170, 180, 114, 94, 83, 75, 54, 35, 40, 40, 49, 51,/* 200,*/170, 180, 135, 107, 95, 85, 76, 70, 61, 40, 49, 51,/* 250,*/170, 180, 165, 120, 105, 94, 86, 80, 74, 65, 49, 51,/* 300,*/170, 180, 165, 133, 114, 101, 93, 87, 81, 73, 64, 57,/* 350,*/170, 180, 165, 152, 123, 108, 99, 93, 87, 80, 76, 64,/* 400,*/170, 180, 165, 152, 132, 115, 104, 98, 91, 85, 80, 76,/* 450,*/170, 180, 165, 152, 142, 123, 108, 103, 95, 89, 85, 80,/* 500,*/170, 180, 165, 152, 155, 131, 112, 108, 99, 93, 89, 85 };

Knowing the current speed and power, we can then index into this table to find the closest resistance value.

**Interpolation**

However using just the values at the 12×12 speed/power intersections would be very coarse, so we’ll interpolate between the surrounding four table points to get a more accurate value.

There will doubtless be much better explanations of bilinear interpolation out there on the internet, so I won’t try here, but basically it’s interpolating twice along one axis, and then once along the other axis using the intermediate values from the first step.

// x1 x x2 // | | | // y1---z11----------z21--- // | | | // y ---|-----z------|---- // | | | // y2---z12----------z22--- // | | |

**Test ride**

Here’s a screenshot from a new ergo session using the lookup table. It’s doing a reasonable job of tracking the target load, but still not as close as I’d like it to be.

As mentioned above, I’m not very confident in the output from the 2nd curve fitting, so repeating this step with additional data may help. Alternatively, I suspect it may be just as quick to set a target power using the manual ergo mode of GC, and then tweak the resistance levels as necessary for each speed point in the table. That said, it’s still a lot of faffing around trying to get it spot on, so may be worth trying to trim the resistance based on real-time power data first.

**Next post**

I’ve had my fill of calibration for a while, so next I’ll be porting the microcontroller code to a USB part.