Tài liệu Tham khảo Kỹ thuật & Công thức

Triển khai Toán học Hoàn chỉnh

Hướng dẫn Triển khai

Trang này cung cấp các công thức sao chép-dán và phương pháp tính toán từng bước cho tất cả các chỉ số của Swim Analytics. Sử dụng chúng để tự triển khai, xác minh, hoặc hiểu sâu hơn.

⚠️ Ghi chú Triển khai

  • Tất cả thời gian nên được chuyển đổi sang giây để tính toán
  • Tốc độ bơi là nghịch đảo (số càng cao = tốc độ càng chậm)
  • Luôn xác thực đầu vào cho các phạm vi hợp lý
  • Xử lý các trường hợp biên (chia cho không, giá trị âm)

Bạn muốn tránh tính toán thủ công? Đọc của chúng tôiHướng dẫn tải đào tạohoặc sử dụng ứng dụng Phân tích bơi lội miễn phí.

Các Chỉ số Hiệu suất Cốt lõi

Tốc độ Bơi tới hạn (CSS)

Công thức:

CSS (m/s) = (D₂ - D₁) / (T₂ - T₁)
Tốc độ CSS/100m (giây) = (T₄₀₀ - T₂₀₀) / 2

🧪 Máy tính Tương tác - Kiểm tra Công thức

Tốc độ CSS mỗi 100m:
1:49
Các bước tính toán:
CSS (m/s) = (400 - 200) / (368 - 150) = 0.917 m/s
Pace/100m = 100 / 0.917 = 109 seconds = 1:49

Triển khai JavaScript:

