Mga Formula sa Power sa Cycling

Ang Matematikong Pundasyon ng mga Metric sa Bike Analytics

Gabay sa Implementation

Ang pahinang ito ay nagbibigay ng mga formula at sunud-sunod na paraan ng pagkalkula para sa lahat ng metric sa Bike Analytics. Gamitin ang mga ito para sa mga custom implementation, verification, o mas malalim na pag-unawa sa power-based training.

⚠️ Mga Tala sa Implementation

  • Lahat ng power values ay sa watts (W), ang oras (time) ay sa segundo maliban kung tinukoy.
  • Ang FTP at CP ay individual-specific thresholds—walang universal na halaga.
  • Palaging i-validate ang mga input para sa mga makatwirang range (karaniwan ay 0-2000W).
  • Asikasuhin ang mga edge cases (paghahati sa zero, negatibong power).
  • Ang data ng power ay nangangailangan ng 1-segundong recording intervals para sa katumpakan.

Mga Pangunahing Metric sa Performance

1. Training Stress Score (TSS)

Formula:

TSS = (duration_seconds × NP × IF) / (FTP × 3600) × 100
kung saan ang IF = NP / FTP

Halimbawa ng Pagkalkula:

Sitwasyon: 2-oras na padyak, NP = 235W, FTP = 250W

  1. Kalkulahin ang IF: IF = 235 / 250 = 0.94
  2. Duration sa segundo: 2 oras × 3600 = 7200 segundo
  3. TSS = (7200 × 235 × 0.94) / (250 × 3600) × 100
  4. TSS = 1,590,720 / 900,000 × 100 = 176.7 TSS

Interpretasyon: Mabigat na training ride (>150 TSS), asahan ang 2-3 araw na recovery.

Implementation sa JavaScript:

function calculateTSS(durationSeconds, normalizedPower, ftp) {
  const intensityFactor = normalizedPower / ftp;
  const tss = (durationSeconds * normalizedPower * intensityFactor) / (ftp * 3600) * 100;
  return Math.round(tss);
}

// Halimbawa ng paggamit:
const tss = calculateTSS(7200, 235, 250);
// Returns: 177

2. Normalized Power (NP)

Algorithm (30-segundong rolling average):

1. Kalkulahin ang 30-segundong rolling average power para sa buong ride.
2. Itaas ang bawat 30-segundong halaga sa ika-4 na power (^4).
3. Kunin ang average ng lahat ng ^4 na halagang ito.
4. Kunin ang 4th root ng average na iyon.
NP = ⁴√(average ng [30s_avg^4])

Bakit ika-4 na Power?

Ang quartic (4th power) na ugnayan ay sumasalamin sa non-linear na physiological cost ng mga pabagu-bagong effort. Ang isang ride na may mga surge at recovery ay mas nakakapagod kaysa sa steady power sa parehong average.

Halimbawa:

  • Steady ride: 200W sa loob ng 1 oras → NP = 200W, Average = 200W
  • Variable ride: Nagpapalit-palit na 300W/100W → Average = 200W, NP = 225W

Parehong average power, pero ang variable ride ay may 12% na mas mataas na NP dahil sa pagod mula sa mga surge.

Implementation sa JavaScript:

function calculateNormalizedPower(powerData) {
  // Ang powerData ay array ng 1-segundong power values

  // Step 1: Kalkulahin ang 30-segundong rolling averages
  const rollingAvgs = [];
  for (let i = 29; i < powerData.length; i++) {
    const window = powerData.slice(i - 29, i + 1);
    const avg = window.reduce((sum, p) => sum + p, 0) / 30;
    rollingAvgs.push(avg);
  }

  // Step 2: Itaas sa ika-4 na power
  const powered = rollingAvgs.map(p => Math.pow(p, 4));

  // Step 3: Average ng ika-4 na power
  const avgPowered = powered.reduce((sum, p) => sum + p, 0) / powered.length;

  // Step 4: Kunin ang 4th root
  const np = Math.pow(avgPowered, 0.25);

  return Math.round(np);
}

