Referensi Teknis & Formula

Implementasi Matematika Lengkap

Panduan Implementasi

Halaman ini menyediakan formula siap pakai dan metode perhitungan langkah demi langkah untuk semua metrik Run Analytics. Gunakan ini untuk implementasi kustom, verifikasi, atau pemahaman yang lebih mendalam.

⚠️ Catatan Implementasi

  • Semua waktu harus dikonversi ke detik untuk perhitungan
  • Perhitungan rTSS menggunakan Intensity Factor kuadrat (IF²)
  • Selalu validasi input untuk rentang yang wajar
  • Tangani kasus ekstrem (pembagian dengan nol, nilai negatif)

Metrik Performa Inti

Kecepatan Lari Kritis (CRS)

Formula:

CRS (m/s) = (D₂ - D₁) / (T₂ - T₁)
Pace CRS (mnt/km) = 16.667 / CRS (m/s)

🧪 Kalkulator Interaktif - Uji Formulanya

Pace CRS (mnt/km):
4:17
Langkah perhitungan:
CRS (m/s) = (3600 - 1200) / (900 - 270) = 3.81 m/s
Pace (mnt/km) = 1000 / 3.81 = 262 detik = 4:22

Implementasi JavaScript:

function calculateCRS(distance1, time1, distance2, time2) {
  // Konversi waktu ke detik
  const t1 = typeof time1 === 'string' ? timeToSeconds(time1) : time1;
  const t2 = typeof time2 === 'string' ? timeToSeconds(time2) : time2;

  // Hitung kecepatan CRS dalam m/s
  const crs_ms = (distance2 - distance1) / (t2 - t1);

  // Hitung pace per km dalam detik
  const pace_per_km = 1000 / crs_ms;

  // Konversi ke format mm:ss
  const minutes = Math.floor(pace_per_km / 60);
  const seconds = Math.round(pace_per_km % 60);

  return {
    velocity_ms: crs_ms,
    pace_seconds: pace_per_km,
    pace_formatted: `${minutes}:${seconds.toString().padStart(2, '0')}`
  };
}

Skor Stres Lari (rTSS)

Formula Lengkap:

rTSS = (IF²) × Durasi (jam) × 100
IF = NSS / Kecepatan Ambang
NSS = Total Jarak / Total Waktu (m/mnt)

🧪 Kalkulator Interaktif - Uji Formulanya

Hasil Perhitungan rTSS:
31
Langkah perhitungan:
NSS = 5000m / 25mnt = 200 m/mnt
FTP = 1000 / (255/60) = 235.3 m/mnt
IF = 200 / 235.3 = 0.850
rTSS = 0.850² × (25/60) × 100 = 31

Implementasi JavaScript:

function calculateRTSS(distance, timeMinutes, ftpMetersPerMin) {
  // Hitung Kecepatan Lari Normal (NSS)
  const nss = distance / timeMinutes;

  // Hitung Faktor Intensitas (IF)
  const intensityFactor = nss / ftpMetersPerMin;

  // Hitung jam
  const hours = timeMinutes / 60;

  // Hitung rTSS menggunakan kuadrat faktor intensitas
  const rtss = Math.pow(intensityFactor, 2) * hours * 100;

  return Math.round(rtss);
}

// Contoh penggunaan:
const rtss = calculateRTSS(3000, 55, 64.5);
// Mengembalikan: 65

// Pembantu: Konversi Pace CRS ke Kecepatan Lari (m/mnt)
function crsPaceToSpeed(crsPacePerKmSeconds) {
  // Kecepatan dalam m/mnt = 1000m / (pace dalam menit)
  return 1000 / (crsPacePerKmSeconds / 60);
}

// Contoh: CRS 4:15 (255 detik)
const speed = crsPaceToSpeed(255); // Mengembalikan: 235.3 m/mnt

Efisiensi Biomekanik: Rasio Vertikal

Formula:

Rasio Vertikal (%) = (Osilasi Vertikal ÷ Panjang Langkah) × 100

🧪 Kalkulator Interaktif - Uji Formulanya

Rasio Vertikal:
7.4%
Perhitungan:
Rasio Vertikal = (8.5 / 115) × 100 = 7.4%

Implementasi JavaScript:

function calculateVerticalRatio(oscillationCm, strideLengthCm) {
  return (oscillationCm / strideLengthCm) * 100;
}

function calculateEfficiencyFactor(paceMetersPerMin, avgHeartRate) {
  return paceMetersPerMin / avgHeartRate;
}

Berlari Mekanika Langkah

Frekuensi Langkah (SR)

Formula:

SR = 60 / Waktu Siklus (detik)
SR = (Jumlah Langkah / Waktu dalam detik) × 60

🧪 Kalkulator Interaktif - Uji Formulanya

Kadens (SPM):
180
Perhitungan:
Kadens = (180 / 60) × 60 = 180 SPM