function calculateCSS(distance1, time1, distance2, time2) {
  // Convert times to seconds if needed
  const t1 = typeof time1 === 'string' ? timeToSeconds(time1) : time1;
  const t2 = typeof time2 === 'string' ? timeToSeconds(time2) : time2;

  // Calculate CSS in m/s
  const css_ms = (distance2 - distance1) / (t2 - t1);

  // Calculate pace per 100m in seconds
  const pace_per_100m = 100 / css_ms;

  // Convert to mm:ss format
  const minutes = Math.floor(pace_per_100m / 60);
  const seconds = Math.round(pace_per_100m % 60);

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

// Example usage:
const result = calculateCSS(200, 150, 400, 368);
// Returns: { css_ms: 0.917, pace_seconds: 109, pace_formatted: "1:49" }

Điểm Căng thẳng Tập luyện Bơi lội (sTSS)

Công thức Hoàn chỉnh:

sTSS = (IF³) × Thời lượng (giờ) × 100
IF = NSS / FTP
NSS = Tổng Khoảng cách / Tổng Thời gian (m/phút)

🧪 Máy tính Tương tác - Kiểm tra Công thức

sTSS Đã tính:
55
Các bước tính toán:
NSS = 3000m / 55min = 54.5 m/min
FTP = 100 / (93/60) = 64.5 m/min
IF = 54.5 / 64.5 = 0.845
sTSS = 0.845³ × (55/60) × 100 = 55

Triển khai JavaScript:

function calculateSTSS(distance, timeMinutes, ftpMetersPerMin) {
  // Calculate Normalized Swim Speed
  const nss = distance / timeMinutes;

  // Calculate Intensity Factor
  const intensityFactor = nss / ftpMetersPerMin;

  // Calculate hours
  const hours = timeMinutes / 60;

  // Calculate sTSS using cubed intensity factor
  const stss = Math.pow(intensityFactor, 3) * hours * 100;

  return Math.round(stss);
}

// Example usage:
const stss = calculateSTSS(3000, 55, 64.5);
// Returns: 55

// Helper: Convert CSS to FTP
function cssToFTP(cssPacePer100mSeconds) {
  // FTP in m/min = 100m / (pace in minutes)
  return 100 / (cssPacePer100mSeconds / 60);
}

// Example: CSS of 1:33 (93 seconds)
const ftp = cssToFTP(93); // Returns: 64.5 m/min

SWOLF

Công thức:

SWOLF = Thời gian vòng bơi (giây) + Số sải tay
SWOLF₂₅ = (Thời gian × 25/Chiều dài bể) + (Sải tay × 25/Chiều dài bể)

🧪 Máy tính Tương tác - Kiểm tra Công thức

Điểm SWOLF:
35
Tính toán:
SWOLF = 20s + 15 strokes = 35

Triển khai JavaScript:

function calculateSWOLF(timeSeconds, strokeCount) {
  return timeSeconds + strokeCount;
}

function calculateNormalizedSWOLF(timeSeconds, strokeCount, poolLength) {
  const normalizedTime = timeSeconds * (25 / poolLength);
  const normalizedStrokes = strokeCount * (25 / poolLength);
  return normalizedTime + normalizedStrokes;
}

// Example:
const swolf = calculateSWOLF(20, 15);
// Returns: 35

const swolf50m = calculateNormalizedSWOLF(40, 30, 50);
// Returns: 35 (normalized to 25m)

Cơ học Sải tay

Nhịp sải tay (Stroke Rate - SR)

Công thức:

SR = 60 / Thời gian chu kỳ (giây)
SR = (Số sải tay / Thời gian tính bằng giây) × 60

🧪 Máy tính Tương tác - Kiểm tra Công thức

Nhịp sải tay (SPM):
72
Tính toán:
SR = (30 / 25) × 60 = 72 SPM

Triển khai JavaScript:

function calculateStrokeRate(strokeCount, timeSeconds) {
  return (strokeCount / timeSeconds) * 60;
}

// Example:
const sr = calculateStrokeRate(30, 25);
// Returns: 72 SPM

Khoảng cách mỗi Sải tay (Distance Per Stroke - DPS)

Công thức:

DPS = Khoảng cách / Số sải tay
DPS = Khoảng cách / (SR / 60)

Triển khai JavaScript:

function calculateDPS(distance, strokeCount, pushoffDistance = 0) {
  const effectiveDistance = distance - pushoffDistance;
  return effectiveDistance / strokeCount;
}

// Example (25m pool, 5m push-off):
const dps = calculateDPS(25, 12, 5);
// Returns: 1.67 m/stroke

// For multiple laps:
const dps100m = calculateDPS(100, 48, 4 * 5);
// Returns: 1.67 m/stroke (4 laps × 5m push-off)

Vận tốc từ SR và DPS

Công thức:

Vận tốc (m/s) = (SR / 60) × DPS

Triển khai JavaScript:

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

// Example:
const velocity = calculateVelocity(70, 1.6);
// Returns: 1.87 m/s

Chỉ số Sải tay (Stroke Index - SI)

Công thức:

SI = Vận tốc (m/s) × DPS (m/stroke)

Triển khai JavaScript:

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

// Example:
const si = calculateStrokeIndex(1.5, 1.7);
// Returns: 2.55

Biểu đồ Quản lý Hiệu suất (PMC)

Tính toán CTL, ATL, TSB

Công thức:

CTL hôm nay = CTL hôm qua + (TSS hôm nay - CTL hôm qua) × (1/42)
ATL hôm nay = ATL hôm qua + (TSS hôm nay - ATL hôm qua) × (1/7)
TSB = CTL hôm qua - ATL hôm qua

Triển khai 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;
}

// Calculate PMC for series of workouts
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;
}

// Example usage:
const workouts = [
  { date: '2025-01-01', tss: 50 },
  { date: '2025-01-02', tss: 60 },
  { date: '2025-01-03', tss: 45 },
  // ... more workouts
];

const pmc = calculatePMC(workouts);
// Returns array with CTL, ATL, TSB for each day

Tính toán Nâng cao

CSS từ Nhiều khoảng cách (Phương pháp Hồi quy)

Triển khai JavaScript:

function calculateCSSRegression(distances, times) {
  // Linear regression: distance = a + b*time
  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 {
    css: slope, // Critical swimming velocity (m/s)
    anaerobic_capacity: intercept // Anaerobic distance capacity (m)
  };
}

// Example with multiple test distances:
const distances = [100, 200, 400, 800];
const times = [65, 150, 340, 720]; // in seconds
const result = calculateCSSRegression(distances, times);
// Returns: { css: 1.18, anaerobic_capacity: 15.3 }

Hệ số Cường độ (IF) từ Tốc độ

Triển khai JavaScript:

function calculateIntensityFactor(actualPace100m, thresholdPace100m) {
  // Convert pace to speed (m/s)
  const actualSpeed = 100 / actualPace100m;
  const thresholdSpeed = 100 / thresholdPace100m;
  return actualSpeed / thresholdSpeed;
}

// Example:
const if_value = calculateIntensityFactor(110, 93);
// Returns: 0.845 (swimming at 84.5% of threshold)

Phân tích Sự ổn định Tốc độ

Triển khai JavaScript:

function analyzePaceConsistency(laps) {
  const paces = laps.map(lap => lap.distance / lap.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 ? "Excellent" :
                 coefficientOfVariation < 10 ? "Good" :
                 coefficientOfVariation < 15 ? "Moderate" : "Variable"
  };
}

// Example:
const laps = [
  { distance: 100, time: 70 },
  { distance: 100, time: 72 },
  { distance: 100, time: 71 },
  // ...
  ];
const analysis = analyzePaceConsistency(laps);
// Returns: { avgPace: 1.41, stdDev: 0.02, coefficientOfVariation: 1.4, consistency: "Excellent" }

Phát hiện Mệt mỏi từ Số sải tay

Triển khai JavaScript:

function detectFatigue(laps) {
  const firstThird = laps.slice(0, Math.floor(laps.length/3));
  const lastThird = laps.slice(-Math.floor(laps.length/3));

  const firstThirdAvg = firstThird.reduce((sum, lap) =>
    sum + lap.strokeCount, 0) / firstThird.length;
  const lastThirdAvg = lastThird.reduce((sum, lap) =>
    sum + lap.strokeCount, 0) / lastThird.length;

  const strokeCountIncrease = ((lastThirdAvg - firstThirdAvg) / firstThirdAvg) * 100;

  return {
    firstThirdAvg: Math.round(firstThirdAvg * 10) / 10,
    lastThirdAvg: Math.round(lastThirdAvg * 10) / 10,
    percentIncrease: Math.round(strokeCountIncrease * 10) / 10,
    fatigueLevel: strokeCountIncrease < 5 ? "Minimal" :
                  strokeCountIncrease < 10 ? "Moderate" :
                  strokeCountIncrease < 20 ? "Significant" : "Severe"
  };
}

// Example:
const laps = [
  { strokeCount: 14 }, { strokeCount: 14 }, { strokeCount: 15 },
  { strokeCount: 15 }, { strokeCount: 16 }, { strokeCount: 16 },
  { strokeCount: 17 }, { strokeCount: 18 }, { strokeCount: 18 }
];
const fatigue = detectFatigue(laps);
// Returns: { firstThirdAvg: 14.3, lastThirdAvg: 17.7, percentIncrease: 23.8, fatigueLevel: "Severe" }

Xác thực Dữ liệu

Kiểm tra Chất lượng Dữ liệu Bài tập

Triển khai JavaScript:

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

  // Check for reasonable pace ranges (1:00-5:00 per 100m)
  const avgPace = (workout.totalTime / workout.totalDistance) * 100;
  if (avgPace < 60 || avgPace > 300) {
    issues.push(`Unusual average pace: ${Math.round(avgPace)}s per 100m`);
  }

  // Check for reasonable stroke counts (10-50 per 25m)
  const avgStrokesPer25m = (workout.totalStrokes / workout.totalDistance) * 25;
  if (avgStrokesPer25m < 10 || avgStrokesPer25m > 50) {
    issues.push(`Unusual stroke count: ${Math.round(avgStrokesPer25m)} per 25m`);
  }

  // Check for reasonable stroke rate (30-150 SPM)
  const avgSR = calculateStrokeRate(workout.totalStrokes, workout.totalTime);
  if (avgSR < 30 || avgSR > 150) {
    issues.push(`Unusual stroke rate: ${Math.round(avgSR)} SPM`);
  }

  // Check for missing laps (gaps in time)
  if (workout.laps && workout.laps.length > 1) {
    for (let i = 1; i < workout.laps.length; i++) {
      const gap = workout.laps[i].startTime -
                  (workout.laps[i-1].startTime + workout.laps[i-1].duration);
      if (gap > 300) { // 5 minute gap
        issues.push(`Large gap detected between laps ${i} and ${i+1}`);
      }
    }
  }

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