// Halimbawa ng paggamit:
const powerData = [/* 1-segundong power array */];
const np = calculateNormalizedPower(powerData);
// Returns: NP sa watts

3. Intensity Factor (IF)

Formula:

IF = NP / FTP

Mga Range ng Interpretasyon:

Range ng IF Antas ng Effort Halimbawa ng Workout
< 0.75 Recovery / Madali Active recovery ride, Zone 1-2
0.75 - 0.85 Endurance Mahabang steady ride, aerobic base
0.85 - 0.95 Tempo Sweet spot training, tempo intervals
0.95 - 1.05 Threshold FTP intervals, time trial effort
1.05 - 1.15 VO₂max 5-minutong interval, criterium race
> 1.15 Anaerobic Maikling sprint, attack, MTB burst

Halimbawa ng Pagkalkula:

Sitwasyon: NP = 235W, FTP = 250W

IF = 235 / 250 = 0.94

Interpretasyon: High tempo / sub-threshold na effort, kayang panatilihin sa loob ng 2-3 oras.

function calculateIF(normalizedPower, ftp) {
  return (normalizedPower / ftp).toFixed(2);
}

// Halimbawa:
const if_value = calculateIF(235, 250);
// Returns: 0.94

4. Variability Index (VI)

Formula:

VI = NP / Average Power

Interpretasyon base sa Disiplina:

Disiplina Karaniwang VI Kahulugan
Road TT / Steady Effort 1.00 - 1.05 Napaka-consistent na power, maayos na pacing
Road Racing 1.05 - 1.10 May ilang mga surge, sa pangkalahatan ay steady
Criterium 1.10 - 1.20 Madalas na acceleration at attack
Mountain Bike XC 1.15 - 1.30+ Lubos na nagbabago-bago, laging may surge

Halimbawa ng Pagkalkula:

Road Race: NP = 240W, Avg Power = 230W

VI = 240 / 230 = 1.04 (steady pacing)

MTB Race: NP = 285W, Avg Power = 235W

VI = 285 / 235 = 1.21 (pabagu-bago, burst efforts)

function calculateVI(normalizedPower, averagePower) {
  return (normalizedPower / averagePower).toFixed(2);
}

// Halimbawa:
const vi_road = calculateVI(240, 230);  // Returns: 1.04
const vi_mtb = calculateVI(285, 235);   // Returns: 1.21

Critical Power & W' (Anaerobic Capacity)

5. Critical Power (CP) - Linear na Modelo

Formula:

Oras (Time) = W' / (Power - CP)
Naka-ayos: Power × Oras = CP × Oras + W'

Kalkulasyon mula sa Maraming Effort:

Nangangailangan ng 2-4 na maximal effort sa magkakaibang duration (halimbawa, 3, 5, 12, 20 minuto).

Halimbawa ng Data:

Duration Power (W) Kabuuang Trabaho (kJ)
3 min (180s) 400W 72 kJ
5 min (300s) 365W 109.5 kJ
12 min (720s) 310W 223.2 kJ
20 min (1200s) 285W 342 kJ

Gamit ang linear regression (Work = CP × Time + W'):

  • CP = 270W (slope ng regression line)
  • W' = 18.5 kJ (y-intercept)

Implementation sa JavaScript:

function calculateCP_Linear(efforts) {
  // efforts = [{duration: segundo, power: watts}, ...]

  const times = efforts.map(e => e.duration);
  const work = efforts.map(e => e.power * e.duration / 1000); // kJ

  // Linear regression: work = CP * time + W'
  const n = efforts.length;
  const sumT = times.reduce((a, b) => a + b, 0);
  const sumW = work.reduce((a, b) => a + b, 0);
  const sumTW = times.reduce((sum, t, i) => sum + t * work[i], 0);
  const sumTT = times.reduce((sum, t) => sum + t * t, 0);

  const CP = (n * sumTW - sumT * sumW) / (n * sumTT - sumT * sumT);
  const Wprime = (sumW - CP * sumT) / n;

  return {
    CP: Math.round(CP * 10) / 10,      // watts
    Wprime: Math.round(Wprime * 10) / 10  // kJ
  };
}

// Halimbawa ng paggamit:
const efforts = [
  {duration: 180, power: 400},
  {duration: 300, power: 365},
  {duration: 720, power: 310},
  {duration: 1200, power: 285}
];

const result = calculateCP_Linear(efforts);
// Returns: { CP: 270.0, Wprime: 18.5 }

6. W' Balance (W'bal) - Differential Equation na Modelo