Implementasi JavaScript:

function calculateCadence(stepCount, timeSeconds) {
  return (stepCount / timeSeconds) * 60;
}

// Contoh:
const spm = calculateCadence(180, 60);
// Mengembalikan: 180 SPM

Panjang Langkah

Formula:

Panjang Langkah = Jarak / (Jumlah Langkah / 2)
Panjang Langkah = Kecepatan / (Kadens / 120)

Implementasi JavaScript:

function calculateStrideLength(distanceMeters, stepCount) {
  // Panjang langkah adalah jarak / jumlah pasangan langkah (kiri+kanan)
  return distanceMeters / (stepCount / 2);
}

// Contoh (1000m, 800 langkah):
const strideLength = calculateStrideLength(1000, 800);
// Mengembalikan: 2.50 meter per langkah

Kecepatan dari SR dan DPS

Formula:

Kecepatan (m/s) = (SR / 60) × DPS

Implementasi JavaScript:

function calculateVelocity(strideRate, dps) {
  return (strideRate / 60) * dps;
}

// Contoh:
const velocity = calculateVelocity(70, 1.6);
// Mengembalikan: 1.87 m/s

Indeks Langkah (SI)

Formula:

SI = Kecepatan (m/s) × DPS (m/langkah)

Implementasi JavaScript:

function calculateStrideIndex(velocity, dps) {
  return velocity * dps;
}

// Contoh:
const si = calculateStrideIndex(1.5, 1.7);
// Mengembalikan: 2.55

Grafik Manajemen Performa (PMC) untuk Berlari

Perhitungan CTL, ATL, TSB

Formula:

CTL hari ini = CTL kemarin + (TSS hari ini - CTL kemarin) × (1/42)
ATL hari ini = ATL kemarin + (TSS hari ini - ATL kemarin) × (1/7)
TSB = CTL kemarin - ATL kemarin

Implementasi JavaScript:

function updateCTL(previousCTL, todayTSS) {
  return previousCTL + (todayTSS - previousCTL) * (1/42);
}

function updateATL(previousATL, todayTSS) {
  return previousATL + (todayTSS - previousATL) * (1/7);
}

function calculateTSB(yesterdayCTL, yesterdayATL) {
  return yesterdayCTL - yesterdayATL;
}

// Hitung PMC untuk serangkaian latihan
function calculatePMC(workouts) {
  let ctl = 0, atl = 0;
  const results = [];

  workouts.forEach(workout => {
    ctl = updateCTL(ctl, workout.tss);
    atl = updateATL(atl, workout.tss);
    const tsb = calculateTSB(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
    });
  });

  return results;
}

// Contoh penggunaan:
const workouts = [
  { date: '2025-01-01', tss: 50 },
  { date: '2025-01-02', tss: 60 },
  { date: '2025-01-03', tss: 45 },
  // ... latihan lainnya
];

const pmc = calculatePMC(workouts);
// Mengembalikan array dengan CTL, ATL, TSB untuk setiap hari

Perhitungan Lanjutan

CRS dari Beberapa Jarak (Metode Regresi)

Implementasi JavaScript:

function calculateCRSRegression(distances, times) {
  // Regresi linear: jarak = a + b*waktu
  const n = distances.length;
  const sumX = times.reduce((a, b) => a + b, 0);
  const sumY = distances.reduce((a, b) => a + b, 0);
  const sumXY = times.reduce((sum, x, i) => sum + x * distances[i], 0);
  const sumXX = times.reduce((sum, x) => sum + x * x, 0);

  const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
  const intercept = (sumY - slope * sumX) / n;

  return {
    crs: slope, // Kecepatan lari kritis (m/s)
    anaerobic_capacity: intercept // Kapasitas jarak anaerobik (m)
  };
}

// Contoh dengan beberapa jarak pengujian:
const distances = [100, 200, 400, 800];
const times = [65, 150, 340, 720]; // dalam detik
const result = calculateCRSRegression(distances, times);
// Mengembalikan: { crs: 1.18, anaerobic_capacity: 15.3 }

Faktor Intensitas dari Pace

Implementasi JavaScript:

function calculateIntensityFactor(actualPaceMinKm, thresholdPaceMinKm) {
  // Konversi string pace "mm:ss" ke detik jika perlu
  const actualSecs = typeof actualPaceMinKm === 'string' ? timeToSeconds(actualPaceMinKm) : actualPaceMinKm;
  const thresholdSecs = typeof thresholdPaceMinKm === 'string' ? timeToSeconds(thresholdPaceMinKm) : thresholdPaceMinKm;

  // IF adalah Pace Ambang / Pace Aktual (pace lebih cepat = nilai detik lebih kecil)
  return thresholdSecs / actualSecs;
}

