Dokumentacja Techniczna i Formuły

Kompletna Implementacja Matematyczna

Przewodnik Implementacji

Ta strona dostarcza formuł gotowych do skopiowania oraz metod obliczeniowych krok po kroku dla wszystkich metryk SwimAnalytics. Użyj ich do niestandardowych implementacji, weryfikacji lub głębszego zrozumienia.

⚠️ Uwagi Implementacyjne

  • Wszystkie czasy muszą być przekonwertowane na sekundy do obliczeń
  • Tempo pływania jest odwrotne (wyższy % = wolniejsze tempo)
  • Zawsze waliduj dane wejściowe dla rozsądnych zakresów
  • Obsługuj przypadki graniczne (dzielenie przez zero, wartości ujemne)

Główne Metryki Wydolności

Critical Swim Speed (CSS)

Formuła:

CSS (m/s) = (D₂ - D₁) / (T₂ - T₁)
CSS Tempo/100m (sekundy) = (T₄₀₀ - T₂₀₀) / 2

🧪 Kalkulator Interaktywny - Przetestuj Formułę

Tempo CSS na 100m:
1:49
Kroki obliczenia:
CSS (m/s) = (400 - 200) / (368 - 150) = 0.917 m/s
Tempo/100m = 100 / 0.917 = 109 sekund = 1:49

Implementacja w 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" }

Swim Training Stress Score (sTSS)

Formuła Kompletna:

sTSS = (IF³) × Czas Trwania (godziny) × 100
IF = NSS / FTP
NSS = Dystans Całkowity / Czas Całkowity (m/min)

🧪 Kalkulator Interaktywny - Przetestuj Formułę

Obliczone sTSS:
55
Kroki obliczenia:
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

Implementacja w 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

Formuła:

SWOLF = Czas Długości (sekundy) + Liczba Ruchów
SWOLF₂₅ = (Czas × 25/Długość Basenu) + (Ruchy × 25/Długość Basenu)

🧪 Kalkulator Interaktywny - Przetestuj Formułę

Wynik SWOLF:
35
Obliczenie:
SWOLF = 20s + 15 ruchów = 35

Implementacja w 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)

Mechanika Ruchu

Stroke Rate (SR)

Formuła:

SR = 60 / Czas Cyklu (sekundy)
SR = (Liczba Ruchów / Czas w sekundach) × 60

🧪 Kalkulator Interaktywny - Przetestuj Formułę

Stroke Rate (SPM):
72
Obliczenie:
SR = (30 / 25) × 60 = 72 SPM

Implementacja w JavaScript:

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

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

Distance Per Stroke (DPS)

Formuła:

DPS = Dystans / Liczba Ruchów
DPS = Dystans / (SR / 60)

Implementacja w 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)

Prędkość z SR i DPS

Formuła:

Prędkość (m/s) = (SR / 60) × DPS

Implementacja w JavaScript:

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

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

Stroke Index (SI)

Formuła:

SI = Prędkość (m/s) × DPS (m/ruch)

Implementacja w JavaScript:

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

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

Wykres Zarządzania Wydolnością (PMC)

Obliczenia CTL, ATL, TSB

Formuły:

CTL dziś = CTL wczoraj + (TSS dziś - CTL wczoraj) × (1/42)
ATL dziś = ATL wczoraj + (TSS dziś - ATL wczoraj) × (1/7)
TSB = CTL wczoraj - ATL wczoraj

Implementacja w 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

Obliczenia Zaawansowane

CSS z Wielu Dystansów (Metoda Regresji)

Implementacja w 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 }

Czynnik Intensywności z Tempa

Implementacja w 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)

Analiza Konsystencji Tempa

Implementacja w 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 ? "Doskonała" :
                 coefficientOfVariation < 10 ? "Dobra" :
                 coefficientOfVariation < 15 ? "Umiarkowana" : "Zmienna"
  };
}

// 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: "Doskonała" }

Wykrywanie Zmęczenia poprzez Liczbę Ruchów

Implementacja w 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 ? "Minimalne" :
                  strokeCountIncrease < 10 ? "Umiarkowane" :
                  strokeCountIncrease < 20 ? "Znaczące" : "Poważne"
  };
}

// 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: "Poważne" }

Walidacja Danych

Weryfikacja Jakości Danych Treningowych

Implementacja w 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(`Niezwykłe tempo średnie: ${Math.round(avgPace)}s na 100m`);
  }

  // Check for reasonable stroke counts (10-50 per 25m)
  const avgStrokesPer25m = (workout.totalStrokes / workout.totalDistance) * 25;
  if (avgStrokesPer25m < 10 || avgStrokesPer25m > 50) {
    issues.push(`Niezwykła liczba ruchów: ${Math.round(avgStrokesPer25m)} na 25m`);
  }

  // Check for reasonable stroke rate (30-150 SPM)
  const avgSR = calculateStrokeRate(workout.totalStrokes, workout.totalTime);
  if (avgSR < 30 || avgSR > 150) {
    issues.push(`Niezwykła częstotliwość ruchów: ${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(`Wykryto dużą przerwę między długościami ${i} i ${i+1}`);
      }
    }
  }

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

// Example:
const workout = {
  totalDistance: 2000,
  totalTime: 1800, // 30 minutes
  totalStrokes: 800,
  laps: [/* lap data */]
};
const validation = validateWorkoutData(workout);
// Returns: { isValid: true, issues: [] }

Funkcje Pomocnicze

Narzędzia Konwersji Czasu

Implementacja w JavaScript:

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

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

// Convert seconds to hh:mm:ss
function secondsToTimeDetailed(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')}`;
}

// Examples:
timeToSeconds("1:33"); // Returns: 93
secondsToTime(93); // Returns: "1:33"
secondsToTimeDetailed(3665); // Returns: "1:01:05"

Zasoby Implementacyjne

Wszystkie formuły na tej stronie są gotowe do produkcji i zwalidowane względem literatury naukowej. Użyj ich do niestandardowych narzędzi analitycznych, weryfikacji lub głębszego zrozumienia obliczeń wydolności pływackiej.

💡 Najlepsze Praktyki

  • Waliduj dane wejściowe: Sprawdzaj rozsądne zakresy przed obliczeniami
  • Obsługuj przypadki graniczne: Dzielenie przez zero, wartości ujemne, dane puste
  • Zaokrąglaj odpowiednio: CTL/ATL/TSB do 1 miejsca po przecinku, sTSS do liczby całkowitej
  • Przechowuj precyzję: Zachowuj pełną precyzję w bazie danych, zaokrąglaj do wyświetlenia
  • Testuj gruntownie: Używaj znanych poprawnych danych do weryfikacji obliczeń