Mga Formula:

Expenditure (kapag ang P > CP):
W'exp(t) = ∫(P(t) - CP) dt
Recovery (kapag ang P < CP):
W'rec(t) = W' × (1 - e^(-t/τ))
kung saan ang τ = 546 × e^(-0.01 × ΔCP) + 316
at ΔCP = (CP - P(t))

Halimbawa sa Tunay na Buhay:

Siklista specs: CP = 270W, W' = 18.5 kJ

Sitwasyon 1 - Mabigat na Attack:

  • Ang rider ay nag-surge sa 400W sa loob ng 30 segundo.
  • W' expenditure: (400 - 270) × 30 = 3,900 J = 3.9 kJ
  • W'bal na natira: 18.5 - 3.9 = 14.6 kJ

Sitwasyon 2 - Recovery:

  • Pagkatapos ng attack, bumaba sa 200W (70W sa ibaba ng CP) sa loob ng 2 minuto.
  • ΔCP = 270 - 200 = 70W
  • τ = 546 × e^(-0.01 × 70) + 316 = 588 segundo
  • Recovery sa loob ng 120s: 18.5 × (1 - e^(-120/588)) = 3.5 kJ recovered
  • Bagong W'bal: 14.6 + 3.5 = 18.1 kJ

Implementation sa JavaScript:

function calculateWbalance(powerData, CP, Wprime) {
  // powerData = array ng {time: segundo, power: watts}
  let wbal = Wprime * 1000; // I-convert sa joules
  const wbalHistory = [];

  for (let i = 1; i < powerData.length; i++) {
    const dt = powerData[i].time - powerData[i-1].time;
    const power = powerData[i].power;

    if (power > CP) {
      // Expenditure sa itaas ng CP
      const expenditure = (power - CP) * dt;
      wbal -= expenditure;
    } else {
      // Recovery sa ibaba ng CP
      const deltaCP = CP - power;
      const tau = 546 * Math.exp(-0.01 * deltaCP) + 316;
      const recovery = (Wprime * 1000 - wbal) * (1 - Math.exp(-dt / tau));
      wbal += recovery;
    }

    // Siguraduhing ang W'bal ay hindi sosobra sa max o magiging negatibo
    wbal = Math.max(0, Math.min(wbal, Wprime * 1000));

    wbalHistory.push({
      time: powerData[i].time,
      wbal: wbal / 1000, // kJ
      percent: (wbal / (Wprime * 1000)) * 100
    });
  }

  return wbalHistory;
}

// Halimbawa ng paggamit:
const powerData = [
  {time: 0, power: 200},
  {time: 1, power: 210},
  // ... natitirang ride data
];

const wbalHistory = calculateWbalance(powerData, 270, 18.5);
// Nag-re-return ng array ng W'bal values sa paglipas ng panahon

Performance Management Chart (PMC)

7. Mga Kalkulasyon sa CTL, ATL, TSB

Mga Formula (Exponentially Weighted Moving Averages):

CTL_ngayon = CTL_kahapon + (TSS_ngayon - CTL_kahapon) / 42
ATL_ngayon = ATL_kahapon + (TSS_ngayon - ATL_kahapon) / 7
TSB_ngayon = CTL_kahapon - ATL_kahapon

Mga Definisyon ng Metric:

  • CTL (Chronic Training Load): 42-araw na exponentially weighted average - kumakatawan sa fitness.
  • ATL (Acute Training Load): 7-araw na exponentially weighted average - kumakatawan sa pagkapagod (fatigue).
  • TSB (Training Stress Balance): Form = Fitness - Fatigue

