Updated the bgcolor settings for the Pixel=Plots area.
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
@@ -460,7 +460,7 @@ def safe_add_column_to_dataframe(df, column_name, values):
|
|||||||
# ── Biometrics (kalibriert gegen Strava) ─────────────────────────────────────
|
# ── Biometrics (kalibriert gegen Strava) ─────────────────────────────────────
|
||||||
_WEIGHT_KG = 75.0
|
_WEIGHT_KG = 75.0
|
||||||
_HEIGHT_CM = 178.0
|
_HEIGHT_CM = 178.0
|
||||||
_AGE_YEARS = 35
|
_AGE_YEARS = 36
|
||||||
_IS_MALE = True
|
_IS_MALE = True
|
||||||
_HR_REST = 64.5 # Ruhepuls in bpm – kalibriert gegen Strava 550 kcal
|
_HR_REST = 64.5 # Ruhepuls in bpm – kalibriert gegen Strava 550 kcal
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
@@ -488,30 +488,45 @@ def calculate_calories_burned(df):
|
|||||||
# Kein fixer HR_REST — nutze den tatsächlichen Minimum-HR aus dem Lauf
|
# Kein fixer HR_REST — nutze den tatsächlichen Minimum-HR aus dem Lauf
|
||||||
# als Annäherung an den Ruhepuls
|
# als Annäherung an den Ruhepuls
|
||||||
hr_max = 220 - _AGE_YEARS
|
hr_max = 220 - _AGE_YEARS
|
||||||
|
hr_rest = _HR_REST # 64.5 bpm
|
||||||
|
|
||||||
|
# Check, ob genug Herzfrequenz-Daten vorhanden sind
|
||||||
use_hr = ('heart_rate' in df.columns and
|
use_hr = ('heart_rate' in df.columns and
|
||||||
df['heart_rate'].notna().sum() > len(df) * 0.5)
|
df['heart_rate'].notna().sum() > len(df) * 0.5)
|
||||||
|
|
||||||
if not use_hr:
|
if not use_hr:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
# Datenvorbereitung
|
||||||
hr = df['heart_rate'].ffill().fillna(df['heart_rate'].bfill()).values
|
hr = df['heart_rate'].ffill().fillna(df['heart_rate'].bfill()).values
|
||||||
ts = df['time_diff_sec'].values
|
ts = df['time_diff_sec'].values
|
||||||
|
|
||||||
# Ruhepuls aus dem Lauf selbst schätzen: 5. Perzentile der HR-Werte
|
# 2. VO2max Schätzung basierend auf deinem echten Ruhepuls
|
||||||
hr_rest = float(df['heart_rate'].quantile(0.05))
|
# Die Formel 15 * (HR_max / HR_rest) ist ein Standard-Approximationswert
|
||||||
hr_rest = max(45.0, min(hr_rest, 80.0)) # Plausibilitätsgrenze
|
|
||||||
|
|
||||||
vo2max = 15.0 * (hr_max / hr_rest)
|
vo2max = 15.0 * (hr_max / hr_rest)
|
||||||
|
|
||||||
|
# Zeitdifferenzen berechnen
|
||||||
dt = np.diff(ts, prepend=ts[0])
|
dt = np.diff(ts, prepend=ts[0])
|
||||||
mask = (dt > 0) & (dt <= 10)
|
mask = (dt > 0) & (dt <= 10) # Filtert Pausen oder Sprünge in der Aufzeichnung
|
||||||
|
|
||||||
|
# 3. Berechnung der Intensität (Karvonen-Prinzip)
|
||||||
|
# Anteil der Herzfrequenzreserve (HRR)
|
||||||
frac = np.clip((hr - hr_rest) / (hr_max - hr_rest), 0, None)
|
frac = np.clip((hr - hr_rest) / (hr_max - hr_rest), 0, None)
|
||||||
|
|
||||||
|
# Umrechnung in MET (Metabolic Equivalent of Task)
|
||||||
|
# Ein MET = 3.5 ml VO2/kg/min
|
||||||
met = np.clip((frac * vo2max) / 3.5, 1.0, 18.0)
|
met = np.clip((frac * vo2max) / 3.5, 1.0, 18.0)
|
||||||
|
|
||||||
|
# 4. Umrechnung in kcal pro Sekunde
|
||||||
|
# Formel: (MET * 3.5 * Gewicht_kg) / (200 * 60) -> 12000
|
||||||
kcal_per_s = met * _WEIGHT_KG * 3.5 / 12000.0
|
kcal_per_s = met * _WEIGHT_KG * 3.5 / 12000.0
|
||||||
|
|
||||||
|
# Summation über den gesamten Lauf
|
||||||
cumulative = float(np.sum(kcal_per_s[mask] * dt[mask]))
|
cumulative = float(np.sum(kcal_per_s[mask] * dt[mask]))
|
||||||
|
|
||||||
print(f"DEBUG calories: rows={len(df)}, hr_valid={df['heart_rate'].notna().sum()}, duration={df['time_diff_sec'].iloc[-1]:.0f}s, hr_mean={df['heart_rate'].mean():.1f}")
|
print(f"DEBUG calories: HR_REST_USED={hr_rest}, VO2MAX_EST={vo2max:.1f}, "
|
||||||
|
f"DURATION={df['time_diff_sec'].iloc[-1]/60:.1f}min, TOTAL_KCAL={cumulative:.1f}")
|
||||||
|
|
||||||
return int(round(cumulative))
|
return int(round(cumulative))
|
||||||
|
|
||||||
|
|
||||||
@@ -2408,7 +2423,7 @@ app.layout = html.Div([
|
|||||||
|
|
||||||
# START !!!!!!!!!!!!!!!
|
# START !!!!!!!!!!!!!!!
|
||||||
# Pixel-Map Überschrift
|
# Pixel-Map Überschrift
|
||||||
html.Hr(style={'borderColor': '#333', 'margin': '10px 20px'}),
|
html.Hr(style={'borderColor': '#111111', 'margin': '10px 20px'}), #'#333'
|
||||||
html.Div([
|
html.Div([
|
||||||
html.H3("Pixel-Maps", style={
|
html.H3("Pixel-Maps", style={
|
||||||
'color': '#aaaaaa', 'margin': '10px 0 0 0', 'fontSize': '16px'
|
'color': '#aaaaaa', 'margin': '10px 0 0 0', 'fontSize': '16px'
|
||||||
@@ -2416,7 +2431,7 @@ app.layout = html.Div([
|
|||||||
html.P(
|
html.P(
|
||||||
"Plot 1) Heatmap: Einzellauf oder alle Läufe der Region umschalten.\n"
|
"Plot 1) Heatmap: Einzellauf oder alle Läufe der Region umschalten.\n"
|
||||||
"Plot 2), 3) & 4) Elevation-, Pace- & HR-Map: des aktuell gewählten Laufs.",
|
"Plot 2), 3) & 4) Elevation-, Pace- & HR-Map: des aktuell gewählten Laufs.",
|
||||||
style={'color': '#666', 'margin': '2px 0 8px 0', 'fontSize': '12px'}
|
style={'color': '#aaaaaa', 'margin': '2px 0 8px 0', 'fontSize': '12px'}
|
||||||
),
|
),
|
||||||
], style={'padding': '0 20px'}),
|
], style={'padding': '0 20px'}),
|
||||||
|
|
||||||
@@ -2488,7 +2503,7 @@ app.layout = html.Div([
|
|||||||
'backgroundColor': '#111111'
|
'backgroundColor': '#111111'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
html.Hr(style={'borderColor': '#333', 'margin': '10px 20px'}),
|
html.Hr(style={'borderColor': '#111111', 'margin': '10px 20px'}),
|
||||||
# ENDE !!!!!!!!!!!!!!!!
|
# ENDE !!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
dcc.Graph(id='fig-elevation'),
|
dcc.Graph(id='fig-elevation'),
|
||||||
|
|||||||
Reference in New Issue
Block a user