Analyze F1 Lap Telemetry with Octave

Build an F1 telemetry analysis toolkit in GNU Octave with multi-panel charts.

Introduction

30 Second Summary

Every Formula 1 race weekend, performance engineers analyze thousands of data points from car sensors to find the tiny differences that separate winning from losing. The charts they build in MATLAB are how teams decide on car setup, strategy, and driver coaching.

In this project, you will use GNU Octave, a free and open-source alternative to MATLAB, to load simulated F1 telemetry data and build the same kinds of visualizations that real F1 engineers produce at the pit wall.

What You'll Build

A telemetry analysis toolkit that processes Formula 1 lap data and produces multi-panel performance charts comparing two drivers on the same circuit.

By the end of this project, you'll have:

  • A CSV telemetry dataset with realistic F1 data fields including speed, throttle, braking, gear, and G-force channels.
  • A speed trace overlay comparing two drivers' lap performance. This is the single most important chart in F1 telemetry analysis.
  • A multi-panel telemetry dashboard with speed, throttle, braking, and lateral G-force subplots matching the layout used on F1 pit wall screens.
  • A speed delta chart that pinpoints exactly where on track each driver gains or loses time.
  • Secret Mission: A braking point detector that identifies exactly where each driver brakes into corners and calculates who brakes later.

Are there any prerequisites?

None. This project walks you through installing GNU Octave from scratch on Windows. Everything you need is free and included in the guide.

The syntax you learn in Octave transfers directly to MATLAB, so you will be ready to hit the ground running when you get access at university.

Before We Start

Before We Start...

Install GNU Octave on Windows

Before you can analyze F1 telemetry, you need a data analysis tool that handles matrices, calculations, and plotting.

GNU Octave is a free, open-source alternative to MATLAB. In this step, you'll install it on your Windows machine and create a project folder for your telemetry work.

What is GNU Octave?

Octave is a scientific computing language designed for matrix operations, data analysis, and visualization. It uses nearly identical syntax to MATLAB, so the skills you learn here transfer directly when you get MATLAB access at university.

In this step, get ready to:
  • Install GNU Octave 11.1.0 on Windows.
  • Verify that Octave can run commands and create plots.
  • Create a project folder for your telemetry scripts.
Download and install GNU Octave

The Octave installer is approximately 540 MB. Make sure you have a stable internet connection before starting the download.

  • In your web browser, go to the GNU Octave download page.
  • Scroll down to the Microsoft Windows section.
  • Click the download link for octave-11.1.0-w64-installer.exe.
  • Once the download completes, double-click the installer file to run it.

Seeing a Windows SmartScreen warning?

Windows may block the installer because Octave is open-source software. Click More info, then click Run anyway to proceed.

  • Click Next on each screen to accept the default settings.
  • Click Install to begin the installation.
  • Wait for the installation to complete. This takes 5 to 12 minutes depending on your machine.
  • Click Finish to close the installer.
Launch Octave and verify it works
  • Press the Windows key to open your search bar.
  • Type GNU Octave and press Enter to open it.

The Octave GUI opens with several panels. The Command Window at the bottom is where you'll type all your commands.

  • Confirm your Octave version by typing this command in the Command Window and pressing Enter:
ver

You should see GNU Octave Version 11.1.0 in the output.

  • Test the plotting engine by running this command:
plot(1:10)

A plot window appears with a simple line going from 1 to 10. This confirms that Octave's graphics engine is working.

  • Close the plot window by clicking the X button in the top right corner.

Plot window didn't appear?

Make sure you typed plot(1:10) exactly, including the parentheses and colon. The plot window may also open behind the Octave GUI. Check your taskbar for a second Octave window.

If you're still stuck, help me troubleshoot why my Octave plot window isn't showing up.

Create your project folder

All your telemetry scripts and data files will live in one dedicated folder. This keeps everything organized as the project grows.

  • Create the project folder and move into it by running these commands in the Command Window:
% Create a new folder for all your F1 telemetry work
mkdir("f1_telemetry")

% Move into the new folder
cd("f1_telemetry")

What do these commands do?

  • mkdir("f1_telemetry") creates a new folder called f1_telemetry inside your current directory.
  • cd("f1_telemetry") changes your working directory into that folder, so any files you create next land inside it.
  • Confirm your location by running:
pwd

You should see a file path ending in f1_telemetry.

Seeing an error from mkdir?

If Octave says the folder already exists, that's fine. Just run cd("f1_telemetry") to move into it and continue.

If you see a different error, help me fix this folder creation error in Octave.

Your environment is all set. Next up, you'll create the telemetry dataset that simulates a real F1 lap.

Create the Telemetry Dataset

Your Octave environment is installed and your plotting engine is verified. The next step is to create the data you'll analyze for the rest of this project.

Real F1 cars generate thousands of data points per second from over 150 sensors. You'll build a script that creates a simplified version of this telemetry using the same data channels real engineers analyze: speed, throttle, brake, gear, and G-forces.

In this step, get ready to:
  • Create a data generation script in the Octave editor.
  • Run the script to produce CSV telemetry files for two drivers.
  • Load and inspect the data matrix to verify it looks correct.
Create the data generation script

Instead of downloading real telemetry from an external source, you'll generate synthetic data programmatically. The data uses sine waves to simulate speed variation around a circuit, with peaks representing straights and dips representing corners.

Why generate synthetic data?

Generating data in code means no external downloads or API keys are required. It also teaches you a core MATLAB skill: building datasets programmatically.

The data structure matches real F1 telemetry exactly, so the analysis techniques you build here transfer directly to real-world data.

  • Click File in the Octave menu bar.
  • Click New Script to open the editor.
  • Paste the following code into the editor. This first section creates the track layout and generates all seven telemetry channels for Driver VER:
% generate_data.m - Generate simulated F1 telemetry data for two drivers
% Creates CSV files with columns: distance, speed, throttle, brake, gear, lateral_g, longitudinal_g