Halimbawa ng Pagkalkula (7-Araw na Training Block):

Araw TSS CTL ATL TSB Status
Mon 100 75.0 80.0 -5.0 Training
Tue 50 74.4 75.7 -1.3 Recovery
Wed 120 75.5 82.0 -6.5 Hard Training
Thu 0 73.7 70.3 +3.4 Rest Day
Fri 80 73.8 71.7 +2.1 Moderate
Sat 150 75.6 82.9 -7.3 Long Ride
Sun 40 74.8 76.8 -2.0 Recovery

Interpretasyon ng TSB:

Range ng TSB Status Aksyon
< -30 High Risk Overtraining warning - bawasan ang load
-30 hanggang -10 Training Hard Bumubuo ng fitness, bantayan ang recovery
-10 hanggang +5 Optimal Normal training zone
+5 hanggang +15 Race Ready Peak form - pwede nang mag-race ngayong weekend
> +25 Detraining Nawawala ang fitness - dagdagan ang load

Implementation sa JavaScript:

function calculatePMC(workouts) {
  // workouts = [{date: "YYYY-MM-DD", tss: number}, ...]
  let ctl = 0, atl = 0;
  const results = [];

  workouts.forEach(workout => {
    // I-update ang CTL (42-day time constant)
    ctl = ctl + (workout.tss - ctl) / 42;

    // I-update ang ATL (7-day time constant)
    atl = atl + (workout.tss - atl) / 7;

    // Kalkulahin ang TSB (kahapong CTL - ngayong ATL para sa traditional na kalkulasyon)
    // Para mas simple dito, gamit ang kasalukuyang mga halaga
    const tsb = ctl - atl;

    results.push({
      date: workout.date,
      tss: workout.tss,
      ctl: Math.round(ctl * 10) / 10,
      atl: Math.round(atl * 10) / 10,
      tsb: Math.round(tsb * 10) / 10,
      status: getTSBStatus(tsb)
    });
  });

  return results;
}

function getTSBStatus(tsb) {
  if (tsb < -30) return "High Risk";
  if (tsb < -10) return "Training Hard";
  if (tsb < 5) return "Optimal";
  if (tsb < 15) return "Race Ready";
  return "Detraining";
}

// Halimbawa ng paggamit:
const workouts = [
  {date: "2025-01-01", tss: 100},
  {date: "2025-01-02", tss: 50},
  {date: "2025-01-03", tss: 120},
  // ... higit pang workouts
];

const pmc = calculatePMC(workouts);
// Returns array na may CTL, ATL, TSB para sa bawat araw

Mga Metric sa Power-to-Weight at Climbing

8. Power-to-Weight Ratio

Formula:

W/kg = Power (watts) / Body Mass (kg)

Mga Benchmark para sa FTP W/kg:

Antas Male W/kg Female W/kg Kategorya
Recreational 2.5 - 3.5 2.0 - 3.0 Fitness rider
Competitive 3.5 - 4.5 3.0 - 4.0 Cat 3-4, age group racer
Advanced 4.5 - 5.5 4.0 - 5.0 Cat 1-2, malakas na amateur
Elite Amateur 5.5 - 6.0 5.0 - 5.5 National level
Professional 6.0 - 7.0+ 5.5 - 6.5+ World Tour, Grand Tour GC

Halimbawa ng Pagkalkula:

Sitwasyon: Siklista na may FTP = 275W, body mass = 70kg

W/kg = 275 / 70 = 3.93 W/kg

Interpretasyon: Competitive level, mahusay sa mga hilly races.

function calculateWattsPerKg(power, bodyMassKg) {
  return (power / bodyMassKg).toFixed(2);
}

// Halimbawa:
const wpkg = calculateWattsPerKg(275, 70);
// Returns: 3.93

9. VAM (Velocità Ascensionale Media)

Formula:

VAM (m/h) = Elevation Gain (m) / Oras (hours)

Mga Benchmark ng VAM:

VAM (m/h) Antas Halimbawa
600 - 900 Recreational Club rider sa lokal na ahon
900 - 1200 Competitive Mahusay na amateur sa Alpe d'Huez
1200 - 1500 Elite Amateur National-level climber
1500 - 1800 Professional World Tour domestique
> 1800 Grand Tour Winner Pogačar, Vingegaard sa mga key climbs

Halimbawa ng Pagkalkula:

Sitwasyon: Alpe d'Huez climb

  • Elevation gain: 1100 meters
  • Oras: 55 minutes = 0.917 hours
  • VAM = 1100 / 0.917 = 1200 m/h

Interpretasyon: Competitive-level na pag-ahon.

function calculateVAM(elevationGainMeters, timeMinutes) {
  const hours = timeMinutes / 60;
  return Math.round(elevationGainMeters / hours);
}

// Halimbawa:
const vam = calculateVAM(1100, 55);
// Returns: 1200 m/h

10. Pag-estimate ng W/kg mula sa VAM

Formula:

W/kg ≈ VAM (m/h) / 100 / (Gradient% + 3)

Halimbawa ng Pagkalkula:

Sitwasyon: Ahon na may 8% average gradient, VAM = 1200 m/h

W/kg = 1200 / 100 / (8 + 3)

W/kg = 12 / 11 = 4.36 W/kg

Cross-check: Para sa 70kg rider → 305W na lakas sa pag-ahon.

function estimateWkgFromVAM(vam, gradientPercent) {
  return (vam / 100 / (gradientPercent + 3)).toFixed(2);
}

// Halimbawa:
const wkg = estimateWkgFromVAM(1200, 8);
// Returns: 4.36

Aerodynamic Power Equation

11. Mga Pangangailangan sa Kabuuang Power

Kumpletong Formula:

P_total = P_aero + P_gravity + P_rolling + P_kinetic

Mga Formula ng Bawat Component:

Aerodynamic Drag:
P_aero = CdA × 0.5 × ρ × V³
Gravitational (Pag-ahon):
P_gravity = m × g × sin(θ) × V
Rolling Resistance:
P_rolling = Crr × m × g × cos(θ) × V
Kinetic (Acceleration):
P_kinetic = m × a × V

Mga Constant at Variable:

  • CdA = Drag coefficient × frontal area (m²)
    • Karaniwang road bike hoods: 0.35-0.40 m²
    • Drops: 0.32-0.37 m²
    • TT position: 0.20-0.25 m²
  • ρ = Densidad ng hangin (1.225 kg/m³ sa sea level, 15°C)
  • V = Bilis o Velocity (m/s)
  • m = Kabuuang bigat (rider + bike, kg)
  • g = Gravity (9.81 m/s²)
  • θ = Anggulo ng gradient (radians o degrees na nai-convert)
  • Crr = Rolling resistance coefficient (~0.004 para sa magandang road tires)
  • a = Acceleration (m/s²)

Halimbawa ng Pagkalkula (Flat Road TT):

Sitwasyon:

  • Bilis: 40 km/h = 11.11 m/s
  • CdA: 0.22 m² (mahusay na TT position)
  • Kabuuang bigat: 75kg (rider) + 8kg (bike) = 83kg
  • Patag na kalsada (gradient = 0°)
  • Consistent na bilis (acceleration = 0)

Pagkalkula:

  1. P_aero = 0.22 × 0.5 × 1.225 × 11.11³ = 185W
  2. P_gravity = 0W (flat kalsada)
  3. P_rolling = 0.004 × 83 × 9.81 × 11.11 = 36W
  4. P_kinetic = 0W (constant bilis)
  5. P_total = 185 + 0 + 36 + 0 = 221W

Interpretasyon: Kailangan ng 221W para mapanatili ang 40 km/h sa TT position sa patag na kalsada.

Implementation sa JavaScript:

function calculatePowerRequired(params) {
  const {
    velocityKph,
    CdA = 0.32,              // m²
    rho = 1.225,             // kg/m³
    mass = 83,               // kg (rider + bike)
    gradientPercent = 0,     // %
    Crr = 0.004,             // rolling resistance
    accelerationMps2 = 0     // m/s²
  } = params;

  // I-convert ang velocity sa m/s
  const V = velocityKph / 3.6;

  // I-convert ang gradient sa anggulo
  const theta = Math.atan(gradientPercent / 100);

  // Kalkulahin ang bawat component
  const P_aero = CdA * 0.5 * rho * Math.pow(V, 3);
  const P_gravity = mass * 9.81 * Math.sin(theta) * V;
  const P_rolling = Crr * mass * 9.81 * Math.cos(theta) * V;
  const P_kinetic = mass * accelerationMps2 * V;

  return {
    total: Math.round(P_aero + P_gravity + P_rolling + P_kinetic),
    aero: Math.round(P_aero),
    gravity: Math.round(P_gravity),
    rolling: Math.round(P_rolling),
    kinetic: Math.round(P_kinetic)
  };
}

// Halimbawa: TT sa 40 km/h
const power_tt = calculatePowerRequired({
  velocityKph: 40,
  CdA: 0.22,
  mass: 83,
  gradientPercent: 0
});
// Returns: { total: 221, aero: 185, gravity: 0, rolling: 36, kinetic: 0 }

// Halimbawa: 8% climb sa 15 km/h
const power_climb = calculatePowerRequired({
  velocityKph: 15,
  CdA: 0.38,
  mass: 75,
  gradientPercent: 8
});
// Returns: { total: 265, aero: 27, gravity: 244, rolling: 11, kinetic: 0 }

Mga Helper Function

Mga Utility para sa Unit Conversion

Implementation sa JavaScript:

// Mga conversion ng oras
function hoursToSeconds(hours) {
  return hours * 3600;
}

function minutesToSeconds(minutes) {
  return minutes * 60;
}

function secondsToHours(seconds) {
  return seconds / 3600;
}

function formatDuration(seconds) {
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = Math.round(seconds % 60);
  return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}

// Mga conversion ng bilis
function kphToMps(kph) {
  return kph / 3.6;
}

function mpsToKph(mps) {
  return mps * 3.6;
}

// Mga conversion ng enerhiya
function joulesTo kJ(joules) {
  return joules / 1000;
}

function kJToJoules(kJ) {
  return kJ * 1000;
}

function wattsToKJ(watts, durationSeconds) {
  return (watts * durationSeconds) / 1000;
}

// Mga Halamba:
formatDuration(7265);        // Returns: "2:01:05"
kphToMps(40);                // Returns: 11.11 m/s
wattsToKJ(250, 3600);        // Returns: 900 kJ (1 oras sa 250W)

Mga Resource para sa Implementation

Lahat ng formula sa pahinang ito ay production-ready at napatunayan na base sa siyentipikong literatura at real-world power meter data. Gamitin ang mga ito para sa mga custom analytics tool, verification, o mas malalim na pag-unawa sa mga kalkulasyon sa power-based training.

💡 Mga Best Practice

  • I-validate ang mga input: Suriin ang mga makatwirang power range (0-2000W) at positibong duration.
  • Asikasuhin ang mga edge cases: Paghahati sa zero, null/undefined data, o kakulangan ng FTP.
  • Mag-round nang wasto: Ang CTL/ATL/TSB sa 1 decimal, TSS sa integer, at W/kg sa 2 decimals.
  • I-store ang presisyon: Panatilihin ang buong presisyon sa database, mag-round lamang para sa display.
  • Mga Time zone: I-handle nang maayos ang UTC vs. local time para sa multi-day analysis.
  • Pag-calibrate ng power meter: Paalalahanan ang mga user na mag-zero-offset bago pumadyak.
  • Pag-validate ng FTP: Lagyan ng flag ang mga kaduda-dudang FTP values (>500W o <100W para sa mga matanda).
  • Mag-test nang mabuti: Gamitin ang mga kilalang maayos na ride files para i-verify ang mga kalkulasyon.