// Contoh:
const if_value = calculateIntensityFactor("4:45", "4:15");
// Mengembalikan: 0.895 (berlari pada 89.5% dari ambang)

Analisis Konsistensi Pace

Implementasi JavaScript:

function analyzePaceConsistency(segments) {
  const paces = segments.map(kilometer => kilometer.distance / kilometer.time);
  const avgPace = paces.reduce((a, b) => a + b) / paces.length;

  const variance = paces.reduce((sum, pace) =>
    sum + Math.pow(pace - avgPace, 2), 0) / paces.length;
  const stdDev = Math.sqrt(variance);
  const coefficientOfVariation = (stdDev / avgPace) * 100;

  return {
    avgPace,
    stdDev,
    coefficientOfVariation,
    consistency: coefficientOfVariation < 5 ? "Sangat Baik" :
                 coefficientOfVariation < 10 ? "Baik" :
                 coefficientOfVariation < 15 ? "Cukup" : "Bervariasi"
  };
}

// Contoh:
const segments = [
  { distance: 100, time: 70 },
  { distance: 100, time: 72 },
  { distance: 100, time: 71 },
  // ...
];
const analysis = analyzePaceConsistency(segments);
// Mengembalikan: { avgPace: 1.41, stdDev: 0.02, coefficientOfVariation: 1.4, consistency: "Sangat Baik" }

Deteksi Kelelehan dari Efisiensi Langkah

Implementasi JavaScript:

function detectFatigue(segments) {
  // Bandingkan Rasio Vertikal segmen pertama dan terakhir
  const firstSegment = segments[0];
  const lastSegment = segments[segments.length - 1];

  const ratioIncrease = lastSegment.verticalRatio - firstSegment.verticalRatio;

  return {
    startRatio: firstSegment.verticalRatio,
    endRatio: lastSegment.verticalRatio,
    increase: Math.round(ratioIncrease * 10) / 10,
    fatigueLevel: ratioIncrease < 0.2 ? "Minimal" :
                  ratioIncrease < 0.5 ? "Sedang" :
                  ratioIncrease < 1.0 ? "Signifikan" : "Parah"
  };
}

// Contoh:
const segments = [
  { verticalRatio: 7.2 }, { verticalRatio: 7.3 }, { verticalRatio: 8.1 }
];
const fatigue = detectFatigue(segments);
// Mengembalikan: { startRatio: 7.2, endRatio: 8.1, increase: 0.9, fatigueLevel: "Signifikan" }

Validasi Data

Pemeriksaan Kualitas Data Latihan

Implementasi JavaScript:

function validateWorkoutData(workout) {
  const issues = [];

  // Periksa rentang pace yang wajar (3:00-8:00 per km)
  const avgPaceSecs = workout.totalTime / (workout.totalDistance / 1000);
  if (avgPaceSecs < 180 || avgPaceSecs > 480) {
    issues.push(`Pace rata-rata tidak biasa: ${Math.round(avgPaceSecs)} dtk per km`);
  }

  // Periksa Rasio Vertikal yang wajar (4% - 15%)
  if (workout.avgVerticalRatio < 4 || workout.avgVerticalRatio > 15) {
    issues.push(`Rasio Vertikal tidak biasa: ${workout.avgVerticalRatio}%`);
  }

  // Periksa kadens yang wajar (120-220 SPM)
  const avgCadence = calculateCadence(workout.totalSteps, workout.totalTime);
  if (avgCadence < 120 || avgCadence > 220) {
    issues.push(`Kadens tidak biasa: ${Math.round(avgCadence)} SPM`);
  }

  // Periksa segmen yang hilang (celah waktu)
  if (workout.segments && workout.segments.length > 1) {
    for (let i = 1; i < workout.segments.length; i++) {
      const gap = workout.segments[i].startTime -
                  (workout.segments[i-1].startTime + workout.segments[i-1].duration);
      if (gap > 300) { // Celah 5 menit
        issues.push(`Terdeteksi celah besar antara segmen ${i} dan ${i+1}`);
      }
    }
  }

  return {
    isValid: issues.length === 0,
    issues
  };
}

// Contoh:
const workout = {
  totalDistance: 2000,
  totalTime: 1800, // 30 menit
  totalStrides: 800,
  segments: [/* data kilometer */]
};
const validation = validateWorkoutData(workout);
// Mengembalikan: { isValid: true, issues: [] }

Fungsi Pembantu

Utilitas Konversi Waktu

Implementasi JavaScript:

// Konversi mm:ss ke detik
function timeToSeconds(timeString) {
  const parts = timeString.split(':');
  return parseInt(parts[0]) * 60 + parseInt(parts[1]);
}

// Konversi detik ke mm:ss
function secondsToTime(seconds) {
  const minutes = Math.floor(seconds / 60);
  const secs = Math.round(seconds % 60);
  return `${minutes}:${secs.toString().padStart(2, '0')}`;
}