% Track distance points (0 to 5000 meters, one lap)
dist = linspace(0, 5000, 500)';

% --- Helper: generate a single driver's telemetry ---
% We simulate a lap with straights and corners using sine/cosine patterns

% Driver VER (slightly faster in corners, slightly slower on straights)
rng_seed_ver = 42;
rand("state", rng_seed_ver);

% Base speed profile: straights at ~310 km/h, corners dip to ~120 km/h
speed_ver = 215 + 95 * sin(2 * pi * dist / 1250) ...
            + 30 * sin(2 * pi * dist / 625) ...
            + 5 * randn(size(dist));
speed_ver = max(speed_ver, 80);  % minimum speed 80 km/h
speed_ver = min(speed_ver, 340); % maximum speed 340 km/h

% Throttle: high on straights, low in corners (correlated with speed)
throttle_ver = max(0, min(100, (speed_ver - 80) / (340 - 80) * 100 + 5 * randn(size(dist))));

% Brake: active when speed is dropping significantly
speed_diff_ver = [0; diff(speed_ver)];
brake_ver = double(speed_diff_ver < -2);

% Gear: proportional to speed (1-8)
gear_ver = max(1, min(8, round((speed_ver - 80) / (340 - 80) * 7 + 1)));

% Lateral G: high in corners (inversely related to speed on a track)
lateral_g_ver = -3.5 * cos(2 * pi * dist / 1250) ...
               - 1.5 * cos(2 * pi * dist / 625) ...
               + 0.3 * randn(size(dist));

% Longitudinal G: based on acceleration/braking
longitudinal_g_ver = speed_diff_ver * 3.6 / 0.27 / 9.81;  % approximate m/s^2 to G
longitudinal_g_ver = max(-5, min(2, longitudinal_g_ver));
longitudinal_g_ver = longitudinal_g_ver + 0.1 * randn(size(dist));

% Combine and write VER data
data_ver = [dist, speed_ver, throttle_ver, brake_ver, gear_ver, lateral_g_ver, longitudinal_g_ver];
csvwrite("driver_ver.csv", data_ver);

What does this code do?

  • linspace(0, 5000, 500) creates 500 evenly spaced distance points from 0 to 5000 meters, representing one lap of a circuit.
  • The speed profile uses sine waves to simulate a lap. Peaks are straights (up to 340 km/h) and troughs are corners (down to 80 km/h).
  • randn(size(dist)) adds small random noise so the data looks realistic rather than perfectly smooth.
  • Each telemetry channel (throttle, brake, gear, lateral G, longitudinal G) is derived from the speed profile, just like a real car where all these values are physically connected.
  • csvwrite("driver_ver.csv", data_ver) writes the combined matrix to a CSV file on disk.

You won't see any output yet. The next section adds the second driver's data.

  • Continue in the same file. Paste the following code directly below the VER section to generate Driver NOR's telemetry and print a confirmation message:
% Driver NOR (slightly slower in corners, slightly faster on straights)
rand("state", 77);

speed_nor = 218 + 92 * sin(2 * pi * dist / 1250) ...
            + 28 * sin(2 * pi * dist / 625) ...
            + 5 * randn(size(dist));
speed_nor = max(speed_nor, 80);
speed_nor = min(speed_nor, 340);

throttle_nor = max(0, min(100, (speed_nor - 80) / (340 - 80) * 100 + 5 * randn(size(dist))));

speed_diff_nor = [0; diff(speed_nor)];
brake_nor = double(speed_diff_nor < -2);

gear_nor = max(1, min(8, round((speed_nor - 80) / (340 - 80) * 7 + 1)));

lateral_g_nor = -3.2 * cos(2 * pi * dist / 1250) ...
               - 1.4 * cos(2 * pi * dist / 625) ...
               + 0.3 * randn(size(dist));

longitudinal_g_nor = speed_diff_nor * 3.6 / 0.27 / 9.81;
longitudinal_g_nor = max(-5, min(2, longitudinal_g_nor));
longitudinal_g_nor = longitudinal_g_nor + 0.1 * randn(size(dist));

data_nor = [dist, speed_nor, throttle_nor, brake_nor, gear_nor, lateral_g_nor, longitudinal_g_nor];
csvwrite("driver_nor.csv", data_nor);

printf("Telemetry data generated successfully!\n");
printf("  driver_ver.csv: %d rows x %d columns\n", rows(data_ver), columns(data_ver));
printf("  driver_nor.csv: %d rows x %d columns\n", rows(data_nor), columns(data_nor));

What does this code do?

  • This section follows the exact same pattern as VER, but with slightly different tuning. NOR has a higher base speed on straights (218 vs 215) and lower cornering amplitudes (92 vs 95).
  • rand("state", 77) sets a different random seed so NOR's noise pattern is unique from VER's.
  • The printf lines at the end print a confirmation message with the row and column counts for each CSV file.
  • Press Ctrl+S to save the file.
  • In the Save dialog, name the file generate_data.m.
  • Make sure you save it inside your f1_telemetry folder.

Not sure which folder to save in?

Check the File Browser panel on the left side of the Octave GUI. It shows your current directory. If it doesn't say f1_telemetry, navigate to it before saving.

You can also type pwd in the Command Window to confirm your current location.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file before moving on.

ⓧ I'd like to double check the full code

Here is the complete generate_data.m file. Compare it against your editor to make sure everything matches.

% generate_data.m - Generate simulated F1 telemetry data for two drivers
% Creates CSV files with columns: distance, speed, throttle, brake, gear, lateral_g, longitudinal_g

% Track distance points (0 to 5000 meters, one lap)
dist = linspace(0, 5000, 500)';

% --- Helper: generate a single driver's telemetry ---
% We simulate a lap with straights and corners using sine/cosine patterns

% Driver VER (slightly faster in corners, slightly slower on straights)
rng_seed_ver = 42;
rand("state", rng_seed_ver);

