mirror of
https://github.com/sstent/FitTrack_GarminSync.git
synced 2026-01-27 01:22:03 +00:00
feat: Add files from examples/Garmin_Analyser
This commit is contained in:
5
examples/Garmin_Analyser/config/__init__.py
Normal file
5
examples/Garmin_Analyser/config/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Configuration management for Garmin Analyser."""
|
||||
|
||||
from . import settings
|
||||
|
||||
__all__ = ['settings']
|
||||
79
examples/Garmin_Analyser/config/config.yaml
Normal file
79
examples/Garmin_Analyser/config/config.yaml
Normal file
@@ -0,0 +1,79 @@
|
||||
# Garmin Analyser Configuration
|
||||
|
||||
# Garmin Connect credentials (optional - can be provided via environment variables)
|
||||
garmin_username: your_garmin_username
|
||||
garmin_password: your_garmin_password
|
||||
|
||||
# Output settings
|
||||
output_dir: output
|
||||
log_level: INFO
|
||||
|
||||
# Training zones configuration
|
||||
zones:
|
||||
# Functional Threshold Power (W)
|
||||
ftp: 250
|
||||
|
||||
# Maximum heart rate (bpm)
|
||||
max_heart_rate: 185
|
||||
|
||||
# Power zones as percentage of FTP
|
||||
power_zones:
|
||||
- name: Active Recovery
|
||||
min: 0
|
||||
max: 55
|
||||
percentage: true
|
||||
- name: Endurance
|
||||
min: 56
|
||||
max: 75
|
||||
percentage: true
|
||||
- name: Tempo
|
||||
min: 76
|
||||
max: 90
|
||||
percentage: true
|
||||
- name: Threshold
|
||||
min: 91
|
||||
max: 105
|
||||
percentage: true
|
||||
- name: VO2 Max
|
||||
min: 106
|
||||
max: 120
|
||||
percentage: true
|
||||
- name: Anaerobic
|
||||
min: 121
|
||||
max: 150
|
||||
percentage: true
|
||||
|
||||
# Heart rate zones as percentage of max HR
|
||||
heart_rate_zones:
|
||||
- name: Zone 1 - Recovery
|
||||
min: 0
|
||||
max: 60
|
||||
percentage: true
|
||||
- name: Zone 2 - Endurance
|
||||
min: 60
|
||||
max: 70
|
||||
percentage: true
|
||||
- name: Zone 3 - Tempo
|
||||
min: 70
|
||||
max: 80
|
||||
percentage: true
|
||||
- name: Zone 4 - Threshold
|
||||
min: 80
|
||||
max: 90
|
||||
percentage: true
|
||||
- name: Zone 5 - VO2 Max
|
||||
min: 90
|
||||
max: 100
|
||||
percentage: true
|
||||
|
||||
# Chart settings
|
||||
charts:
|
||||
theme: seaborn
|
||||
figsize: [12, 8]
|
||||
dpi: 300
|
||||
|
||||
# Report settings
|
||||
reports:
|
||||
include_charts: true
|
||||
include_raw_data: false
|
||||
timezone: UTC
|
||||
156
examples/Garmin_Analyser/config/settings.py
Normal file
156
examples/Garmin_Analyser/config/settings.py
Normal file
@@ -0,0 +1,156 @@
|
||||
"""Configuration settings for Garmin Analyser."""
|
||||
|
||||
import os
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict, Tuple
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Logger for this module
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Base paths
|
||||
BASE_DIR = Path(__file__).parent.parent
|
||||
DATA_DIR = BASE_DIR / "data"
|
||||
REPORTS_DIR = BASE_DIR / "reports"
|
||||
|
||||
# Database settings
|
||||
DB_PATH = BASE_DIR / "garmin_analyser.db"
|
||||
DATABASE_URL = f"sqlite:///{DB_PATH}"
|
||||
|
||||
# Create directories if they don't exist
|
||||
DATA_DIR.mkdir(exist_ok=True)
|
||||
REPORTS_DIR.mkdir(exist_ok=True)
|
||||
|
||||
# Garmin Connect credentials
|
||||
GARMIN_EMAIL = os.getenv("GARMIN_EMAIL")
|
||||
GARMIN_PASSWORD = os.getenv("GARMIN_PASSWORD")
|
||||
|
||||
# Flag to ensure deprecation warning is logged only once per process
|
||||
_deprecation_warned = False
|
||||
|
||||
def get_garmin_credentials() -> Tuple[str, str]:
|
||||
"""Get Garmin Connect credentials from environment variables.
|
||||
|
||||
Prefers GARMIN_EMAIL and GARMIN_PASSWORD. If GARMIN_EMAIL is not set
|
||||
but GARMIN_USERNAME is present, uses GARMIN_USERNAME as email with a
|
||||
one-time deprecation warning.
|
||||
|
||||
Returns:
|
||||
Tuple of (email, password)
|
||||
|
||||
Raises:
|
||||
ValueError: If required credentials are not found
|
||||
"""
|
||||
global _deprecation_warned
|
||||
|
||||
email = os.getenv("GARMIN_EMAIL")
|
||||
password = os.getenv("GARMIN_PASSWORD")
|
||||
|
||||
if email and password:
|
||||
return email, password
|
||||
|
||||
# Fallback to GARMIN_USERNAME
|
||||
username = os.getenv("GARMIN_USERNAME")
|
||||
if username and password:
|
||||
if not _deprecation_warned:
|
||||
logger.warning(
|
||||
"GARMIN_USERNAME is deprecated. Please use GARMIN_EMAIL instead. "
|
||||
"GARMIN_USERNAME will be removed in a future version."
|
||||
)
|
||||
_deprecation_warned = True
|
||||
return username, password
|
||||
|
||||
raise ValueError(
|
||||
"Garmin credentials not found. Set GARMIN_EMAIL and GARMIN_PASSWORD "
|
||||
"environment variables."
|
||||
)
|
||||
|
||||
# Bike specifications
|
||||
class BikeConfig:
|
||||
"""Bike configuration constants."""
|
||||
|
||||
# Valid gear configurations
|
||||
VALID_CONFIGURATIONS: Dict[int, list] = {
|
||||
38: [14, 16, 18, 20],
|
||||
46: [16]
|
||||
}
|
||||
|
||||
# Default bike specifications
|
||||
DEFAULT_CHAINRING_TEETH = 38
|
||||
BIKE_WEIGHT_LBS = 22
|
||||
BIKE_WEIGHT_KG = BIKE_WEIGHT_LBS * 0.453592
|
||||
|
||||
# Wheel specifications (700x25c)
|
||||
WHEEL_CIRCUMFERENCE_MM = 2111 # 700x25c wheel circumference
|
||||
WHEEL_CIRCUMFERENCE_M = WHEEL_CIRCUMFERENCE_MM / 1000
|
||||
TIRE_CIRCUMFERENCE_M = WHEEL_CIRCUMFERENCE_M # Alias for gear estimation
|
||||
|
||||
# Physics-based power estimation constants
|
||||
BIKE_MASS_KG = 75.0 # Total bike + rider mass in kg
|
||||
BIKE_CRR = 0.004 # Rolling resistance coefficient
|
||||
BIKE_CDA = 0.3 # Aerodynamic drag coefficient * frontal area (m²)
|
||||
AIR_DENSITY = 1.225 # Air density in kg/m³
|
||||
DRIVE_EFFICIENCY = 0.97 # Drive train efficiency
|
||||
|
||||
# Analysis toggles and caps
|
||||
INDOOR_AERO_DISABLED = True # Disable aerodynamic term for indoor workouts
|
||||
INDOOR_BASELINE_WATTS = 10.0 # Baseline power for indoor when stationary
|
||||
POWER_ESTIMATE_SMOOTHING_WINDOW_SAMPLES = 3 # Smoothing window for power estimates
|
||||
MAX_POWER_WATTS = 1500 # Maximum allowed power estimate to cap spikes
|
||||
|
||||
# Legacy constants (kept for compatibility)
|
||||
AERO_CDA_BASE = 0.324 # Base aerodynamic drag coefficient * frontal area (m²)
|
||||
ROLLING_RESISTANCE_BASE = 0.0063 # Base rolling resistance coefficient
|
||||
EFFICIENCY = 0.97 # Drive train efficiency
|
||||
MECHANICAL_LOSS_COEFF = 5.0 # Mechanical losses in watts
|
||||
INDOOR_BASE_RESISTANCE = 0.02 # Base grade equivalent for indoor bikes
|
||||
INDOOR_CADENCE_THRESHOLD = 80 # RPM threshold for increased indoor resistance
|
||||
|
||||
# Gear ratios
|
||||
GEAR_RATIOS = {
|
||||
38: {
|
||||
14: 38/14,
|
||||
16: 38/16,
|
||||
18: 38/18,
|
||||
20: 38/20
|
||||
},
|
||||
46: {
|
||||
16: 46/16
|
||||
}
|
||||
}
|
||||
|
||||
# Indoor activity detection
|
||||
INDOOR_KEYWORDS = [
|
||||
'indoor_cycling', 'indoor cycling', 'indoor bike',
|
||||
'trainer', 'zwift', 'virtual'
|
||||
]
|
||||
|
||||
# File type detection
|
||||
SUPPORTED_FORMATS = ['.fit', '.tcx', '.gpx']
|
||||
|
||||
# Logging configuration
|
||||
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
|
||||
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
|
||||
# Report generation
|
||||
REPORT_TEMPLATE_DIR = BASE_DIR / "reports" / "templates"
|
||||
DEFAULT_REPORT_FORMAT = "markdown"
|
||||
CHART_DPI = 300
|
||||
CHART_FORMAT = "png"
|
||||
|
||||
# Data processing
|
||||
SMOOTHING_WINDOW = 10 # meters for gradient smoothing
|
||||
MIN_WORKOUT_DURATION = 300 # seconds (5 minutes)
|
||||
MAX_POWER_ESTIMATE = 1000 # watts
|
||||
|
||||
# User-specific settings (can be overridden via CLI or environment)
|
||||
FTP = int(os.getenv("FTP", "250")) # Functional Threshold Power in watts
|
||||
MAX_HEART_RATE = int(os.getenv("MAX_HEART_RATE", "185")) # Maximum heart rate in bpm
|
||||
COG_SIZE = int(os.getenv("COG_SIZE", str(BikeConfig.DEFAULT_CHAINRING_TEETH))) # Chainring teeth
|
||||
|
||||
# Zones configuration
|
||||
ZONES_FILE = BASE_DIR / "config" / "zones.json"
|
||||
20
examples/Garmin_Analyser/config/zones.json
Normal file
20
examples/Garmin_Analyser/config/zones.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"power": {
|
||||
"zone1": {"min": 0, "max": 55, "label": "Active Recovery"},
|
||||
"zone2": {"min": 56, "max": 75, "label": "Endurance"},
|
||||
"zone3": {"min": 76, "max": 90, "label": "Tempo"},
|
||||
"zone4": {"min": 91, "max": 105, "label": "Lactate Threshold"},
|
||||
"zone5": {"min": 106, "max": 120, "label": "VO2 Max"},
|
||||
"zone6": {"min": 121, "max": 150, "label": "Anaerobic Capacity"},
|
||||
"zone7": {"min": 151, "max": 999, "label": "Neuromuscular Power"}
|
||||
},
|
||||
"heart_rate": {
|
||||
"zone1": {"min": 0, "max": 60, "label": "Active Recovery"},
|
||||
"zone2": {"min": 61, "max": 70, "label": "Endurance"},
|
||||
"zone3": {"min": 71, "max": 80, "label": "Tempo"},
|
||||
"zone4": {"min": 81, "max": 90, "label": "Lactate Threshold"},
|
||||
"zone5": {"min": 91, "max": 100, "label": "VO2 Max"},
|
||||
"zone6": {"min": 101, "max": 110, "label": "Anaerobic Capacity"},
|
||||
"zone7": {"min": 111, "max": 999, "label": "Neuromuscular Power"}
|
||||
}
|
||||
}
|
||||
66
examples/Garmin_Analyser/config/zones.yaml
Normal file
66
examples/Garmin_Analyser/config/zones.yaml
Normal file
@@ -0,0 +1,66 @@
|
||||
# Custom zones configuration example
|
||||
# This file can be used to override the default zones in config.yaml
|
||||
|
||||
# Functional Threshold Power (W)
|
||||
ftp: 275
|
||||
|
||||
# Maximum heart rate (bpm)
|
||||
max_heart_rate: 190
|
||||
|
||||
# Power zones as percentage of FTP
|
||||
power_zones:
|
||||
- name: Recovery
|
||||
min: 0
|
||||
max: 50
|
||||
percentage: true
|
||||
- name: Endurance
|
||||
min: 51
|
||||
max: 70
|
||||
percentage: true
|
||||
- name: Tempo
|
||||
min: 71
|
||||
max: 85
|
||||
percentage: true
|
||||
- name: Sweet Spot
|
||||
min: 84
|
||||
max: 97
|
||||
percentage: true
|
||||
- name: Threshold
|
||||
min: 96
|
||||
max: 105
|
||||
percentage: true
|
||||
- name: VO2 Max
|
||||
min: 106
|
||||
max: 120
|
||||
percentage: true
|
||||
- name: Anaerobic
|
||||
min: 121
|
||||
max: 150
|
||||
percentage: true
|
||||
|
||||
# Heart rate zones as percentage of max HR
|
||||
heart_rate_zones:
|
||||
- name: Zone 1 - Recovery
|
||||
min: 0
|
||||
max: 60
|
||||
percentage: true
|
||||
- name: Zone 2 - Endurance
|
||||
min: 60
|
||||
max: 70
|
||||
percentage: true
|
||||
- name: Zone 3 - Tempo
|
||||
min: 70
|
||||
max: 80
|
||||
percentage: true
|
||||
- name: Zone 4 - Threshold
|
||||
min: 80
|
||||
max: 90
|
||||
percentage: true
|
||||
- name: Zone 5 - VO2 Max
|
||||
min: 90
|
||||
max: 95
|
||||
percentage: true
|
||||
- name: Zone 6 - Neuromuscular
|
||||
min: 95
|
||||
max: 100
|
||||
percentage: true
|
||||
Reference in New Issue
Block a user