Referència Tècnica i Fórmules
Implementació Matemàtica Completa
Guia d'Implementació
Aquesta pàgina proporciona fórmules copiables i mètodes de càlcul pas a pas per a totes les mètriques de Swim Analytics. Utilitza-les per a implementacions personalitzades, verificació o comprensió més profunda.
⚠️ Notes d'Implementació
- Tots els temps s'han de convertir a segons per als càlculs
- El ritme de natació és invers (% més alt = ritme més lent)
- Sempre valida les entrades per a rangs raonables
- Gestiona casos extrems (divisió per zero, valors negatius)
Voleu evitar els càlculs manuals? Llegeix el nostreGuia de càrrega d'entrenamento utilitzeu l'aplicació gratuïta Swim Analytics.
Mètriques de Rendiment Fonamentals
Critical Swim Speed (CSS)
Fórmula:
CSS (m/s) = (D₂ - D₁) / (T₂ - T₁)
Ritme CSS/100m (segons) = (T₄₀₀ - T₂₀₀) / 2
🧪 Calculadora Interactiva - Prova la Fórmula
Ritme CSS per 100m:
1:49
Passos del càlcul:
CSS (m/s) = (400 - 200) / (368 - 150) = 0.917 m/s
Ritme/100m = 100 / 0.917 = 109 segons = 1:49
CSS (m/s) = (400 - 200) / (368 - 150) = 0.917 m/s
Ritme/100m = 100 / 0.917 = 109 segons = 1:49
Implementació 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)
Fórmula Completa:
sTSS = (IF³) × Durada (hores) × 100
IF = NSS / FTP
NSS = Distància Total / Temps Total (m/min)
🧪 Calculadora Interactiva - Prova la Fórmula
sTSS Calculat:
55
Passos del càlcul:
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
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
Implementació 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/minSWOLF
Fórmula:
SWOLF = Temps del Llarg (segons) + Recompte de Braçades
SWOLF₂₅ = (Temps × 25/Llargada Piscina) + (Braçades × 25/Llargada Piscina)
🧪 Calculadora Interactiva - Prova la Fórmula
Puntuació SWOLF:
35
Càlcul:
SWOLF = 20s + 15 braçades = 35
SWOLF = 20s + 15 braçades = 35
Implementació 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)Mecànica de Braçada
Freqüència de Braçada (SR)
Fórmula:
SR = 60 / Temps de Cicle (segons)
SR = (Nombre de Braçades / Temps en segons) × 60
🧪 Calculadora Interactiva - Prova la Fórmula
Freqüència de Braçada (SPM):
72
Càlcul:
SR = (30 / 25) × 60 = 72 SPM
SR = (30 / 25) × 60 = 72 SPM
Implementació JavaScript:
function calculateStrokeRate(strokeCount, timeSeconds) {
return (strokeCount / timeSeconds) * 60;
}
// Example:
const sr = calculateStrokeRate(30, 25);
// Returns: 72 SPMDistància Per Braçada (DPS)
Fórmula:
DPS = Distància / Recompte de Braçades
DPS = Distància / (SR / 60)
Implementació 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)Velocitat des de SR i DPS
Fórmula:
Velocitat (m/s) = (SR / 60) × DPS
Implementació JavaScript:
function calculateVelocity(strokeRate, dps) {
return (strokeRate / 60) * dps;
}
// Example:
const velocity = calculateVelocity(70, 1.6);
// Returns: 1.87 m/sÍndex de Braçada (SI)
Fórmula:
SI = Velocitat (m/s) × DPS (m/braçada)
Implementació JavaScript:
function calculateStrokeIndex(velocity, dps) {
return velocity * dps;
}
// Example:
const si = calculateStrokeIndex(1.5, 1.7);
// Returns: 2.55Performance Management Chart (PMC)
Càlculs de CTL, ATL, TSB
Fórmules:
CTL avui = CTL ahir + (TSS avui - CTL ahir) × (1/42)
ATL avui = ATL ahir + (TSS avui - ATL ahir) × (1/7)
TSB = CTL ahir - ATL ahir
Implementació 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 dayCàlculs Avançats
CSS des de Múltiples Distàncies (Mètode de Regressió)
Implementació 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 }Factor d'Intensitat des del Ritme
Implementació 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)Anàlisi de Consistència del Ritme
Implementació 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 ? "Excel·lent" :
coefficientOfVariation < 10 ? "Bo" :
coefficientOfVariation < 15 ? "Moderat" : "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: "Excel·lent" }Detecció de Fatiga des del Recompte de Braçades
Implementació 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 ? "Mínima" :
strokeCountIncrease < 10 ? "Moderada" :
strokeCountIncrease < 20 ? "Significativa" : "Severa"
};
}
// 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: "Severa" }Validació de Dades
Comprovacions de Qualitat de Dades d'Entrenament
Implementació 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(`Ritme mitjà inusual: ${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(`Recompte de braçades inusual: ${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(`Freqüència de braçada inusual: ${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(`Buit gran detectat entre llargs ${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: [] }Funcions d'Ajuda
Utilitats de Conversió de Temps
Implementació 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"Recursos d'Implementació
Totes les fórmules d'aquesta pàgina estan preparades per a producció i validades contra literatura científica. Utilitza-les per a eines d'analítica personalitzades, verificació o comprensió més profunda dels càlculs de rendiment en natació.
💡 Bones Pràctiques
- Valida les entrades: Comprova rangs raonables abans de calcular
- Gestiona casos extrems: Divisió per zero, valors negatius, dades nul·les
- Arrodoneix adequadament: CTL/ATL/TSB a 1 decimal, sTSS a enter
- Emmagatzema precisió: Manté precisió completa a la base de dades, arrodoneix per a visualització
- Prova a fons: Utilitza dades conegudes per verificar càlculs