% Base speed profile: straights at ~310 km/h, corners dip to ~120 km/h
speed_ver = 215 + 95 * sin(2 * pi * dist / 1250) ...
            + 30 * sin(2 * pi * dist / 625) ...
            + 5 * randn(size(dist));
speed_ver = max(speed_ver, 80);  % minimum speed 80 km/h
speed_ver = min(speed_ver, 340); % maximum speed 340 km/h

% Throttle: high on straights, low in corners (correlated with speed)
throttle_ver = max(0, min(100, (speed_ver - 80) / (340 - 80) * 100 + 5 * randn(size(dist))));

% Brake: active when speed is dropping significantly
speed_diff_ver = [0; diff(speed_ver)];
brake_ver = double(speed_diff_ver < -2);

% Gear: proportional to speed (1-8)
gear_ver = max(1, min(8, round((speed_ver - 80) / (340 - 80) * 7 + 1)));

% Lateral G: high in corners (inversely related to speed on a track)
lateral_g_ver = -3.5 * cos(2 * pi * dist / 1250) ...
               - 1.5 * cos(2 * pi * dist / 625) ...
               + 0.3 * randn(size(dist));

% Longitudinal G: based on acceleration/braking
longitudinal_g_ver = speed_diff_ver * 3.6 / 0.27 / 9.81;  % approximate m/s^2 to G
longitudinal_g_ver = max(-5, min(2, longitudinal_g_ver));
longitudinal_g_ver = longitudinal_g_ver + 0.1 * randn(size(dist));

% Combine and write VER data
data_ver = [dist, speed_ver, throttle_ver, brake_ver, gear_ver, lateral_g_ver, longitudinal_g_ver];
csvwrite("driver_ver.csv", data_ver);

% Driver NOR (slightly slower in corners, slightly faster on straights)
rand("state", 77);

speed_nor = 218 + 92 * sin(2 * pi * dist / 1250) ...
            + 28 * sin(2 * pi * dist / 625) ...
            + 5 * randn(size(dist));
speed_nor = max(speed_nor, 80);
speed_nor = min(speed_nor, 340);

throttle_nor = max(0, min(100, (speed_nor - 80) / (340 - 80) * 100 + 5 * randn(size(dist))));

speed_diff_nor = [0; diff(speed_nor)];
brake_nor = double(speed_diff_nor < -2);

gear_nor = max(1, min(8, round((speed_nor - 80) / (340 - 80) * 7 + 1)));

lateral_g_nor = -3.2 * cos(2 * pi * dist / 1250) ...
               - 1.4 * cos(2 * pi * dist / 625) ...
               + 0.3 * randn(size(dist));

longitudinal_g_nor = speed_diff_nor * 3.6 / 0.27 / 9.81;
longitudinal_g_nor = max(-5, min(2, longitudinal_g_nor));
longitudinal_g_nor = longitudinal_g_nor + 0.1 * randn(size(dist));

data_nor = [dist, speed_nor, throttle_nor, brake_nor, gear_nor, lateral_g_nor, longitudinal_g_nor];
csvwrite("driver_nor.csv", data_nor);

printf("Telemetry data generated successfully!\n");
printf("  driver_ver.csv: %d rows x %d columns\n", rows(data_ver), columns(data_ver));
printf("  driver_nor.csv: %d rows x %d columns\n", rows(data_nor), columns(data_nor));
Run the script and verify the CSV files

With your script saved, you can now execute it from the Command Window. Octave runs any .m script by typing its name (without the extension).

  • Run your data generation script by typing this in the Command Window:
generate_data

You should see three lines of output confirming the files were created. The message will show 500 rows x 7 columns for each driver.

  • Verify the CSV files were created by running:
ls *.csv

You should see driver_nor.csv and driver_ver.csv listed in the output.

Seeing an error when running the script?

  • If you see error: 'generate_data' undefined, Octave can't find your file. Make sure you saved it as generate_data.m (not generate_data.m.m or a different name) inside the f1_telemetry folder.
  • If you see a syntax error with a line number, double check that line in your editor against the full code in the double-check tab above.
  • Confirm your current directory is correct by typing pwd. The path should end in f1_telemetry.

Why is my generate_data script not running in Octave?

Inspect the data matrix

Before building any charts, it's good practice to load the data and check that it has the shape you expect. The csvread function reads a numeric CSV file directly into a matrix.

  • Load and inspect Driver VER's data by running these commands in the Command Window:
data = csvread("driver_ver.csv");
size(data)
data(1:5, :)

What should I see?

  • size(data) returns 500 7. That means 500 data points (rows) across 7 telemetry channels (columns).
  • data(1:5, :) shows the first 5 rows of all 7 columns. The columns are: distance, speed, throttle, brake, gear, lateral G, and longitudinal G.
  • The first column (distance) should start near 0 and increase. The second column (speed) should be a number in the range of 80 to 340.

What is matrix indexing?

The syntax data(1:5, :) means "rows 1 through 5, all columns." The colon : on its own is shorthand for "everything." This is how you slice data in Octave and MATLAB.

Your telemetry data is ready. You have two CSV files with realistic F1 data for two drivers on the same circuit. Next up, you'll create your first visualization: a speed trace that overlays both drivers' laps.

Plot the Speed Trace

Your telemetry data is ready. Two CSV files are sitting in your f1_telemetry folder, each containing 500 rows of simulated lap data across 7 telemetry channels.

Now it's time to visualize that data. The speed trace is the most fundamental chart in F1 telemetry analysis. It plots a driver's speed against their position on track, and every performance engineer reads one daily.

In this step, get ready to:
  • Create a speed_trace.m script that loads and plots both drivers' telemetry.
  • Run the script and learn how to read a speed trace chart.
  • Export the chart to a PNG image file.
Create the speed trace script

You'll write a script that loads both drivers' CSV files, pulls out the distance and speed columns, and overlays them on a single chart.

  • In the GNU Octave GUI, click File in the menu bar.
  • Click New Script (or press Ctrl+N) to open a blank editor tab.
  • Load both drivers' CSV files and extract the distance and speed columns by pasting this code into the new editor tab:
% speed_trace.m - Plot speed traces for two drivers
% The speed trace is the most fundamental chart in F1 telemetry analysis

% Load telemetry data
ver = csvread("driver_ver.csv");
nor = csvread("driver_nor.csv");

% Extract distance (column 1) and speed (column 2)
dist_ver = ver(:, 1);
speed_ver = ver(:, 2);

dist_nor = nor(:, 1);
speed_nor = nor(:, 2);

What does this code do?

  • csvread("driver_ver.csv") reads the entire CSV file into a matrix called ver. Each row is one data point and each column is a telemetry channel.
  • ver(:, 1) extracts column 1 (distance) from the matrix. The colon : means "all rows".
  • ver(:, 2) extracts column 2 (speed). The same pattern works for any of the 7 columns in your telemetry data.
  • Add the plotting code directly below the lines you just pasted:
% Create the speed trace plot
figure("name", "Speed Trace Comparison");

plot(dist_ver, speed_ver, "b", "linewidth", 1.5);
hold on;
plot(dist_nor, speed_nor, "r", "linewidth", 1.5);
hold off;

xlabel("Distance (m)");
ylabel("Speed (km/h)");
title("F1 Speed Trace - VER vs NOR");
legend("VER", "NOR", "location", "northeast");
grid on;

What does this code do?

  • figure("name", "Speed Trace Comparison") creates a new plot window with a descriptive title.
  • plot(dist_ver, speed_ver, "b", "linewidth", 1.5) draws VER's speed as a blue line against distance. The "b" sets the color to blue.
  • hold on tells Octave to keep the first line when drawing the second. Without it, the second plot call would erase the first.
  • xlabel, ylabel, title, legend, and grid on add labels and formatting to make the chart readable.
  • Save the file by pressing Ctrl+S.
  • In the save dialog, type speed_trace.m as the filename and confirm it saves to your f1_telemetry folder.

Save dialog pointing to the wrong folder?

If the save dialog opens to a different location, navigate to the f1_telemetry folder you created in Step 1. You can confirm your current working directory at any time by typing pwd in the Command Window.

Help me save my script in the right folder

Run the speed trace script

Your script is saved and ready to run. Octave runs scripts by name from the Command Window.

  • Run your speed trace script by typing this in the Command Window:
speed_trace

A plot window appears with two colored lines. The blue line is VER and the red line is NOR.

How to read a speed trace

Speed traces are the single most important chart in F1 telemetry. Here's what the different parts of your chart mean.

  • Peaks are where drivers reach top speed on straights.
  • Sharp drops are braking zones, where drivers brake hard before a corner.
  • Valleys are the corners themselves, where speed is at its lowest.
  • Where one line sits higher than the other, that driver is carrying more speed through that section of the track.

Don't see a plot window?

  • Make sure you saved speed_trace.m inside the f1_telemetry folder. Octave can only run scripts that are in your current working directory.
  • Check that you typed speed_trace (without the .m extension) in the Command Window.
  • The plot window may have opened behind the Octave GUI. Check your taskbar for an extra window.

My speed trace script isn't producing a plot window

Save the plot to a PNG file

The plot is visible in a window, but it's not saved as a file yet. Octave can export figures to image files with a single command.

  • In the speed_trace.m editor tab, scroll to the bottom of the script.
  • Add these lines at the very end, below grid on;:
% Save to file
print -dpng speed_trace.png
printf("Speed trace saved to speed_trace.png\n");

What does this code do?

  • print -dpng speed_trace.png exports the current figure to a PNG image file. The -dpng flag tells Octave to use the PNG format.
  • printf prints a confirmation message to the Command Window so you know the save completed.
  • Save speed_trace.m by pressing Ctrl+S.
  • Rerun the script by typing this in the Command Window:
speed_trace
  • Verify the PNG file was created by typing this in the Command Window:
ls *.png

You should see speed_trace.png listed in the output. You should also see "Speed trace saved to speed_trace.png" printed in the Command Window.

Don't see speed_trace.png?

  • Make sure you saved speed_trace.m after adding the new lines. Unsaved changes won't run.
  • Check that print -dpng speed_trace.png appears after grid on; at the bottom of your script.
  • Close any existing plot windows and rerun the script. The print command saves whatever figure is currently active.

My script isn't saving the plot to a PNG file

✔️ Awesome, I've got everything!

Double check you've saved speed_trace.m and that speed_trace.png exists in your f1_telemetry folder.

ⓧ I'd like to double check the full code

Here is the complete speed_trace.m file. Make sure your script matches this exactly.

% speed_trace.m - Plot speed traces for two drivers
% The speed trace is the most fundamental chart in F1 telemetry analysis

% Load telemetry data
ver = csvread("driver_ver.csv");
nor = csvread("driver_nor.csv");

% Extract distance (column 1) and speed (column 2)
dist_ver = ver(:, 1);
speed_ver = ver(:, 2);

dist_nor = nor(:, 1);
speed_nor = nor(:, 2);

% Create the speed trace plot
figure("name", "Speed Trace Comparison");

plot(dist_ver, speed_ver, "b", "linewidth", 1.5);
hold on;
plot(dist_nor, speed_nor, "r", "linewidth", 1.5);
hold off;

xlabel("Distance (m)");
ylabel("Speed (km/h)");
title("F1 Speed Trace - VER vs NOR");
legend("VER", "NOR", "location", "northeast");
grid on;

% Save to file
print -dpng speed_trace.png
printf("Speed trace saved to speed_trace.png\n");

You now have a working speed trace and a saved image of it. Next up, you'll build a full multi-panel telemetry dashboard that shows speed, throttle, braking, and G-forces all at once.

Build the Telemetry Dashboard

Your speed trace gives a clear picture of how two drivers compare on raw pace. In a real F1 garage, though, engineers never look at speed alone.

