xGAP and xPower

In the vein of this thread and this blog post, I had a crack at creating a script for a custom activity stream for xPower and xGAP. Please note I do not know how to code, and tried to teach myself with copilot, VSC and the resources on this forum.

xPower

{
let watts = icu.streams.fixed_watts
let w25 = icu.stats.calcCenteredMovingAvg(watts, 25)
for (let i = 0; i < data.length; i++)
data[i] = Math.pow(Math.pow(w25[i], 4), 0.25)
}

xGAP

{
let gaStr = icu.streams.ga_velocity
let g25 = icu.stats.calcCenteredMovingAvg(gaStr, 25)
for (let i = 0; i < data.length; i++)
data[i] = Math.pow(Math.pow(g25[i], 4), 0.25)
}

Also had a crack at average activity values:
xPower

{
let watts = icu.streams.fixed_watts
let w25 = icu.stats.calcCenteredMovingAvg(watts, 25)
let temp = w25.map(v => Math.pow(v, 4))
let tot = 0
let c = 0
for (let i = 0; i < temp.length; i++){
let v = temp[i]
if (v) {
tot += v
++c
}
Math.pow(tot / c, 0.25)
}
}

xGAP

{
let gaStr = icu.streams.ga_velocity
let g25 = icu.stats.calcCenteredMovingAvg(gaStr, 25)
let temp = g25.map(v => Math.pow(v, 4))
let tot = 0
let c = 0
for (let i = 0; i < temp.length; i++){
let v = temp[i]
if (v) {
tot += v
++c
}
Math.pow(tot / c, 0.25)
}
}

Please have a look over it and let me know if I’m missing a step anywhere
So far, I can’t get xGAP to sit on the same scale/axis as my regular pace chart without messing up one of the graphs (pace axis and xGAP axis need to be on different sides for the shapes to look comparable).
I’ve made the streams public for the time being, if anybody wanted to experiment.

I think the formula is not correct. You have to build the average before taking the 4th root.
Normalized Power or xPower = (Average(Psmoothed^4))^1/4

Thank you for the feedback, I now understand the sticking point of this other thread you’ve been commenting on and understand that the step of averaging needs to happen before taking the area under the curve and that this is the main criticism of Coggan’s NP → TSS calculation. Essentially, even though you can see NP as a stream/graph, this information is useless as the calculated average NP for the whole activity isn’t the average line across this stream, so there was no point in making these custom streams.

Instead, I’ve made a few different load calculations in addition to Piotrek’s suggested formula in the previous thread

xLoad or xTSS (average xpower expressed as a load)

  • simply instead of normalising over 30 sec, it is over 25

{
watts = streams. Get(“fixed_watts”).data
w25 = icu.stats.calcMovingAvg(watts, 25)
tss = 0
for (let i = 0; i < w25.length; i++) {
w = (w25[i]/icu.sportSettings.ftp)**2;
tss += w
}
tss = tss/36
//activity.icu_training_load = tss
}

I’ve also tried with Skiba’s GOVSS, below, but it didn’t give very many meaningful deviations in data compared with the TSS calculation above (Note this assumes an accurate FTP and does not actually do Skiba’s first step according to his PDF)

{
watts = streams. Get(“fixed_watts”).data
w120 = icu.stats.calcMovingAvg(watts, 120)
tss = 0
for (let i = 0; i < w120.length; i++) {
w = (w120[i]/icu.sportSettings.ftp)**2;
tss += w
}
data = tss/36
}