redesign of simulated data functionality
This commit is contained in:
16585
ALONSO_2023_MONZA_RACE
Normal file
16585
ALONSO_2023_MONZA_RACE
Normal file
File diff suppressed because it is too large
Load Diff
649
fetch_data.ipynb
Normal file
649
fetch_data.ipynb
Normal file
@@ -0,0 +1,649 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 40,
|
||||||
|
"id": "9a9714f8",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stderr",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"core INFO \tLoading data for Italian Grand Prix - Race [v3.6.1]\n",
|
||||||
|
"req INFO \tUsing cached data for session_info\n",
|
||||||
|
"req INFO \tUsing cached data for driver_info\n",
|
||||||
|
"req INFO \tUsing cached data for session_info\n",
|
||||||
|
"req INFO \tUsing cached data for driver_info\n",
|
||||||
|
"req INFO \tUsing cached data for session_status_data\n",
|
||||||
|
"req INFO \tUsing cached data for lap_count\n",
|
||||||
|
"req INFO \tUsing cached data for track_status_data\n",
|
||||||
|
"req INFO \tUsing cached data for session_status_data\n",
|
||||||
|
"req INFO \tUsing cached data for lap_count\n",
|
||||||
|
"req INFO \tUsing cached data for track_status_data\n",
|
||||||
|
"req INFO \tUsing cached data for _extended_timing_data\n",
|
||||||
|
"req INFO \tUsing cached data for timing_app_data\n",
|
||||||
|
"core INFO \tProcessing timing data...\n",
|
||||||
|
"req INFO \tUsing cached data for _extended_timing_data\n",
|
||||||
|
"req INFO \tUsing cached data for timing_app_data\n",
|
||||||
|
"core INFO \tProcessing timing data...\n",
|
||||||
|
"req INFO \tUsing cached data for car_data\n",
|
||||||
|
"req INFO \tUsing cached data for car_data\n",
|
||||||
|
"req INFO \tUsing cached data for position_data\n",
|
||||||
|
"req INFO \tUsing cached data for position_data\n",
|
||||||
|
"req INFO \tUsing cached data for weather_data\n",
|
||||||
|
"req INFO \tUsing cached data for race_control_messages\n",
|
||||||
|
"core WARNING \tDriver 1 completed the race distance 06:25.888000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 11 completed the race distance 06:19.824000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 55 completed the race distance 06:14.695000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 16 completed the race distance 06:14.511000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 63 completed the race distance 06:07.860000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 44 completed the race distance 05:48.209000 before the recorded end of the session.\n",
|
||||||
|
"req INFO \tUsing cached data for weather_data\n",
|
||||||
|
"req INFO \tUsing cached data for race_control_messages\n",
|
||||||
|
"core WARNING \tDriver 1 completed the race distance 06:25.888000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 11 completed the race distance 06:19.824000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 55 completed the race distance 06:14.695000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 16 completed the race distance 06:14.511000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 63 completed the race distance 06:07.860000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 44 completed the race distance 05:48.209000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 23 completed the race distance 05:40.782000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 4 completed the race distance 05:40.439000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 14 completed the race distance 05:39.594000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 23 completed the race distance 05:40.782000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 4 completed the race distance 05:40.439000 before the recorded end of the session.\n",
|
||||||
|
"core WARNING \tDriver 14 completed the race distance 05:39.594000 before the recorded end of the session.\n",
|
||||||
|
"core INFO \tFinished loading data for 20 drivers: ['1', '11', '55', '16', '63', '44', '23', '4', '14', '77', '40', '81', '2', '24', '10', '18', '27', '20', '31', '22']\n",
|
||||||
|
"core INFO \tFinished loading data for 20 drivers: ['1', '11', '55', '16', '63', '44', '23', '4', '14', '77', '40', '81', '2', '24', '10', '18', '27', '20', '31', '22']\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Created dataframe with 16584 rows\n",
|
||||||
|
"Total laps in race: 51.0\n",
|
||||||
|
"Laps covered: 1.0 to 51.0\n",
|
||||||
|
"Tire life range: 1.0 to 33.0 laps\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/html": [
|
||||||
|
"<div>\n",
|
||||||
|
"<style scoped>\n",
|
||||||
|
" .dataframe tbody tr th:only-of-type {\n",
|
||||||
|
" vertical-align: middle;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe tbody tr th {\n",
|
||||||
|
" vertical-align: top;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe thead th {\n",
|
||||||
|
" text-align: right;\n",
|
||||||
|
" }\n",
|
||||||
|
"</style>\n",
|
||||||
|
"<table border=\"1\" class=\"dataframe\">\n",
|
||||||
|
" <thead>\n",
|
||||||
|
" <tr style=\"text-align: right;\">\n",
|
||||||
|
" <th></th>\n",
|
||||||
|
" <th>lap_number</th>\n",
|
||||||
|
" <th>total_laps</th>\n",
|
||||||
|
" <th>speed</th>\n",
|
||||||
|
" <th>overall_time</th>\n",
|
||||||
|
" <th>throttle</th>\n",
|
||||||
|
" <th>brake</th>\n",
|
||||||
|
" <th>tire_compound</th>\n",
|
||||||
|
" <th>tire_life_laps</th>\n",
|
||||||
|
" <th>track_temperature</th>\n",
|
||||||
|
" <th>rainfall</th>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </thead>\n",
|
||||||
|
" <tbody>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>0</th>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>0.0</td>\n",
|
||||||
|
" <td>0 days 01:22:21.734000</td>\n",
|
||||||
|
" <td>23.0</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" <td>MEDIUM</td>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>42.5</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>1</th>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>0.0</td>\n",
|
||||||
|
" <td>0 days 01:22:21.894000</td>\n",
|
||||||
|
" <td>23.0</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" <td>MEDIUM</td>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>42.5</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>2</th>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>4.0</td>\n",
|
||||||
|
" <td>0 days 01:22:22.214000</td>\n",
|
||||||
|
" <td>26.0</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" <td>MEDIUM</td>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>42.5</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>3</th>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>14.0</td>\n",
|
||||||
|
" <td>0 days 01:22:22.494000</td>\n",
|
||||||
|
" <td>24.0</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" <td>MEDIUM</td>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>42.5</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>4</th>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>24.0</td>\n",
|
||||||
|
" <td>0 days 01:22:22.774000</td>\n",
|
||||||
|
" <td>24.0</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" <td>MEDIUM</td>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>42.5</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>5</th>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>31.0</td>\n",
|
||||||
|
" <td>0 days 01:22:22.974000</td>\n",
|
||||||
|
" <td>26.0</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" <td>MEDIUM</td>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>42.5</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>6</th>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>38.0</td>\n",
|
||||||
|
" <td>0 days 01:22:23.254000</td>\n",
|
||||||
|
" <td>36.0</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" <td>MEDIUM</td>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>42.5</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>7</th>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>50.0</td>\n",
|
||||||
|
" <td>0 days 01:22:23.494000</td>\n",
|
||||||
|
" <td>41.0</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" <td>MEDIUM</td>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>42.5</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>8</th>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>58.0</td>\n",
|
||||||
|
" <td>0 days 01:22:23.694000</td>\n",
|
||||||
|
" <td>44.0</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" <td>MEDIUM</td>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>42.5</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>9</th>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>71.0</td>\n",
|
||||||
|
" <td>0 days 01:22:23.974000</td>\n",
|
||||||
|
" <td>55.0</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" <td>MEDIUM</td>\n",
|
||||||
|
" <td>1.0</td>\n",
|
||||||
|
" <td>42.5</td>\n",
|
||||||
|
" <td>False</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </tbody>\n",
|
||||||
|
"</table>\n",
|
||||||
|
"</div>"
|
||||||
|
],
|
||||||
|
"text/plain": [
|
||||||
|
" lap_number total_laps speed overall_time throttle brake \\\n",
|
||||||
|
"0 1.0 51.0 0.0 0 days 01:22:21.734000 23.0 False \n",
|
||||||
|
"1 1.0 51.0 0.0 0 days 01:22:21.894000 23.0 False \n",
|
||||||
|
"2 1.0 51.0 4.0 0 days 01:22:22.214000 26.0 False \n",
|
||||||
|
"3 1.0 51.0 14.0 0 days 01:22:22.494000 24.0 False \n",
|
||||||
|
"4 1.0 51.0 24.0 0 days 01:22:22.774000 24.0 False \n",
|
||||||
|
"5 1.0 51.0 31.0 0 days 01:22:22.974000 26.0 False \n",
|
||||||
|
"6 1.0 51.0 38.0 0 days 01:22:23.254000 36.0 False \n",
|
||||||
|
"7 1.0 51.0 50.0 0 days 01:22:23.494000 41.0 False \n",
|
||||||
|
"8 1.0 51.0 58.0 0 days 01:22:23.694000 44.0 False \n",
|
||||||
|
"9 1.0 51.0 71.0 0 days 01:22:23.974000 55.0 False \n",
|
||||||
|
"\n",
|
||||||
|
" tire_compound tire_life_laps track_temperature rainfall \n",
|
||||||
|
"0 MEDIUM 1.0 42.5 False \n",
|
||||||
|
"1 MEDIUM 1.0 42.5 False \n",
|
||||||
|
"2 MEDIUM 1.0 42.5 False \n",
|
||||||
|
"3 MEDIUM 1.0 42.5 False \n",
|
||||||
|
"4 MEDIUM 1.0 42.5 False \n",
|
||||||
|
"5 MEDIUM 1.0 42.5 False \n",
|
||||||
|
"6 MEDIUM 1.0 42.5 False \n",
|
||||||
|
"7 MEDIUM 1.0 42.5 False \n",
|
||||||
|
"8 MEDIUM 1.0 42.5 False \n",
|
||||||
|
"9 MEDIUM 1.0 42.5 False "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 40,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"\"\"\"\n",
|
||||||
|
"FastF1 Data Fetcher for HPC F1 AI Strategy System\n",
|
||||||
|
"\n",
|
||||||
|
"Downloads telemetry and race data from a specific F1 session to simulate\n",
|
||||||
|
"live telemetry streaming from a Raspberry Pi \"racecar\" to the HPC layer.\n",
|
||||||
|
"\n",
|
||||||
|
"Usage:\n",
|
||||||
|
" python fetch_race_data.py --year 2024 --race \"Monaco\" --driver VER --output data/\n",
|
||||||
|
"\"\"\"\n",
|
||||||
|
"import fastf1\n",
|
||||||
|
"import pandas as pd\n",
|
||||||
|
"\n",
|
||||||
|
"# 1. Load the session\n",
|
||||||
|
"session = fastf1.get_session(2023, 'Monza', 'R')\n",
|
||||||
|
"session.load(telemetry=True, laps=True, weather=True)\n",
|
||||||
|
"\n",
|
||||||
|
"# 2. Pick the driver\n",
|
||||||
|
"driver_laps = session.laps.pick_drivers('ALO')\n",
|
||||||
|
"\n",
|
||||||
|
"# Get total number of laps in the race (maximum lap number from all drivers)\n",
|
||||||
|
"total_laps = session.laps['LapNumber'].max()\n",
|
||||||
|
"\n",
|
||||||
|
"# 3. Collect all telemetry data with lap information\n",
|
||||||
|
"telemetry_list = []\n",
|
||||||
|
"\n",
|
||||||
|
"for lap_idx in driver_laps.index:\n",
|
||||||
|
" lap = driver_laps.loc[lap_idx]\n",
|
||||||
|
" lap_number = lap['LapNumber']\n",
|
||||||
|
" tire_compound = lap['Compound']\n",
|
||||||
|
" tire_life = lap['TyreLife'] # Number of laps on current tires\n",
|
||||||
|
" \n",
|
||||||
|
" # Get telemetry for this lap\n",
|
||||||
|
" car_data = lap.get_car_data()\n",
|
||||||
|
" \n",
|
||||||
|
" if car_data is not None and len(car_data) > 0:\n",
|
||||||
|
" # Add lap number, tire compound, and tire life to each telemetry point\n",
|
||||||
|
" car_data['LapNumber'] = lap_number\n",
|
||||||
|
" car_data['Compound'] = tire_compound\n",
|
||||||
|
" car_data['TyreLife'] = tire_life\n",
|
||||||
|
" telemetry_list.append(car_data)\n",
|
||||||
|
"\n",
|
||||||
|
"# 4. Combine all telemetry data\n",
|
||||||
|
"all_telemetry = pd.concat(telemetry_list, ignore_index=True)\n",
|
||||||
|
"\n",
|
||||||
|
"# 5. Get weather data\n",
|
||||||
|
"weather = session.weather_data\n",
|
||||||
|
"\n",
|
||||||
|
"# 6. Merge telemetry with weather based on timestamp\n",
|
||||||
|
"# First, ensure both have SessionTime column\n",
|
||||||
|
"all_telemetry['SessionTime'] = pd.to_timedelta(all_telemetry['SessionTime'])\n",
|
||||||
|
"weather['SessionTime'] = pd.to_timedelta(weather['Time'])\n",
|
||||||
|
"\n",
|
||||||
|
"# Merge using merge_asof for time-based joining\n",
|
||||||
|
"all_telemetry = all_telemetry.sort_values('SessionTime')\n",
|
||||||
|
"weather = weather.sort_values('SessionTime')\n",
|
||||||
|
"\n",
|
||||||
|
"merged_data = pd.merge_asof(\n",
|
||||||
|
" all_telemetry,\n",
|
||||||
|
" weather[['SessionTime', 'TrackTemp', 'Rainfall']],\n",
|
||||||
|
" on='SessionTime',\n",
|
||||||
|
" direction='nearest'\n",
|
||||||
|
")\n",
|
||||||
|
"\n",
|
||||||
|
"# 7. Create final dataframe with requested columns\n",
|
||||||
|
"final_df = pd.DataFrame({\n",
|
||||||
|
" 'lap_number': merged_data['LapNumber'],\n",
|
||||||
|
" 'total_laps': total_laps, # Total laps in the race\n",
|
||||||
|
" 'speed': merged_data['Speed'],\n",
|
||||||
|
" 'overall_time': merged_data['SessionTime'],\n",
|
||||||
|
" 'throttle': merged_data['Throttle'],\n",
|
||||||
|
" 'brake': merged_data['Brake'],\n",
|
||||||
|
" 'tire_compound': merged_data['Compound'],\n",
|
||||||
|
" 'tire_life_laps': merged_data['TyreLife'], # Number of laps on current tires\n",
|
||||||
|
" 'track_temperature': merged_data['TrackTemp'],\n",
|
||||||
|
" 'rainfall': merged_data['Rainfall']\n",
|
||||||
|
"})\n",
|
||||||
|
"\n",
|
||||||
|
"print(f\"Created dataframe with {len(final_df)} rows\")\n",
|
||||||
|
"print(f\"Total laps in race: {total_laps}\")\n",
|
||||||
|
"print(f\"Laps covered: {final_df['lap_number'].min()} to {final_df['lap_number'].max()}\")\n",
|
||||||
|
"print(f\"Tire life range: {final_df['tire_life_laps'].min()} to {final_df['tire_life_laps'].max()} laps\")\n",
|
||||||
|
"final_df.head(10)\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 41,
|
||||||
|
"id": "45d27f05",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Dataframe Info:\n",
|
||||||
|
"Total telemetry points: 16584\n",
|
||||||
|
"\n",
|
||||||
|
"Column types:\n",
|
||||||
|
"lap_number float64\n",
|
||||||
|
"total_laps float64\n",
|
||||||
|
"speed float64\n",
|
||||||
|
"overall_time timedelta64[ns]\n",
|
||||||
|
"throttle float64\n",
|
||||||
|
"brake bool\n",
|
||||||
|
"tire_compound object\n",
|
||||||
|
"tire_life_laps float64\n",
|
||||||
|
"track_temperature float64\n",
|
||||||
|
"rainfall bool\n",
|
||||||
|
"dtype: object\n",
|
||||||
|
"\n",
|
||||||
|
"Basic statistics:\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/html": [
|
||||||
|
"<div>\n",
|
||||||
|
"<style scoped>\n",
|
||||||
|
" .dataframe tbody tr th:only-of-type {\n",
|
||||||
|
" vertical-align: middle;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe tbody tr th {\n",
|
||||||
|
" vertical-align: top;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe thead th {\n",
|
||||||
|
" text-align: right;\n",
|
||||||
|
" }\n",
|
||||||
|
"</style>\n",
|
||||||
|
"<table border=\"1\" class=\"dataframe\">\n",
|
||||||
|
" <thead>\n",
|
||||||
|
" <tr style=\"text-align: right;\">\n",
|
||||||
|
" <th></th>\n",
|
||||||
|
" <th>lap_number</th>\n",
|
||||||
|
" <th>total_laps</th>\n",
|
||||||
|
" <th>speed</th>\n",
|
||||||
|
" <th>overall_time</th>\n",
|
||||||
|
" <th>throttle</th>\n",
|
||||||
|
" <th>tire_life_laps</th>\n",
|
||||||
|
" <th>track_temperature</th>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </thead>\n",
|
||||||
|
" <tbody>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>count</th>\n",
|
||||||
|
" <td>16584.000000</td>\n",
|
||||||
|
" <td>16584.0</td>\n",
|
||||||
|
" <td>16584.000000</td>\n",
|
||||||
|
" <td>16584</td>\n",
|
||||||
|
" <td>16584.000000</td>\n",
|
||||||
|
" <td>16584.000000</td>\n",
|
||||||
|
" <td>16584.000000</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>mean</th>\n",
|
||||||
|
" <td>25.891341</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>235.570188</td>\n",
|
||||||
|
" <td>0 days 01:59:34.577446394</td>\n",
|
||||||
|
" <td>72.291546</td>\n",
|
||||||
|
" <td>15.339243</td>\n",
|
||||||
|
" <td>42.908816</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>std</th>\n",
|
||||||
|
" <td>14.710977</td>\n",
|
||||||
|
" <td>0.0</td>\n",
|
||||||
|
" <td>76.948906</td>\n",
|
||||||
|
" <td>0 days 00:21:30.065940875</td>\n",
|
||||||
|
" <td>40.561237</td>\n",
|
||||||
|
" <td>8.558018</td>\n",
|
||||||
|
" <td>0.897756</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>min</th>\n",
|
||||||
|
" <td>1.000000</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>0.000000</td>\n",
|
||||||
|
" <td>0 days 01:22:21.734000</td>\n",
|
||||||
|
" <td>0.000000</td>\n",
|
||||||
|
" <td>1.000000</td>\n",
|
||||||
|
" <td>40.800000</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>25%</th>\n",
|
||||||
|
" <td>13.000000</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>180.000000</td>\n",
|
||||||
|
" <td>0 days 01:40:53.558000</td>\n",
|
||||||
|
" <td>40.000000</td>\n",
|
||||||
|
" <td>8.000000</td>\n",
|
||||||
|
" <td>42.500000</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>50%</th>\n",
|
||||||
|
" <td>26.000000</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>245.000000</td>\n",
|
||||||
|
" <td>0 days 01:59:31.222000</td>\n",
|
||||||
|
" <td>100.000000</td>\n",
|
||||||
|
" <td>15.000000</td>\n",
|
||||||
|
" <td>43.100000</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>75%</th>\n",
|
||||||
|
" <td>39.000000</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>309.000000</td>\n",
|
||||||
|
" <td>0 days 02:18:13.365000</td>\n",
|
||||||
|
" <td>100.000000</td>\n",
|
||||||
|
" <td>21.000000</td>\n",
|
||||||
|
" <td>43.600000</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>max</th>\n",
|
||||||
|
" <td>51.000000</td>\n",
|
||||||
|
" <td>51.0</td>\n",
|
||||||
|
" <td>351.000000</td>\n",
|
||||||
|
" <td>0 days 02:36:49.228000</td>\n",
|
||||||
|
" <td>100.000000</td>\n",
|
||||||
|
" <td>33.000000</td>\n",
|
||||||
|
" <td>44.400000</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </tbody>\n",
|
||||||
|
"</table>\n",
|
||||||
|
"</div>"
|
||||||
|
],
|
||||||
|
"text/plain": [
|
||||||
|
" lap_number total_laps speed overall_time \\\n",
|
||||||
|
"count 16584.000000 16584.0 16584.000000 16584 \n",
|
||||||
|
"mean 25.891341 51.0 235.570188 0 days 01:59:34.577446394 \n",
|
||||||
|
"std 14.710977 0.0 76.948906 0 days 00:21:30.065940875 \n",
|
||||||
|
"min 1.000000 51.0 0.000000 0 days 01:22:21.734000 \n",
|
||||||
|
"25% 13.000000 51.0 180.000000 0 days 01:40:53.558000 \n",
|
||||||
|
"50% 26.000000 51.0 245.000000 0 days 01:59:31.222000 \n",
|
||||||
|
"75% 39.000000 51.0 309.000000 0 days 02:18:13.365000 \n",
|
||||||
|
"max 51.000000 51.0 351.000000 0 days 02:36:49.228000 \n",
|
||||||
|
"\n",
|
||||||
|
" throttle tire_life_laps track_temperature \n",
|
||||||
|
"count 16584.000000 16584.000000 16584.000000 \n",
|
||||||
|
"mean 72.291546 15.339243 42.908816 \n",
|
||||||
|
"std 40.561237 8.558018 0.897756 \n",
|
||||||
|
"min 0.000000 1.000000 40.800000 \n",
|
||||||
|
"25% 40.000000 8.000000 42.500000 \n",
|
||||||
|
"50% 100.000000 15.000000 43.100000 \n",
|
||||||
|
"75% 100.000000 21.000000 43.600000 \n",
|
||||||
|
"max 100.000000 33.000000 44.400000 "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 41,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"# Display dataframe info and sample statistics\n",
|
||||||
|
"print(\"Dataframe Info:\")\n",
|
||||||
|
"print(f\"Total telemetry points: {len(final_df)}\")\n",
|
||||||
|
"print(f\"\\nColumn types:\")\n",
|
||||||
|
"print(final_df.dtypes)\n",
|
||||||
|
"print(f\"\\nBasic statistics:\")\n",
|
||||||
|
"final_df.describe()\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 42,
|
||||||
|
"id": "2fbcd2f9",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"final_df.to_csv(\"ALONSO_2023_MONZA_RACE\")"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 43,
|
||||||
|
"id": "729fb12e",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Tire compound usage throughout the race:\n",
|
||||||
|
" lap_number tire_compound tire_life_laps\n",
|
||||||
|
"0 1.0 MEDIUM 1.0\n",
|
||||||
|
"1 2.0 MEDIUM 2.0\n",
|
||||||
|
"2 3.0 MEDIUM 3.0\n",
|
||||||
|
"3 4.0 MEDIUM 4.0\n",
|
||||||
|
"4 5.0 MEDIUM 5.0\n",
|
||||||
|
"5 6.0 MEDIUM 6.0\n",
|
||||||
|
"6 7.0 MEDIUM 7.0\n",
|
||||||
|
"7 8.0 MEDIUM 8.0\n",
|
||||||
|
"8 9.0 MEDIUM 9.0\n",
|
||||||
|
"9 10.0 MEDIUM 10.0\n",
|
||||||
|
"10 11.0 MEDIUM 11.0\n",
|
||||||
|
"11 12.0 MEDIUM 12.0\n",
|
||||||
|
"12 13.0 MEDIUM 13.0\n",
|
||||||
|
"13 14.0 MEDIUM 14.0\n",
|
||||||
|
"14 15.0 MEDIUM 15.0\n",
|
||||||
|
"15 16.0 MEDIUM 16.0\n",
|
||||||
|
"16 17.0 MEDIUM 17.0\n",
|
||||||
|
"17 18.0 MEDIUM 18.0\n",
|
||||||
|
"18 19.0 MEDIUM 19.0\n",
|
||||||
|
"19 20.0 MEDIUM 20.0\n",
|
||||||
|
"20 21.0 MEDIUM 21.0\n",
|
||||||
|
"21 22.0 HARD 4.0\n",
|
||||||
|
"22 23.0 HARD 5.0\n",
|
||||||
|
"23 24.0 HARD 6.0\n",
|
||||||
|
"24 25.0 HARD 7.0\n",
|
||||||
|
"25 26.0 HARD 8.0\n",
|
||||||
|
"26 27.0 HARD 9.0\n",
|
||||||
|
"27 28.0 HARD 10.0\n",
|
||||||
|
"28 29.0 HARD 11.0\n",
|
||||||
|
"29 30.0 HARD 12.0\n",
|
||||||
|
"30 31.0 HARD 13.0\n",
|
||||||
|
"31 32.0 HARD 14.0\n",
|
||||||
|
"32 33.0 HARD 15.0\n",
|
||||||
|
"33 34.0 HARD 16.0\n",
|
||||||
|
"34 35.0 HARD 17.0\n",
|
||||||
|
"35 36.0 HARD 18.0\n",
|
||||||
|
"36 37.0 HARD 19.0\n",
|
||||||
|
"37 38.0 HARD 20.0\n",
|
||||||
|
"38 39.0 HARD 21.0\n",
|
||||||
|
"39 40.0 HARD 22.0\n",
|
||||||
|
"40 41.0 HARD 23.0\n",
|
||||||
|
"41 42.0 HARD 24.0\n",
|
||||||
|
"42 43.0 HARD 25.0\n",
|
||||||
|
"43 44.0 HARD 26.0\n",
|
||||||
|
"44 45.0 HARD 27.0\n",
|
||||||
|
"45 46.0 HARD 28.0\n",
|
||||||
|
"46 47.0 HARD 29.0\n",
|
||||||
|
"47 48.0 HARD 30.0\n",
|
||||||
|
"48 49.0 HARD 31.0\n",
|
||||||
|
"49 50.0 HARD 32.0\n",
|
||||||
|
"50 51.0 HARD 33.0\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"# Show tire compound changes and stint information\n",
|
||||||
|
"print(\"Tire compound usage throughout the race:\")\n",
|
||||||
|
"tire_changes = final_df.groupby(['lap_number', 'tire_compound', 'tire_life_laps']).size().reset_index(name='count')\n",
|
||||||
|
"tire_changes = tire_changes.groupby(['lap_number', 'tire_compound', 'tire_life_laps']).first().reset_index()[['lap_number', 'tire_compound', 'tire_life_laps']]\n",
|
||||||
|
"print(tire_changes.drop_duplicates())\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "d9ebc90c",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "base",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.13.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
@@ -1,96 +1,137 @@
|
|||||||
"""
|
"""
|
||||||
Raspberry Pi Telemetry Stream Simulator
|
Raspberry Pi Telemetry Stream Simulator
|
||||||
|
|
||||||
Replays downloaded FastF1 data as if it's coming from a live Raspberry Pi sensor.
|
Reads the ALONSO_2023_MONZA_RACE CSV file row by row and simulates
|
||||||
Sends data to the HPC simulation layer via HTTP POST.
|
live telemetry streaming from a Raspberry Pi sensor.
|
||||||
|
Sends data to the HPC simulation layer via HTTP POST at intervals
|
||||||
|
determined by the time differences between consecutive rows.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python simulate_pi_stream.py --data data/race_data/VER_telemetry.json --speed 1.0
|
python simulate_pi_stream.py --data ALONSO_2023_MONZA_RACE --speed 1.0
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Any
|
from typing import Dict, Any
|
||||||
|
import pandas as pd
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
def load_telemetry(filepath: Path) -> List[Dict[str, Any]]:
|
def load_telemetry_csv(filepath: Path) -> pd.DataFrame:
|
||||||
"""Load telemetry data from JSON file."""
|
"""Load telemetry data from CSV file."""
|
||||||
with open(filepath, 'r') as f:
|
df = pd.read_csv(filepath, index_col=0)
|
||||||
data = json.load(f)
|
|
||||||
print(f"✓ Loaded {len(data)} telemetry points from {filepath}")
|
# Convert overall_time to timedelta if it's not already
|
||||||
|
if df['overall_time'].dtype == 'object':
|
||||||
|
df['overall_time'] = pd.to_timedelta(df['overall_time'])
|
||||||
|
|
||||||
|
print(f"✓ Loaded {len(df)} telemetry points from {filepath}")
|
||||||
|
print(f" Laps: {df['lap_number'].min():.0f} → {df['lap_number'].max():.0f}")
|
||||||
|
print(f" Duration: {df['overall_time'].iloc[-1]}")
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
def row_to_json(row: pd.Series) -> Dict[str, Any]:
|
||||||
|
"""Convert a 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,
|
||||||
|
'speed': float(row['speed']) if pd.notna(row['speed']) else 0.0,
|
||||||
|
'throttle': float(row['throttle']) if pd.notna(row['throttle']) else 0.0,
|
||||||
|
'brake': bool(row['brake']),
|
||||||
|
'tire_compound': str(row['tire_compound']) if pd.notna(row['tire_compound']) else 'UNKNOWN',
|
||||||
|
'tire_life_laps': float(row['tire_life_laps']) if pd.notna(row['tire_life_laps']) else 0.0,
|
||||||
|
'track_temperature': float(row['track_temperature']) if pd.notna(row['track_temperature']) else 0.0,
|
||||||
|
'rainfall': bool(row['rainfall'])
|
||||||
|
}
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def simulate_stream(
|
def simulate_stream(
|
||||||
telemetry: List[Dict[str, Any]],
|
df: pd.DataFrame,
|
||||||
endpoint: str,
|
endpoint: str,
|
||||||
speed: float = 1.0,
|
speed: float = 1.0,
|
||||||
start_lap: int = 1,
|
start_lap: int = 1,
|
||||||
end_lap: int = None
|
end_lap: int = None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Simulate live telemetry streaming.
|
Simulate live telemetry streaming based on actual time intervals in the data.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
telemetry: List of telemetry points
|
df: DataFrame with telemetry data
|
||||||
endpoint: HPC API endpoint URL
|
endpoint: HPC API endpoint URL
|
||||||
speed: Playback speed multiplier (1.0 = real-time, 2.0 = 2x speed)
|
speed: Playback speed multiplier (1.0 = real-time, 2.0 = 2x speed)
|
||||||
start_lap: Starting lap number
|
start_lap: Starting lap number
|
||||||
end_lap: Ending lap number (None = all laps)
|
end_lap: Ending lap number (None = all laps)
|
||||||
"""
|
"""
|
||||||
# Filter by lap range
|
# Filter by lap range
|
||||||
filtered = [p for p in telemetry if p['lap'] >= start_lap]
|
filtered_df = df[df['lap_number'] >= start_lap].copy()
|
||||||
if end_lap:
|
if end_lap:
|
||||||
filtered = [p for p in filtered if p['lap'] <= end_lap]
|
filtered_df = filtered_df[filtered_df['lap_number'] <= end_lap].copy()
|
||||||
|
|
||||||
if not filtered:
|
if len(filtered_df) == 0:
|
||||||
print("❌ No telemetry points in specified lap range")
|
print("❌ No telemetry points in specified lap range")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Reset index for easier iteration
|
||||||
|
filtered_df = filtered_df.reset_index(drop=True)
|
||||||
|
|
||||||
print(f"\n🏁 Starting telemetry stream simulation")
|
print(f"\n🏁 Starting telemetry stream simulation")
|
||||||
print(f" Endpoint: {endpoint}")
|
print(f" Endpoint: {endpoint}")
|
||||||
print(f" Laps: {start_lap} → {end_lap or 'end'}")
|
print(f" Laps: {start_lap} → {end_lap or 'end'}")
|
||||||
print(f" Speed: {speed}x")
|
print(f" Speed: {speed}x")
|
||||||
print(f" Points: {len(filtered)}")
|
print(f" Points: {len(filtered_df)}")
|
||||||
print(f" Duration: {filtered[-1]['timestamp_ms'] / 1000.0:.1f}s\n")
|
|
||||||
|
|
||||||
start_time = time.time()
|
total_duration = (filtered_df['overall_time'].iloc[-1] - filtered_df['overall_time'].iloc[0]).total_seconds()
|
||||||
start_ts = filtered[0]['timestamp_ms']
|
print(f" Duration: {total_duration:.1f}s (real-time) → {total_duration / speed:.1f}s (playback)\n")
|
||||||
|
|
||||||
sent_count = 0
|
sent_count = 0
|
||||||
error_count = 0
|
error_count = 0
|
||||||
|
current_lap = start_lap
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for i, point in enumerate(filtered):
|
for i in range(len(filtered_df)):
|
||||||
# Calculate when this point should be sent
|
row = filtered_df.iloc[i]
|
||||||
point_offset = (point['timestamp_ms'] - start_ts) / 1000.0 / speed
|
|
||||||
target_time = start_time + point_offset
|
|
||||||
|
|
||||||
# Wait until the right time
|
# Calculate sleep time based on time difference to next row
|
||||||
sleep_time = target_time - time.time()
|
if i < len(filtered_df) - 1:
|
||||||
if sleep_time > 0:
|
next_row = filtered_df.iloc[i + 1]
|
||||||
time.sleep(sleep_time)
|
time_diff = (next_row['overall_time'] - row['overall_time']).total_seconds()
|
||||||
|
sleep_time = time_diff / speed
|
||||||
|
|
||||||
|
# Ensure positive sleep time
|
||||||
|
if sleep_time < 0:
|
||||||
|
sleep_time = 0
|
||||||
|
else:
|
||||||
|
sleep_time = 0
|
||||||
|
|
||||||
|
# Convert row to JSON
|
||||||
|
telemetry_point = row_to_json(row)
|
||||||
|
|
||||||
# Send telemetry point
|
# Send telemetry point
|
||||||
try:
|
try:
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
endpoint,
|
endpoint,
|
||||||
json=point,
|
json=telemetry_point,
|
||||||
timeout=2.0
|
timeout=2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
sent_count += 1
|
sent_count += 1
|
||||||
if sent_count % 100 == 0:
|
|
||||||
elapsed = time.time() - start_time
|
# Print progress updates
|
||||||
progress = (i + 1) / len(filtered) * 100
|
if row['lap_number'] > current_lap:
|
||||||
print(f" 📡 Lap {point['lap']}: {sent_count} points sent "
|
current_lap = row['lap_number']
|
||||||
f"({progress:.1f}% complete, {elapsed:.1f}s elapsed)")
|
progress = (i + 1) / len(filtered_df) * 100
|
||||||
|
print(f" 📡 Lap {int(current_lap)}: {sent_count} points sent "
|
||||||
|
f"({progress:.1f}% complete)")
|
||||||
|
elif sent_count % 500 == 0:
|
||||||
|
progress = (i + 1) / len(filtered_df) * 100
|
||||||
|
print(f" 📡 Lap {int(row['lap_number'])}: {sent_count} points sent "
|
||||||
|
f"({progress:.1f}% complete)")
|
||||||
else:
|
else:
|
||||||
error_count += 1
|
error_count += 1
|
||||||
print(f" ⚠ HTTP {response.status_code}: {response.text[:50]}")
|
print(f" ⚠ HTTP {response.status_code}: {response.text[:50]}")
|
||||||
@@ -98,35 +139,47 @@ def simulate_stream(
|
|||||||
except requests.RequestException as e:
|
except requests.RequestException as e:
|
||||||
error_count += 1
|
error_count += 1
|
||||||
if error_count % 10 == 0:
|
if error_count % 10 == 0:
|
||||||
print(f" ⚠ Connection error ({error_count} total): {e}")
|
print(f" ⚠ Connection error ({error_count} total): {str(e)[:50]}")
|
||||||
|
|
||||||
|
# Sleep until next point should be sent
|
||||||
|
if sleep_time > 0:
|
||||||
|
time.sleep(sleep_time)
|
||||||
|
|
||||||
print(f"\n✅ Stream complete!")
|
print(f"\n✅ Stream complete!")
|
||||||
print(f" Sent: {sent_count} points")
|
print(f" Sent: {sent_count} points")
|
||||||
print(f" Errors: {error_count}")
|
print(f" Errors: {error_count}")
|
||||||
print(f" Duration: {time.time() - start_time:.1f}s")
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print(f"\n⏸ Stream interrupted by user")
|
print(f"\n⏸ Stream interrupted by user")
|
||||||
print(f" Sent: {sent_count}/{len(filtered)} points")
|
print(f" Sent: {sent_count}/{len(filtered_df)} points")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Simulate Raspberry Pi telemetry streaming"
|
description="Simulate Raspberry Pi telemetry streaming from CSV data"
|
||||||
)
|
)
|
||||||
parser.add_argument("--data", type=str, required=True, help="Path to telemetry JSON file")
|
parser.add_argument("--data", type=str, default="ALONSO_2023_MONZA_RACE",
|
||||||
|
help="Path to telemetry CSV file")
|
||||||
parser.add_argument("--endpoint", type=str, default="http://localhost:8000/telemetry",
|
parser.add_argument("--endpoint", type=str, default="http://localhost:8000/telemetry",
|
||||||
help="HPC API endpoint")
|
help="HPC API endpoint")
|
||||||
parser.add_argument("--speed", type=float, default=1.0, help="Playback speed (1.0 = real-time)")
|
parser.add_argument("--speed", type=float, default=1.0,
|
||||||
|
help="Playback speed (1.0 = real-time, 10.0 = 10x speed)")
|
||||||
parser.add_argument("--start-lap", type=int, default=1, help="Starting lap number")
|
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")
|
parser.add_argument("--end-lap", type=int, default=None, help="Ending lap number")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
telemetry = load_telemetry(Path(args.data))
|
# Handle relative paths from the project root
|
||||||
|
data_path = Path(args.data)
|
||||||
|
if not data_path.is_absolute():
|
||||||
|
# Try relative to script location first
|
||||||
|
script_dir = Path(__file__).parent.parent
|
||||||
|
data_path = script_dir / args.data
|
||||||
|
|
||||||
|
df = load_telemetry_csv(data_path)
|
||||||
simulate_stream(
|
simulate_stream(
|
||||||
telemetry,
|
df,
|
||||||
args.endpoint,
|
args.endpoint,
|
||||||
args.speed,
|
args.speed,
|
||||||
args.start_lap,
|
args.start_lap,
|
||||||
@@ -134,6 +187,7 @@ def main():
|
|||||||
)
|
)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(f"❌ File not found: {args.data}")
|
print(f"❌ File not found: {args.data}")
|
||||||
|
print(f" Tried: {data_path}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Error: {e}")
|
print(f"❌ Error: {e}")
|
||||||
|
|||||||
Reference in New Issue
Block a user