They use multi-panel dashboards that stack speed, throttle, braking, and G-force data on top of each other. This lets them spot patterns that a single chart would hide. In this step, you will build that exact layout using GNU Octave's subplot function.

In this step, get ready to:
  • Create a dashboard.m script with four vertically stacked subplot panels.
  • Run the dashboard and examine the telemetry layout.
  • Add a console summary that prints key statistics for both drivers.
Create the dashboard script

The subplot function splits a single figure into a grid of smaller panels. Each panel gets its own axes and its own plot. You will create a 4-row, 1-column grid so that all four telemetry channels line up vertically and share the same distance axis.

  • Open a new script by clicking File in the Octave menu bar, then New Script.
  • Copy the following code into the editor. This loads both CSV files, sets up a tall figure window, and creates the first panel (speed):
% dashboard.m - Multi-panel F1 telemetry dashboard
% Replicates the layout used on F1 pit wall screens

% Load telemetry data
ver = csvread("driver_ver.csv");
nor = csvread("driver_nor.csv");

% Column mapping: 1=distance, 2=speed, 3=throttle, 4=brake, 5=gear, 6=lateral_g, 7=longitudinal_g
dist_ver = ver(:, 1);  dist_nor = nor(:, 1);

% Create a tall figure
figure("name", "Telemetry Dashboard", "position", [100, 50, 900, 700]);

% Panel 1: Speed
subplot(4, 1, 1);
plot(dist_ver, ver(:, 2), "b", "linewidth", 1.2);
hold on;
plot(dist_nor, nor(:, 2), "r", "linewidth", 1.2);
hold off;
ylabel("Speed (km/h)");
title("F1 Telemetry Dashboard - VER (blue) vs NOR (red)");
legend("VER", "NOR", "location", "northeast");
grid on;

What does this code do?

csvread loads each CSV file into a matrix, just like in your speed trace script. The column mapping comment tells you which column number corresponds to which telemetry channel.

figure("position", [100, 50, 900, 700]) creates a 900-pixel-wide, 700-pixel-tall window. The extra height gives each of the four panels enough room to be readable.

subplot(4, 1, 1) means "divide the figure into a 4-row, 1-column grid and activate panel 1." Everything you plot after this call goes into that first panel.

You won't see a change yet. The remaining three panels come next.

  • Add the following code directly below the Panel 1 block. This creates the throttle, brake, and lateral G-force panels and saves the figure to a PNG:
% Panel 2: Throttle
subplot(4, 1, 2);
plot(dist_ver, ver(:, 3), "b", "linewidth", 1.2);
hold on;
plot(dist_nor, nor(:, 3), "r", "linewidth", 1.2);
hold off;
ylabel("Throttle (%)");
grid on;

% Panel 3: Brake
subplot(4, 1, 3);
plot(dist_ver, ver(:, 4), "b", "linewidth", 1.5);
hold on;
plot(dist_nor, nor(:, 4), "r", "linewidth", 1.5);
hold off;
ylabel("Brake (0/1)");
ylim([-0.1, 1.1]);
grid on;

% Panel 4: Lateral G-Force
subplot(4, 1, 4);
plot(dist_ver, ver(:, 6), "b", "linewidth", 1.2);
hold on;
plot(dist_nor, nor(:, 6), "r", "linewidth", 1.2);
hold off;
xlabel("Distance (m)");
ylabel("Lateral G (m/s^2)");
grid on;

% Save dashboard
print -dpng dashboard.png

What does this code do?

Each panel follows the same pattern: subplot to activate the panel, plot both drivers with hold on, then label the Y-axis.

  • Panel 2 plots throttle percentage (column 3). Higher values mean the driver is pushing the accelerator harder.
  • Panel 3 plots the brake signal (column 4). The brake is binary (0 = off, 1 = on), so ylim([-0.1, 1.1]) keeps the axis tight around those values.
  • Panel 4 plots lateral G-force (column 6). High absolute values mean the car is pulling hard through a corner.
  • Only the bottom panel gets an xlabel because all four panels share the same distance axis.
  • Save the file as dashboard.m inside your f1_telemetry folder by pressing Ctrl+S.
Run the dashboard script
  • Run the dashboard by typing this in the Command Window:
dashboard

A tall figure window appears with four vertically stacked charts. All four panels are aligned on the same distance axis so you can read straight down from a speed dip to see what the throttle, brake, and lateral G were doing at that exact point on track.

What should I see in each panel?

  • Speed (top): the same trace you built in the previous step. Peaks are straights, dips are corners.
  • Throttle: shows how aggressively each driver gets on the power coming out of corners. Look for how quickly the trace jumps back to 100% after a dip.
  • Brake: rectangular pulses that mark braking zones. Each pulse is where a driver stamps on the brakes before a corner.
  • Lateral G (bottom): shows cornering forces. Large swings mean the car is pulling hard through a turn.

This multi-panel layout is exactly how F1 teams display telemetry data on their pit wall screens.

Don't see four panels?

  • Make sure all four subplot calls use the same grid size: subplot(4, 1, N) where N is 1, 2, 3, or 4.
  • Check that you saved the file as dashboard.m (not dashboard.m.m or a different name). The Command Window runs the file that matches the name you type.
  • If the figure window is too small to read, try maximizing it. The panels resize automatically.

My dashboard figure is not displaying correctly. Help me debug.

Add a console summary with key statistics

Charts are great for spotting patterns, but engineers also need hard numbers. The fprintf function prints formatted text to the Command Window. You will use it to build a comparison table with stats like max speed, average throttle, and braking events.

  • In the Octave editor, scroll to the bottom of dashboard.m.
  • Find the line that says print -dpng dashboard.png.
  • Add the following code below it:
% --- Console Summary ---
fprintf("\n=== TELEMETRY SUMMARY ===\n\n");

fprintf("%-25s %10s %10s\n", "Metric", "VER", "NOR");
fprintf("%-25s %10s %10s\n", "-------------------------", "----------", "----------");

