Codemasters F1 201x (lastest incarnation is F1 2012) does not come with utilities that allow you analyse your telemetry. This is disappointing to a many long time F1 game players. Even Geoff Crammands’ Grand Prix series came with the ability to analyse many different data points, including: wheelspin at each of the four corners of the car, speed, brake input, throttle input, gear, time, ground clearance, and more. I think Codemasters response when asked why this was not included was because it was a more advanced feature and wouldn’t be popular with the majority of the gamers (the game is available on PC, Xbox, and PS3).
Fortunately for those who are interested in really “Living the life”, Codemasters has exposed a telemetry data point via UDP. By creating a UDP ‘server’ (though it’s only a UDP endpoint) the game can connect to, we can receive the telemetry published by F1 2012. I recently spent a bit of time analysing this stream to help with building my realtime telemetry app for the F1 2012 game ( see here )
Before receiving the data we need to reconfigure F1 2012 to point to our end point. This is done by modifying the “hardware_settings_config.xml” file found in your My Documents folder subpath ./My Games/FormulaOne2012/hardwaresettings/. Open the file and change the line
to be
You can set ip and port to the correct values for wherever your UDP end point will be listening for a connection.
The packet F1 2011 puts out through this contains the following fields in order. All fields are float datatype (in C#)
Field |
Description |
Lap |
The number of laps since the session began.
Restarting a Session in the game will set this back to 0.
|
Time |
Time in seconds since this telemetry session began
Restarting a Session in the game will set this back to 0.
|
LapTime |
Time in seconds since the lap began. F1 2012 has a capture/send rate of 60 telemetry packets per second. This is 0 for an 'out lap' |
LapDistance |
The distance in meters since the lap began. This can be negative for 'out' laps. If you cross the start finish line and then reverse back over it the LapDistance will shoot back up buytbut the Lap will stay the same |
Distance |
The laps and percent of laps covered since the session began.
The value is negative when you first enter a practice session. After you cross the start finish line for the first time it becomes positive (0). A value of 1.5 would indicate you're halfway through your second lap. 1.995 would indicate you're almost finished your second lap.
|
X |
The X position of the car in the world |
Y |
The Y position of the car in the world |
Z |
The Z position of the car in the world |
Speed |
The car speed at this point in time. To get km/hr, multiply this value by 3.6 (I’m not sure why codemasters uses this scale). |
XV |
|
YV |
|
ZV |
|
XR |
|
YR |
|
ZR |
|
XD |
|
YD |
|
ZD |
|
SuspensionPositionRearLeft |
The position of the rear left suspension |
SuspensionPositionRearRight |
The position of the rear right suspension |
SuspensionPositionFrontLeft |
The position of the front left suspension |
SuspensionPositionFrontRight |
The position of the front right suspension |
SuspensionVelocityRearLeft |
The speed of travel of the rear left suspension |
SuspensionVelocityRearRight |
The speed of travel of the rear right suspension |
SuspensionVelocityFrontLeft |
The speed of travel of the front left suspension |
SuspensionVelocityFrontRight |
The speed of travel of the front right suspension |
WheelSpeedRearLeft |
The speed the rear left wheel is travelling. I assume this value with Speed subtracted gives wheelspin. Although it’s a good 6 km/hr faster than the front, if that’s the case! |
WheelSpeedRearRight |
The speed the rear right wheel is travelling. |
WheelSpeedFrontLeft |
The speed the front left wheel is travelling |
WheelSpeedFrontRight |
The speed the front right wheel is travelling |
Throttle |
The percent of throttle applied. 1 = full throttle and 0 = no throttle |
Steer |
The amount of left/right (-/+) input into steering. 0 = straight ahead. |
Brake |
The percent of brake applied. 1 = full brake and 0 = no brake |
Clutch |
This value will always be 0 |
Gear |
The current gear |
GForceLatitudinal |
The amount of gforces being generated due to turning |
GForceLongitudinal |
The amount of gforces being generated due to acceleration or braking |
Lap |
The current Lap. 0 = first lap across the start/finish line. |
EngineRevs |
The RPM of the engine. This value needs to be multiplied by 10 to get the real RPM, so it would seen. |
Detecting new laps
From what I can tell, the following can be used to determine which lap the driver is on.
Action |
Details |
Sitting in the Pits |
When the driver is sitting in the pits LapTime is 0, Speed is 0. |
Out Laps |
Out laps, that is laps where the driver has exited the pits but not yet crossed the start/finish line on the race track have a LapTime value of 0 and a negative Distance value or a Distance value of less than 1. The value is the fraction of a lap you have left before you start your first timed lap. As you leave it'll be around -0.95 (95% to go) and as you're about to cross the start finish line it'll be -0.0001 (.01% to go) approx. If you've crossed the start/finish line once, the value starts at 0 and gets larger as you progress through the lap, toward a value of 1 (which is the start of the next lap).
On your very first of the session the Lap value will be the same as the next lap so you cannot use only changes in the Lap value to detect new laps.
|
Return to Pits |
When you return to the pits after being partway through a lap your Lap value will not change, so you need to use some logic to recognise a return to pits. LapTime = 0 is a good one. |
Packet frequency |
The first packet received from F1 2012 after the car has crossed the start/finish line will be within 1/60th of a second, which is the rate at which F1 2011 publishes data. |
Reversing back across the start finish line |
LapDistance will vary depending on how much you move around during your lap. If you cross the line it'll be set back to 0. If you then reverse back across the finish line it'll jump to a large number. |
The Source
To read the data from UDP point in C# you can use the following
// This method runs continously in the data collection thread. It
// waits to receive UDP packets from the game, converts them and writes
// them to the shared struct variable.
private void FetchData()
{
while (true)
{
// Get the data (this will block until we get a packet)
Byte[] receiveBytes = udpSocket.Receive(ref senderIP);
// Lock access to the shared struct
syncMutex.WaitOne();
// Convert the bytes received to the shared struct
latestData = PacketUtilities.ConvertToPacket(receiveBytes);
manager.AddPacket(latestData);
// Release the lock again
syncMutex.ReleaseMutex();
}
}
public static class PacketUtilities
{
// Helper method to convert the bytes received from the UDP packet into the
// struct variable format.
public static TelemetryPacket ConvertToPacket(byte[] bytes)
{
// Marshal the byte array into the telemetryPacket structure
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
var stuff = (TelemetryPacket)Marshal.PtrToStructure(
handle.AddrOfPinnedObject(), typeof(TelemetryPacket));
handle.Free();
return stuff;
}
}
My TelemetryPacket class is the following. For my particular application (F1Speed) I only need to store certain fields.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Xml.Serialization;
namespace F1Speed.Core
{
[Serializable]
public struct TelemetryPacket : ISerializable
{
public TelemetryPacket(SerializationInfo info, StreamingContext context)
{
Time = info.GetValue<float>("Time");
LapTime = info.GetValue<float>("LapTime");
LapDistance = info.GetValue<float>("LapDistance");
Distance = info.GetValue<float>("Distance");
Speed = info.GetValue<float>("Speed");
Lap = info.GetValue<float>("Lap");
X = 0;
Y = 0;
Z = 0;
XV = 0;
YV = 0;
ZV = 0;
XR = 0;
YR = 0;
ZR = 0;
XD = 0;
YD = 0;
ZD = 0;
SuspensionPositionRearLeft = 0;
SuspensionPositionRearRight = 0;
SuspensionPositionFrontLeft = 0;
SuspensionPositionFrontRight = 0;
SuspensionVelocitoyRearLeft = 0;
SuspensionVelocitoyRearRight = 0;
SuspensionVelocitoyFrontLeft = 0;
SuspensionVelocitoyFrontRight = 0;
WheelSpeedBackLeft = 0;
WheelSpeedBackRight = 0;
WheelSpeedFrontLeft = 0;
WheelSpeedFrontRight = 0;
Throttle = 0;
Steer = 0;
Brake = 0;
Clutch = 0;
Gear = 0;
GForceLatitudinal = 0;
GForceLongitudinal = 0;
EngineRevs = 0;
}
public float Time;
public float LapTime;
public float LapDistance;
public float Distance;
[XmlIgnore]
public float X;
[XmlIgnore]
public float Y;
[XmlIgnore]
public float Z;
public float Speed;
[XmlIgnore]
public float XV;
[XmlIgnore]
public float YV;
[XmlIgnore]
public float ZV;
[XmlIgnore]
public float XR;
[XmlIgnore]
public float YR;
[XmlIgnore]
public float ZR;
[XmlIgnore]
public float XD;
[XmlIgnore]
public float YD;
[XmlIgnore]
public float ZD;
[XmlIgnore]
public float SuspensionPositionRearLeft;
[XmlIgnore]
public float SuspensionPositionRearRight;
[XmlIgnore]
public float SuspensionPositionFrontLeft;
[XmlIgnore]
public float SuspensionPositionFrontRight;
[XmlIgnore]
public float SuspensionVelocitoyRearLeft;
[XmlIgnore]
public float SuspensionVelocitoyRearRight;
[XmlIgnore]
public float SuspensionVelocitoyFrontLeft;
[XmlIgnore]
public float SuspensionVelocitoyFrontRight;
[XmlIgnore]
public float WheelSpeedBackLeft;
[XmlIgnore]
public float WheelSpeedBackRight;
[XmlIgnore]
public float WheelSpeedFrontLeft;
[XmlIgnore]
public float WheelSpeedFrontRight;
[XmlIgnore]
public float Throttle;
[XmlIgnore]
public float Steer;
[XmlIgnore]
public float Brake;
[XmlIgnore]
public float Clutch;
[XmlIgnore]
public float Gear;
[XmlIgnore]
public float GForceLatitudinal;
[XmlIgnore]
public float GForceLongitudinal;
public float Lap;
[XmlIgnore]
public float EngineRevs;
[XmlIgnore]
public float SpeedInKmPerHour
{
get { return Speed*3.60f; }
}
public override string ToString()
{
return "Lap: " + Lap + ", " +
"Time: " + Time + ", " +
"LapTime: " + LapTime + ", " +
"LapDistance: " + LapDistance + ", " +
"Distance: " + Distance + ", " +
"Speed: " + Speed;
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Time", Time);
info.AddValue("LapTime", LapTime);
info.AddValue("LapDistance", LapDistance);
info.AddValue("Distance", Distance);
info.AddValue("Speed", Speed);
info.AddValue("Lap", Lap);
}
}
}
The current project source for F1Speed is available on bitbucket https://bitbucket.org/robgray/f1speed/ If you want the latest source, always consult bitbucket.