Added consideration of race position and gap to ahead cars
This commit is contained in:
@@ -1,52 +1,52 @@
|
||||
lap_number,total_laps,lap_time,average_speed,max_speed,tire_compound,tire_life_laps,track_temperature,rainfall
|
||||
1,51,0 days 00:01:33.340000,210.17,326.0,MEDIUM,1,42.5,False
|
||||
2,51,0 days 00:01:28.012000,236.87,330.0,MEDIUM,2,42.5,False
|
||||
3,51,0 days 00:01:27.546000,236.4,331.0,MEDIUM,3,43.2,False
|
||||
4,51,0 days 00:01:27.221000,240.13,341.0,MEDIUM,4,43.2,False
|
||||
5,51,0 days 00:01:27.033000,236.09,345.0,MEDIUM,5,43.1,False
|
||||
6,51,0 days 00:01:27.175000,236.74,343.0,MEDIUM,6,43.3,False
|
||||
7,51,0 days 00:01:26.929000,239.72,340.0,MEDIUM,7,43.6,False
|
||||
8,51,0 days 00:01:26.943000,238.45,351.0,MEDIUM,8,43.6,False
|
||||
9,51,0 days 00:01:27.383000,236.81,330.0,MEDIUM,9,43.6,False
|
||||
10,51,0 days 00:01:27.368000,232.42,331.0,MEDIUM,10,43.9,False
|
||||
11,51,0 days 00:01:27.343000,237.18,331.0,MEDIUM,11,43.9,False
|
||||
12,51,0 days 00:01:27.339000,236.76,332.0,MEDIUM,12,43.3,False
|
||||
13,51,0 days 00:01:27.158000,235.47,330.0,MEDIUM,13,43.1,False
|
||||
14,51,0 days 00:01:27.607000,235.97,332.0,MEDIUM,14,43.7,False
|
||||
15,51,0 days 00:01:26.929000,237.04,332.0,MEDIUM,15,43.7,False
|
||||
16,51,0 days 00:01:27.164000,238.88,333.0,MEDIUM,16,43.9,False
|
||||
17,51,0 days 00:01:27.156000,235.35,333.0,MEDIUM,17,43.7,False
|
||||
18,51,0 days 00:01:27.275000,238.66,333.0,MEDIUM,18,43.5,False
|
||||
19,51,0 days 00:01:27.318000,234.62,333.0,MEDIUM,19,43.6,False
|
||||
20,51,0 days 00:01:28.284000,235.31,333.0,MEDIUM,20,44.3,False
|
||||
21,51,0 days 00:01:32.377000,224.11,332.0,MEDIUM,21,44.3,False
|
||||
22,51,0 days 00:01:47.272000,191.14,322.0,HARD,4,43.7,False
|
||||
23,51,0 days 00:01:26.849000,236.5,329.0,HARD,5,43.7,False
|
||||
24,51,0 days 00:01:26.949000,238.49,331.0,HARD,6,43.5,False
|
||||
25,51,0 days 00:01:26.899000,237.13,331.0,HARD,7,43.5,False
|
||||
26,51,0 days 00:01:26.792000,238.18,331.0,HARD,8,43.4,False
|
||||
27,51,0 days 00:01:26.666000,238.7,331.0,HARD,9,43.4,False
|
||||
28,51,0 days 00:01:26.723000,234.46,331.0,HARD,10,43.3,False
|
||||
29,51,0 days 00:01:26.997000,236.12,332.0,HARD,11,42.9,False
|
||||
30,51,0 days 00:01:26.915000,238.87,333.0,HARD,12,42.5,False
|
||||
31,51,0 days 00:01:26.848000,240.21,335.0,HARD,13,42.8,False
|
||||
32,51,0 days 00:01:26.747000,235.57,332.0,HARD,14,42.6,False
|
||||
33,51,0 days 00:01:26.769000,238.33,332.0,HARD,15,42.4,False
|
||||
34,51,0 days 00:01:26.563000,236.92,332.0,HARD,16,42.5,False
|
||||
35,51,0 days 00:01:26.517000,239.63,333.0,HARD,17,42.6,False
|
||||
36,51,0 days 00:01:26.457000,237.1,332.0,HARD,18,42.5,False
|
||||
37,51,0 days 00:01:26.380000,238.39,333.0,HARD,19,42.5,False
|
||||
38,51,0 days 00:01:26.574000,237.75,333.0,HARD,20,42.8,False
|
||||
39,51,0 days 00:01:26.707000,238.65,332.0,HARD,21,42.8,False
|
||||
40,51,0 days 00:01:26.610000,235.8,333.0,HARD,22,42.8,False
|
||||
41,51,0 days 00:01:26.815000,236.82,334.0,HARD,23,42.9,False
|
||||
42,51,0 days 00:01:26.403000,239.47,332.0,HARD,24,42.5,False
|
||||
43,51,0 days 00:01:26.105000,240.15,334.0,HARD,25,42.5,False
|
||||
44,51,0 days 00:01:26.155000,241.7,332.0,HARD,26,41.7,False
|
||||
45,51,0 days 00:01:26.277000,241.25,333.0,HARD,27,41.7,False
|
||||
46,51,0 days 00:01:26.453000,238.02,335.0,HARD,28,41.8,False
|
||||
47,51,0 days 00:01:26.912000,236.31,336.0,HARD,29,41.2,False
|
||||
48,51,0 days 00:01:27.183000,237.42,340.0,HARD,30,41.2,False
|
||||
49,51,0 days 00:01:27.245000,238.99,335.0,HARD,31,41.0,False
|
||||
50,51,0 days 00:01:27.360000,237.3,342.0,HARD,32,40.8,False
|
||||
51,51,0 days 00:01:27.395000,237.13,345.0,HARD,33,40.8,False
|
||||
lap_number,total_laps,position,gap_to_leader,gap_to_ahead,lap_time,average_speed,max_speed,tire_compound,tire_life_laps,track_temperature,rainfall
|
||||
1,51,11,6.223,0.62,0 days 00:01:33.340000,210.17,326.0,MEDIUM,1,42.5,False
|
||||
2,51,11,8.229,0.712,0 days 00:01:28.012000,236.87,330.0,MEDIUM,2,42.5,False
|
||||
3,51,11,9.799,0.898,0 days 00:01:27.546000,236.4,331.0,MEDIUM,3,43.2,False
|
||||
4,51,11,10.953,0.919,0 days 00:01:27.221000,240.13,341.0,MEDIUM,4,43.2,False
|
||||
5,51,11,11.563,0.917,0 days 00:01:27.033000,236.09,345.0,MEDIUM,5,43.1,False
|
||||
6,51,11,12.123,0.923,0 days 00:01:27.175000,236.74,343.0,MEDIUM,6,43.3,False
|
||||
7,51,11,12.694,0.387,0 days 00:01:26.929000,239.72,340.0,MEDIUM,7,43.6,False
|
||||
8,51,10,13.413,2.76,0 days 00:01:26.943000,238.45,351.0,MEDIUM,8,43.6,False
|
||||
9,51,10,14.32,3.387,0 days 00:01:27.383000,236.81,330.0,MEDIUM,9,43.6,False
|
||||
10,51,10,15.177,3.76,0 days 00:01:27.368000,232.42,331.0,MEDIUM,10,43.9,False
|
||||
11,51,10,15.829,3.984,0 days 00:01:27.343000,237.18,331.0,MEDIUM,11,43.9,False
|
||||
12,51,10,16.76,4.129,0 days 00:01:27.339000,236.76,332.0,MEDIUM,12,43.3,False
|
||||
13,51,10,17.031,3.995,0 days 00:01:27.158000,235.47,330.0,MEDIUM,13,43.1,False
|
||||
14,51,10,17.666,4.076,0 days 00:01:27.607000,235.97,332.0,MEDIUM,14,43.7,False
|
||||
15,51,10,17.366,0.469,0 days 00:01:26.929000,237.04,332.0,MEDIUM,15,43.7,False
|
||||
16,51,9,17.812,2.844,0 days 00:01:27.164000,238.88,333.0,MEDIUM,16,43.9,False
|
||||
17,51,9,18.235,2.415,0 days 00:01:27.156000,235.35,333.0,MEDIUM,17,43.7,False
|
||||
18,51,9,19.237,2.411,0 days 00:01:27.275000,238.66,333.0,MEDIUM,18,43.5,False
|
||||
19,51,9,20.117,2.28,0 days 00:01:27.318000,234.62,333.0,MEDIUM,19,43.6,False
|
||||
20,51,7,17.552,2.734,0 days 00:01:28.284000,235.31,333.0,MEDIUM,20,44.3,False
|
||||
21,51,6,16.826,4.269,0 days 00:01:32.377000,224.11,332.0,MEDIUM,21,44.3,False
|
||||
22,51,12,29.113,8.135,0 days 00:01:47.272000,191.14,322.0,HARD,4,43.7,False
|
||||
23,51,12,27.412,4.623,0 days 00:01:26.849000,236.5,329.0,HARD,5,43.7,False
|
||||
24,51,11,27.05,4.191,0 days 00:01:26.949000,238.49,331.0,HARD,6,43.5,False
|
||||
25,51,11,27.953,4.679,0 days 00:01:26.899000,237.13,331.0,HARD,7,43.5,False
|
||||
26,51,10,28.959,5.184,0 days 00:01:26.792000,238.18,331.0,HARD,8,43.4,False
|
||||
27,51,10,30.124,5.602,0 days 00:01:26.666000,238.7,331.0,HARD,9,43.4,False
|
||||
28,51,9,31.413,5.714,0 days 00:01:26.723000,234.46,331.0,HARD,10,43.3,False
|
||||
29,51,10,32.939,1.141,0 days 00:01:26.997000,236.12,332.0,HARD,11,42.9,False
|
||||
30,51,10,34.456,1.716,0 days 00:01:26.915000,238.87,333.0,HARD,12,42.5,False
|
||||
31,51,10,35.802,2.16,0 days 00:01:26.848000,240.21,335.0,HARD,13,42.8,False
|
||||
32,51,10,37.139,2.287,0 days 00:01:26.747000,235.57,332.0,HARD,14,42.6,False
|
||||
33,51,10,38.668,2.472,0 days 00:01:26.769000,238.33,332.0,HARD,15,42.4,False
|
||||
34,51,10,39.548,2.799,0 days 00:01:26.563000,236.92,332.0,HARD,16,42.5,False
|
||||
35,51,10,40.25,3.3,0 days 00:01:26.517000,239.63,333.0,HARD,17,42.6,False
|
||||
36,51,10,41.109,3.552,0 days 00:01:26.457000,237.1,332.0,HARD,18,42.5,False
|
||||
37,51,10,41.806,3.933,0 days 00:01:26.380000,238.39,333.0,HARD,19,42.5,False
|
||||
38,51,10,42.795,4.233,0 days 00:01:26.574000,237.75,333.0,HARD,20,42.8,False
|
||||
39,51,10,44.016,4.691,0 days 00:01:26.707000,238.65,332.0,HARD,21,42.8,False
|
||||
40,51,10,44.725,4.643,0 days 00:01:26.610000,235.8,333.0,HARD,22,42.8,False
|
||||
41,51,9,45.761,3.957,0 days 00:01:26.815000,236.82,334.0,HARD,23,42.9,False
|
||||
42,51,9,46.252,4.144,0 days 00:01:26.403000,239.47,332.0,HARD,24,42.5,False
|
||||
43,51,9,46.363,4.156,0 days 00:01:26.105000,240.15,334.0,HARD,25,42.5,False
|
||||
44,51,9,46.767,3.159,0 days 00:01:26.155000,241.7,332.0,HARD,26,41.7,False
|
||||
45,51,9,47.128,2.193,0 days 00:01:26.277000,241.25,333.0,HARD,27,41.7,False
|
||||
46,51,9,47.396,1.629,0 days 00:01:26.453000,238.02,335.0,HARD,28,41.8,False
|
||||
47,51,9,48.171,0.655,0 days 00:01:26.912000,236.31,336.0,HARD,29,41.2,False
|
||||
48,51,9,48.119,1.105,0 days 00:01:27.183000,237.42,340.0,HARD,30,41.2,False
|
||||
49,51,9,47.457,0.689,0 days 00:01:27.245000,238.99,335.0,HARD,31,41.0,False
|
||||
50,51,9,46.424,0.875,0 days 00:01:27.360000,237.3,342.0,HARD,32,40.8,False
|
||||
51,51,9,46.294,0.845,0 days 00:01:27.395000,237.13,345.0,HARD,33,40.8,False
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,182 +0,0 @@
|
||||
"""
|
||||
Raspberry Pi Telemetry Stream Simulator - Lap-Level Data
|
||||
|
||||
Reads the ALONSO_2023_MONZA_LAPS.csv file lap by lap and simulates
|
||||
live telemetry streaming from a Raspberry Pi sensor.
|
||||
Sends data to the HPC simulation layer via HTTP POST at fixed
|
||||
1-minute intervals between laps.
|
||||
|
||||
Usage:
|
||||
python simulate_pi_stream.py
|
||||
python simulate_pi_stream.py --interval 30 # 30 seconds between laps
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import time
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any
|
||||
import pandas as pd
|
||||
import requests
|
||||
|
||||
|
||||
def load_lap_csv(filepath: Path) -> pd.DataFrame:
|
||||
"""Load lap-level telemetry data from CSV file."""
|
||||
df = pd.read_csv(filepath)
|
||||
|
||||
# Convert lap_time to timedelta if it's not already
|
||||
if 'lap_time' in df.columns and df['lap_time'].dtype == 'object':
|
||||
df['lap_time'] = pd.to_timedelta(df['lap_time'])
|
||||
|
||||
print(f"✓ Loaded {len(df)} laps from {filepath}")
|
||||
print(f" Laps: {df['lap_number'].min():.0f} → {df['lap_number'].max():.0f}")
|
||||
|
||||
return df
|
||||
|
||||
|
||||
def lap_to_json(row: pd.Series) -> Dict[str, Any]:
|
||||
"""Convert a lap DataFrame row to a JSON-compatible dictionary."""
|
||||
data = {
|
||||
'lap_number': int(row['lap_number']) if pd.notna(row['lap_number']) else None,
|
||||
'total_laps': int(row['total_laps']) if pd.notna(row['total_laps']) else None,
|
||||
'lap_time': str(row['lap_time']) if pd.notna(row['lap_time']) else None,
|
||||
'average_speed': float(row['average_speed']) if pd.notna(row['average_speed']) else 0.0,
|
||||
'max_speed': float(row['max_speed']) if pd.notna(row['max_speed']) else 0.0,
|
||||
'tire_compound': str(row['tire_compound']) if pd.notna(row['tire_compound']) else 'UNKNOWN',
|
||||
'tire_life_laps': int(row['tire_life_laps']) if pd.notna(row['tire_life_laps']) else 0,
|
||||
'track_temperature': float(row['track_temperature']) if pd.notna(row['track_temperature']) else 0.0,
|
||||
'rainfall': bool(row['rainfall'])
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
def simulate_stream(
|
||||
df: pd.DataFrame,
|
||||
endpoint: str,
|
||||
interval: int = 60,
|
||||
start_lap: int = 1,
|
||||
end_lap: int = None
|
||||
):
|
||||
"""
|
||||
Simulate live telemetry streaming with fixed interval between laps.
|
||||
|
||||
Args:
|
||||
df: DataFrame with lap-level telemetry data
|
||||
endpoint: HPC API endpoint URL
|
||||
interval: Fixed interval in seconds between laps (default: 60 seconds)
|
||||
start_lap: Starting lap number
|
||||
end_lap: Ending lap number (None = all laps)
|
||||
"""
|
||||
# Filter by lap range
|
||||
filtered_df = df[df['lap_number'] >= start_lap].copy()
|
||||
if end_lap:
|
||||
filtered_df = filtered_df[filtered_df['lap_number'] <= end_lap].copy()
|
||||
|
||||
if len(filtered_df) == 0:
|
||||
print("❌ No laps in specified lap range")
|
||||
return
|
||||
|
||||
# Reset index for easier iteration
|
||||
filtered_df = filtered_df.reset_index(drop=True)
|
||||
|
||||
print(f"\n🏁 Starting lap-level telemetry stream simulation")
|
||||
print(f" Endpoint: {endpoint}")
|
||||
print(f" Laps: {start_lap} → {end_lap or 'end'}")
|
||||
print(f" Interval: {interval} seconds between laps")
|
||||
print(f" Total laps: {len(filtered_df)}")
|
||||
print(f" Est. duration: {len(filtered_df) * interval / 60:.1f} minutes\n")
|
||||
|
||||
sent_count = 0
|
||||
error_count = 0
|
||||
|
||||
try:
|
||||
for i in range(len(filtered_df)):
|
||||
row = filtered_df.iloc[i]
|
||||
lap_num = int(row['lap_number'])
|
||||
|
||||
# Convert lap to JSON
|
||||
lap_data = lap_to_json(row)
|
||||
|
||||
# Send lap data
|
||||
try:
|
||||
response = requests.post(
|
||||
endpoint,
|
||||
json=lap_data,
|
||||
timeout=5.0
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
sent_count += 1
|
||||
progress = (i + 1) / len(filtered_df) * 100
|
||||
|
||||
# Print lap info
|
||||
print(f" 📡 Lap {lap_num}/{int(row['total_laps'])}: "
|
||||
f"Avg Speed: {row['average_speed']:.1f} km/h, "
|
||||
f"Tire: {row['tire_compound']} (age: {int(row['tire_life_laps'])} laps) "
|
||||
f"[{progress:.0f}%]")
|
||||
|
||||
# Show response if it contains strategies
|
||||
try:
|
||||
response_data = response.json()
|
||||
if 'strategies_generated' in response_data:
|
||||
print(f" ✓ Generated {response_data['strategies_generated']} strategies")
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
error_count += 1
|
||||
print(f" ⚠ Lap {lap_num}: HTTP {response.status_code}: {response.text[:100]}")
|
||||
|
||||
except requests.RequestException as e:
|
||||
error_count += 1
|
||||
print(f" ⚠ Lap {lap_num}: Connection error: {str(e)[:100]}")
|
||||
|
||||
# Sleep for fixed interval before next lap (except for last lap)
|
||||
if i < len(filtered_df) - 1:
|
||||
time.sleep(interval)
|
||||
|
||||
print(f"\n✅ Stream complete!")
|
||||
print(f" Sent: {sent_count} laps")
|
||||
print(f" Errors: {error_count}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n⏸ Stream interrupted by user")
|
||||
print(f" Sent: {sent_count}/{len(filtered_df)} laps")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Simulate Raspberry Pi lap-level telemetry streaming"
|
||||
)
|
||||
parser.add_argument("--endpoint", type=str, default="http://localhost:8000/ingest/telemetry",
|
||||
help="HPC API endpoint (default: http://localhost:8000/ingest/telemetry)")
|
||||
parser.add_argument("--interval", type=int, default=60,
|
||||
help="Fixed interval in seconds between laps (default: 60)")
|
||||
parser.add_argument("--start-lap", type=int, default=1, help="Starting lap number")
|
||||
parser.add_argument("--end-lap", type=int, default=None, help="Ending lap number")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
# Hardcoded CSV file location in the same folder as this script
|
||||
script_dir = Path(__file__).parent
|
||||
data_path = script_dir / "ALONSO_2023_MONZA_LAPS.csv"
|
||||
|
||||
df = load_lap_csv(data_path)
|
||||
simulate_stream(
|
||||
df,
|
||||
args.endpoint,
|
||||
args.interval,
|
||||
args.start_lap,
|
||||
args.end_lap
|
||||
)
|
||||
except FileNotFoundError:
|
||||
print(f"❌ File not found: {data_path}")
|
||||
print(f" Make sure ALONSO_2023_MONZA_LAPS.csv is in the scripts/ folder")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -66,6 +66,9 @@ class PiSimulator:
|
||||
return {
|
||||
"lap_number": int(row["lap_number"]),
|
||||
"total_laps": int(row["total_laps"]),
|
||||
"position": int(row["position"]) if pd.notna(row["position"]) else 10,
|
||||
"gap_to_leader": float(row["gap_to_leader"]) if pd.notna(row["gap_to_leader"]) else 0.0,
|
||||
"gap_to_ahead": float(row["gap_to_ahead"]) if pd.notna(row["gap_to_ahead"]) else 0.0,
|
||||
"lap_time": str(row["lap_time"]),
|
||||
"average_speed": float(row["average_speed"]),
|
||||
"max_speed": float(row["max_speed"]),
|
||||
|
||||
Reference in New Issue
Block a user