fprintf("%-25s %10.1f %10.1f\n", "Max Speed (km/h)", max(ver(:, 2)), max(nor(:, 2)));
fprintf("%-25s %10.1f %10.1f\n", "Min Speed (km/h)", min(ver(:, 2)), min(nor(:, 2)));
fprintf("%-25s %10.1f %10.1f\n", "Avg Throttle (%)", mean(ver(:, 3)), mean(nor(:, 3)));
fprintf("%-25s %10d %10d\n", "Braking Events", sum(diff(ver(:, 4)) == 1), sum(diff(nor(:, 4)) == 1));
fprintf("%-25s %10.2f %10.2f\n", "Peak Lateral G (m/s^2)", max(abs(ver(:, 6))), max(abs(nor(:, 6))));
fprintf("%-25s %10.2f %10.2f\n", "Peak Braking G", min(ver(:, 7)), min(nor(:, 7)));

fprintf("\nDashboard saved to dashboard.png\n");

What does this code do?

fprintf uses format specifiers to align text into columns. %-25s means a left-aligned string padded to 25 characters. %10.1f means a number with 1 decimal place, right-aligned in a 10-character column.

  • max(ver(:, 2)) finds the highest speed in VER's data (the top of the straights).
  • mean(ver(:, 3)) calculates average throttle application across the whole lap.
  • sum(diff(ver(:, 4)) == 1) counts braking events. diff finds where the brake column jumps from 0 to 1, and sum counts how many times that happens.
  • min(ver(:, 7)) finds the peak braking G. Braking G is negative (deceleration), so the minimum value is the hardest braking moment.
  • Save dashboard.m by pressing Ctrl+S.
  • Rerun the script by typing dashboard in the Command Window and pressing Enter.
  • The dashboard figure window appears again. Below it, the Command Window now shows a formatted table with metrics for both drivers side by side.

What should I see?

The Command Window displays a neatly aligned table with rows for Max Speed, Min Speed, Avg Throttle, Braking Events, Peak Lateral G, and Peak Braking G. Each row has values for both VER and NOR so you can compare them at a glance.

Seeing garbled text or an error?

  • Check that every fprintf line has matching parentheses and a closing ); at the end.
  • Make sure the \n characters are backslash-n (not a raw newline inside the string). These tell fprintf to start a new line in the output.
  • If you see "undefined function" errors, confirm you saved the file as dashboard.m and that your working directory is still f1_telemetry. Type pwd to check.

My fprintf summary is not printing correctly. Help me fix it.

  • Verify the PNG was saved by typing this in the Command Window:
ls *.png

You should see both speed_trace.png and dashboard.png listed.

✔️ Awesome, I've got everything!

Great. Double check you have saved dashboard.m and that both the figure and the console summary look correct.

ⓧ I'd like to double check the full code

Here is the complete dashboard.m file. Compare it with yours line by line.

% dashboard.m - Multi-panel F1 telemetry dashboard
% Replicates the layout used on F1 pit wall screens

% Load telemetry data
ver = csvread("driver_ver.csv");
nor = csvread("driver_nor.csv");

% Column mapping: 1=distance, 2=speed, 3=throttle, 4=brake, 5=gear, 6=lateral_g, 7=longitudinal_g
dist_ver = ver(:, 1);  dist_nor = nor(:, 1);

% Create a tall figure
figure("name", "Telemetry Dashboard", "position", [100, 50, 900, 700]);

% Panel 1: Speed
subplot(4, 1, 1);
plot(dist_ver, ver(:, 2), "b", "linewidth", 1.2);
hold on;
plot(dist_nor, nor(:, 2), "r", "linewidth", 1.2);
hold off;
ylabel("Speed (km/h)");
title("F1 Telemetry Dashboard - VER (blue) vs NOR (red)");
legend("VER", "NOR", "location", "northeast");
grid on;

% Panel 2: Throttle
subplot(4, 1, 2);
plot(dist_ver, ver(:, 3), "b", "linewidth", 1.2);
hold on;
plot(dist_nor, nor(:, 3), "r", "linewidth", 1.2);
hold off;
ylabel("Throttle (%)");
grid on;

% Panel 3: Brake
subplot(4, 1, 3);
plot(dist_ver, ver(:, 4), "b", "linewidth", 1.5);
hold on;
plot(dist_nor, nor(:, 4), "r", "linewidth", 1.5);
hold off;
ylabel("Brake (0/1)");
ylim([-0.1, 1.1]);
grid on;

% Panel 4: Lateral G-Force
subplot(4, 1, 4);
plot(dist_ver, ver(:, 6), "b", "linewidth", 1.2);
hold on;
plot(dist_nor, nor(:, 6), "r", "linewidth", 1.2);
hold off;
xlabel("Distance (m)");
ylabel("Lateral G (m/s^2)");
grid on;

% Save dashboard
print -dpng dashboard.png

% --- Console Summary ---
fprintf("\n=== TELEMETRY SUMMARY ===\n\n");

fprintf("%-25s %10s %10s\n", "Metric", "VER", "NOR");
fprintf("%-25s %10s %10s\n", "-------------------------", "----------", "----------");

fprintf("%-25s %10.1f %10.1f\n", "Max Speed (km/h)", max(ver(:, 2)), max(nor(:, 2)));
fprintf("%-25s %10.1f %10.1f\n", "Min Speed (km/h)", min(ver(:, 2)), min(nor(:, 2)));
fprintf("%-25s %10.1f %10.1f\n", "Avg Throttle (%)", mean(ver(:, 3)), mean(nor(:, 3)));
fprintf("%-25s %10d %10d\n", "Braking Events", sum(diff(ver(:, 4)) == 1), sum(diff(nor(:, 4)) == 1));
fprintf("%-25s %10.2f %10.2f\n", "Peak Lateral G (m/s^2)", max(abs(ver(:, 6))), max(abs(nor(:, 6))));
fprintf("%-25s %10.2f %10.2f\n", "Peak Braking G", min(ver(:, 7)), min(nor(:, 7)));

fprintf("\nDashboard saved to dashboard.png\n");

You now have a full telemetry dashboard that combines visualization with hard numbers. Next up, you will build a speed delta plot that pinpoints exactly where on track each driver gains or loses time.

Compare Drivers with a Speed Delta

Your dashboard gives you four channels of telemetry side by side. That is great for spotting general patterns, but it still forces you to eyeball where one driver is faster than the other.

A speed delta chart solves this. It subtracts one driver's speed from the other at every point on track and plots the difference as a filled area. Positive means VER is faster. Negative means NOR is faster. F1 engineers use this chart to pinpoint exactly where on circuit a car gains or loses time.

In this step, get ready to:
  • Create a speed_delta.m script that calculates the speed difference between drivers at every distance point.
  • Run the script to produce a filled area chart showing where each driver is faster around the lap.
Create the speed delta script

This script introduces a new plotting technique. Instead of drawing lines, you will use the fill function to shade areas above and below zero. The color of each region tells you which driver is faster at that part of the track.

  • Open a new script in the Octave editor by clicking File in the menu bar, then New Script.
  • Start building the data loading and delta calculation section by pasting this code into the editor:
% speed_delta.m - Speed difference analysis between two drivers
% Shows where each driver is faster around the lap

% Load telemetry data
ver = csvread("driver_ver.csv");
nor = csvread("driver_nor.csv");

% Extract distance and speed
dist = ver(:, 1);
speed_ver = ver(:, 2);
speed_nor = nor(:, 2);

% Calculate speed delta (VER minus NOR)
delta = speed_ver - speed_nor;

What does this code do?

  • ver(:, 1) grabs the distance column from VER's data. You only need one distance vector because both drivers are on the same track.
  • delta = speed_ver - speed_nor subtracts NOR's speed from VER's speed at every single distance point. Positive values mean VER is faster. Negative values mean NOR is faster.
  • You won't see a change yet. The next chunk builds the visualization that uses this delta vector.
  • Add the filled area chart code directly below what you just pasted:
% Create the speed delta plot
figure("name", "Speed Delta Analysis", "position", [100, 100, 900, 400]);

% Plot positive delta (VER faster) in blue
pos_delta = max(delta, 0);
neg_delta = min(delta, 0);

% Fill areas
fill_x = [dist; flipud(dist)];

fill_y_pos = [pos_delta; zeros(size(pos_delta))];
fill(fill_x, fill_y_pos, [0.2, 0.4, 0.8], "facealpha", 0.5, "edgecolor", "none");
hold on;

fill_y_neg = [neg_delta; zeros(size(neg_delta))];
fill(fill_x, fill_y_neg, [0.8, 0.2, 0.2], "facealpha", 0.5, "edgecolor", "none");

% Reference line at zero
line([min(dist), max(dist)], [0, 0], "color", "k", "linewidth", 1.5);

hold off;

xlabel("Distance (m)");
ylabel("Speed Delta (km/h)");
title("Speed Delta: VER minus NOR (blue = VER faster, red = NOR faster)");
grid on;

% Save
print -dpng speed_delta.png

What does this code do?

  • pos_delta = max(delta, 0) clamps all negative values to zero, keeping only the points where VER is faster. neg_delta = min(delta, 0) does the opposite, keeping only where NOR is faster.
  • fill_x = [dist; flipud(dist)] builds a polygon by going forward along the distance axis and then backward. This creates a closed shape that fill() can shade in.
  • The fill() function draws a filled polygon. "facealpha", 0.5 makes it semi-transparent so you can see the grid underneath.
  • line([min(dist), max(dist)], [0, 0]) draws a horizontal reference line at zero. Everything above this line is a VER advantage. Everything below is a NOR advantage.
  • You won't see the chart yet. The script needs the summary section before you run it.
  • Add the summary statistics code directly below your chart code:
% Print summary
ver_advantage = sum(delta > 0) / length(delta) * 100;
fprintf("\nVER is faster at %.1f%% of the lap\n", ver_advantage);
fprintf("NOR is faster at %.1f%% of the lap\n", 100 - ver_advantage);
fprintf("Max VER advantage: +%.1f km/h\n", max(delta));
fprintf("Max NOR advantage: +%.1f km/h\n", abs(min(delta)));
fprintf("\nSpeed delta saved to speed_delta.png\n");

What does this code do?

  • sum(delta > 0) counts how many distance points have a positive delta (VER faster). Dividing by length(delta) and multiplying by 100 converts it to a percentage of the lap.
  • max(delta) finds the biggest speed gap in VER's favor. abs(min(delta)) finds the biggest gap in NOR's favor, using abs() to flip the negative value into a positive number for clean display.
  • Save the file as speed_delta.m inside your f1_telemetry folder by pressing Ctrl+S.

File not saving to the right folder?

Make sure the save dialog shows your f1_telemetry folder as the destination. If Octave defaults to a different folder, navigate to f1_telemetry manually before clicking Save.

Can't find your f1_telemetry folder?

✔️ Awesome, I've got everything!

Great. Double check you have saved speed_delta.m before moving on.

ⓧ I'd like to double check the full code

Here is the complete speed_delta.m file. Make sure your code matches this exactly.

% speed_delta.m - Speed difference analysis between two drivers
% Shows where each driver is faster around the lap

% Load telemetry data
ver = csvread("driver_ver.csv");
nor = csvread("driver_nor.csv");

% Extract distance and speed
dist = ver(:, 1);
speed_ver = ver(:, 2);
speed_nor = nor(:, 2);

% Calculate speed delta (VER minus NOR)
delta = speed_ver - speed_nor;

% Create the speed delta plot
figure("name", "Speed Delta Analysis", "position", [100, 100, 900, 400]);

% Plot positive delta (VER faster) in blue
pos_delta = max(delta, 0);
neg_delta = min(delta, 0);

% Fill areas
fill_x = [dist; flipud(dist)];

fill_y_pos = [pos_delta; zeros(size(pos_delta))];
fill(fill_x, fill_y_pos, [0.2, 0.4, 0.8], "facealpha", 0.5, "edgecolor", "none");
hold on;

fill_y_neg = [neg_delta; zeros(size(neg_delta))];
fill(fill_x, fill_y_neg, [0.8, 0.2, 0.2], "facealpha", 0.5, "edgecolor", "none");

% Reference line at zero
line([min(dist), max(dist)], [0, 0], "color", "k", "linewidth", 1.5);

hold off;

xlabel("Distance (m)");
ylabel("Speed Delta (km/h)");
title("Speed Delta: VER minus NOR (blue = VER faster, red = NOR faster)");
grid on;

% Save
print -dpng speed_delta.png

% Print summary
ver_advantage = sum(delta > 0) / length(delta) * 100;
fprintf("\nVER is faster at %.1f%% of the lap\n", ver_advantage);
fprintf("NOR is faster at %.1f%% of the lap\n", 100 - ver_advantage);
fprintf("Max VER advantage: +%.1f km/h\n", max(delta));
fprintf("Max NOR advantage: +%.1f km/h\n", abs(min(delta)));
fprintf("\nSpeed delta saved to speed_delta.png\n");
Run and read the delta chart

The delta chart is one of the most powerful tools in F1 telemetry. Engineers use it to pinpoint exactly where on track one car gains or loses time, and whether the gap comes from straightline speed (aero efficiency) or cornering (downforce).

  • Run the script by typing this command in the Command Window:
speed_delta

A wide plot window appears with blue-shaded and red-shaded regions. A black horizontal line sits at zero.

How do I read this chart?

  • Blue regions (above zero) are sections of the track where VER is faster than NOR. The taller the blue area, the bigger VER's speed advantage.
  • Red regions (below zero) are sections where NOR is faster. The deeper the red area, the bigger NOR's advantage.
  • Wide blue or red blocks on the straights suggest a difference in aero efficiency or engine power. Narrow spikes at corner entry or exit suggest differences in driving style or car setup.
  • Check the Command Window output below the chart. You should see a summary showing the percentage of the lap where each driver is faster, along with maximum advantage values in km/h.
  • Verify the saved image by typing ls *.png in the Command Window. You should see speed_delta.png listed alongside your earlier plots.

Seeing an error when you run the script?

  • If you see error: 'speed_delta' undefined, make sure you saved the file as speed_delta.m (not speed_delta.m.m or another name) inside the f1_telemetry folder.
  • If you see error: csvread: unable to open file, your working directory may have changed. Type pwd to check. If it does not end in f1_telemetry, type cd f1_telemetry and try again.
  • If the chart appears but looks wrong (no filled areas, just lines), double check that your fill_x line uses flipud(dist) to reverse the distance vector. Without the flip, the polygon does not close properly.

Help me debug my speed delta script in GNU Octave.

Why is the delta chart so powerful?

A speed trace overlay shows you that two drivers have different speeds, but a delta chart shows you exactly how much and where. F1 performance engineers use delta charts to decide whether to change aerodynamic setup (wing angles, ride height) or advise the driver on a different approach to specific corners.

The fill() technique you just learned is the Octave equivalent of MATLAB's area plots. You will use the same approach in university for visualizing any two-signal comparison.

Secret mission

Braking Point Analysis

You have learned to visualize telemetry data. Now take it a step further. Build a braking point detector that identifies exactly where each driver hits the brakes before every corner. This is real pattern detection. F1 performance engineers use this same technique to compare driving styles.

Before you go

Clean Up Your Resources

This project runs entirely on your local machine, so there are no ongoing cloud costs. Decide whether to keep your resources as they are, pause by closing Octave, or delete everything.

Resources you used:

  • GNU Octave 11.1.0 installation on Windows.
  • Project folder f1_telemetry containing all scripts and data.
  • Script files: generate_data.m, speed_trace.m, dashboard.m, speed_delta.m.
  • Generated data files: driver_ver.csv, driver_nor.csv.
  • Exported images: speed_trace.png, dashboard.png, speed_delta.png.

Keep everything running

No action needed. Choose this if you plan to keep experimenting with the telemetry scripts or extend the project further.

  • Your GNU Octave installation stays on your machine and is ready to use anytime.
  • Your f1_telemetry folder and all scripts remain where you created them.

Pause - I'll come back to this later

Close GNU Octave to free up memory. Your files stay on disk and are ready when you come back.

  • Close the GNU Octave window.
  • Your f1_telemetry folder, scripts, CSV files, and PNG images all remain on your machine.
  • To resume, reopen GNU Octave and run cd("f1_telemetry") to return to your project folder.

Delete - I don't want to use this again

Remove all project resources and uninstall GNU Octave to start fresh.

  • Delete the f1_telemetry folder from your computer. This removes all scripts, CSV data files, and exported PNG images.
  • To uninstall GNU Octave, press the Windows key and search for Add or remove programs.
  • Find GNU Octave in the list, click it, and select Uninstall.
  • Follow the uninstaller prompts to remove Octave completely. This frees up approximately 1.5 to 2.5 GB of disk space.

Mission accomplished

That's a Wrap!

Great work, future aero engineer. You just built your first telemetry analysis toolkit in GNU Octave.

The syntax you learned here transfers directly to MATLAB when you get access at university. These are foundational skills every F1 performance engineer uses.

You've learned how to:

  • Load and inspect F1 telemetry data from CSV files using matrix operations, column extraction, and basic statistics in GNU Octave.
  • Build a speed trace overlay and a multi-panel telemetry dashboard matching the layout used on F1 pit walls.
  • Create a speed delta chart that pinpoints exactly where on track each driver gains or loses time.
  • Secret Mission: Built a braking point detector that identifies exactly where each driver brakes into corners and compares who brakes later.

Ready to quiz yourself?