示例#1
0
    void DrawTractionCircles(ForzaPacket packet, int packetIndex = -1)
    {
        FLCircle.radius = new Vector2(packet.TireCombinedSlipFrontLeft, packet.TireCombinedSlipFrontLeft) / 2f;
        FRCircle.radius = new Vector2(packet.TireCombinedSlipFrontRight, packet.TireCombinedSlipFrontRight) / 2f;
        RLCircle.radius = new Vector2(packet.TireCombinedSlipRearLeft, packet.TireCombinedSlipRearLeft) / 2f;
        RRCircle.radius = new Vector2(packet.TireCombinedSlipRearRight, packet.TireCombinedSlipRearRight) / 2f;

        FLCircle.UpdateEllipse();
        FRCircle.UpdateEllipse();
        RLCircle.UpdateEllipse();
        RRCircle.UpdateEllipse();

        FLPointer.SetPosition(1, new Vector3(packet.TireSlipAngleFrontLeft, packet.TireSlipRatioFrontLeft, 0) / 2f);
        FRPointer.SetPosition(1, new Vector3(packet.TireSlipAngleFrontRight, packet.TireSlipRatioFrontRight, 0) / 2f);
        RLPointer.SetPosition(1, new Vector3(packet.TireSlipAngleRearLeft, packet.TireSlipRatioRearLeft, 0) / 2f);
        RRPointer.SetPosition(1, new Vector3(packet.TireSlipAngleRearRight, packet.TireSlipRatioRearRight, 0) / 2f);

        if (ShowTractionCircleHistory && packetIndex > -1)
        {
            if (packetIndex % TractionCircleHistoryDensity == 0) // Run every n-th time
            {
                DrawTractionCircleHistory(packetIndex);
            }
        }
    }
示例#2
0
 void DrawTireSuspensionTravel(ForzaPacket packet)
 {
     FLTire.transform.localPosition = new Vector3(FLTire.transform.localPosition.x, 3.2f + packet.NormalizedSuspensionTravelFrontLeft * 0.4f, FLTire.transform.localPosition.z);
     FRTire.transform.localPosition = new Vector3(FRTire.transform.localPosition.x, 3.2f + packet.NormalizedSuspensionTravelFrontRight * 0.4f, FRTire.transform.localPosition.z);
     RLTire.transform.localPosition = new Vector3(RLTire.transform.localPosition.x, 3.2f + packet.NormalizedSuspensionTravelRearLeft * 0.4f, RLTire.transform.localPosition.z);
     RRTire.transform.localPosition = new Vector3(RRTire.transform.localPosition.x, 3.2f + packet.NormalizedSuspensionTravelRearRight * 0.4f, RRTire.transform.localPosition.z);
 }
示例#3
0
    void DrawVisualizations(ForzaPacket packet)
    {
        currentTrackingLapNum = (int)packet.LapNum;

        if (onCarVizType == OnCarVizType.SuspensionTravel)
        {
            FLGraph.AddPoint(1f - packet.NormalizedSuspensionTravelFrontLeft);
            FRGraph.AddPoint(1f - packet.NormalizedSuspensionTravelFrontRight);
            RLGraph.AddPoint(1f - packet.NormalizedSuspensionTravelRearLeft);
            RRGraph.AddPoint(1f - packet.NormalizedSuspensionTravelRearRight);
        }
        else if (onCarVizType == OnCarVizType.TractionCircle)
        {
            DrawTractionCircles(packet);
        }

        DrawTireSuspensionTravel(packet);

        FLUiGraph.AddPoint(1f - packet.NormalizedSuspensionTravelFrontLeft);
        FRUiGraph.AddPoint(1f - packet.NormalizedSuspensionTravelFrontRight);
        RLUiGraph.AddPoint(1f - packet.NormalizedSuspensionTravelRearLeft);
        RRUiGraph.AddPoint(1f - packet.NormalizedSuspensionTravelRearRight);

        GhostPositionViz();
    }
示例#4
0
 // Update is called once per frame
 public void DrawUI(ForzaPacket packet)
 {
     if (mainCamera.IsFollowing())
     {
         speed.text = Mathf.RoundToInt(packet.VelocityZ * 2.237f).ToString();
     }
 }
示例#5
0
    public static DataPoint AddPoint(ForzaPacket packet)
    {
        if (lastTimestamp == 0)
        {
            lastPosition  = new Vector3(0, 0, 0);
            lastTimestamp = packet.TimestampMS - 16; // 16 ms represents one frame at 60 fps, which is the rate FM7 feeds the datastream at
        }

        frameTick = (packet.TimestampMS - lastTimestamp) / 1000f;

        Quaternion newRotation = Quaternion.Euler(Mathf.Rad2Deg * packet.Pitch, Mathf.Rad2Deg * packet.Yaw, Mathf.Rad2Deg * packet.Roll);
        Vector3    newPosition = lastPosition;

        // Keeping as reference. Results in floating point rounding issues
        //newPosition += newRotation * new Vector3(packet.VelocityX * frameTick, packet.VelocityX * frameTick, packet.VelocityZ * frameTick);

        newPosition += VectorRotation(newRotation, packet.VelocityX, packet.VelocityY, packet.VelocityZ) * frameTick;

        DataPoint newPoint = new DataPoint(packet, newPosition, newRotation);

        allPoints.Add(newPoint);

        timestampTotal += packet.TimestampMS - lastTimestamp;
        DebugConsole.Write("Packet frame time average: " + (timestampTotal / allPoints.Count));

        lastPosition  = newPosition;
        lastTimestamp = packet.TimestampMS;

        return(newPoint);
    }
示例#6
0
    void GForceViz(ForzaPacket packet, GameObject go)
    {
        Transform arrow = go.transform.GetChild(0);

        Vector3 accelVector = new Vector3(packet.AccelerationX, packet.AccelerationY, packet.AccelerationZ);

        arrow.transform.forward = accelVector;

        float gforce = accelVector.magnitude / 9.80665f;

        MaterialPropertyBlock props = new MaterialPropertyBlock();

        props.SetColor("_Color", gForceGradient.Evaluate(gforce));
        arrow.GetComponent <MeshRenderer>().SetPropertyBlock(props);

        Vector3 scaleArrow = arrow.transform.localScale;

        scaleArrow.z *= gforce;
        arrow.transform.localScale = scaleArrow;
    }
示例#7
0
    public void DrawVisualizationsAtIndex(int packetIndex)
    {
        ForzaPacket packet = DataPoints.GetPoint(packetIndex).GetPacket();

        currentTrackingLapNum = (int)packet.LapNum;

        List <float> FLSuspensionGraphPoints = new List <float>();
        List <float> FRSuspensionGraphPoints = new List <float>();
        List <float> RLSuspensionGraphPoints = new List <float>();
        List <float> RRSuspensionGraphPoints = new List <float>();

        /*
         * if (packetIndex > 0)
         *  Debug.Log("\nLine trail mesh vertices: " + lineMeshes[packetIndex].vertices[0] + ", " + lineMeshes[packetIndex].vertices[1] + ", " + lineMeshes[packetIndex].vertices[2] + ", " + lineMeshes[packetIndex].vertices[3]
         + " | Distance between points: " + Vector3.Distance(dataRoot.GetChild(packetIndex).position, dataRoot.GetChild(packetIndex - 1).position)
         + " | Difference between packet timestamps: " + (DataPoints.GetPoint(packetIndex).TimestampMS - DataPoints.GetPoint(packetIndex - 1).TimestampMS));
         */
        for (int i = packetIndex; i > packetIndex - suspensionGraphVisiblePoints; i--)
        {
            if (!DataPoints.IsValidIndex(i))
            {
                continue;
            }

            packet = DataPoints.GetPoint(i).GetPacket();

            if (i >= 0)
            {
                FLSuspensionGraphPoints.Add(1f - packet.NormalizedSuspensionTravelFrontLeft);
                FRSuspensionGraphPoints.Add(1f - packet.NormalizedSuspensionTravelFrontRight);
                RLSuspensionGraphPoints.Add(1f - packet.NormalizedSuspensionTravelRearLeft);
                RRSuspensionGraphPoints.Add(1f - packet.NormalizedSuspensionTravelRearRight);
            }
            else
            {
                FLSuspensionGraphPoints.Add(0);
                FRSuspensionGraphPoints.Add(0);
                RLSuspensionGraphPoints.Add(0);
                RRSuspensionGraphPoints.Add(0);
            }
        }

        if (onCarVizType == OnCarVizType.SuspensionTravel)
        {
            FLGraph.AddPoints(FLSuspensionGraphPoints);
            FRGraph.AddPoints(FRSuspensionGraphPoints);
            RLGraph.AddPoints(RLSuspensionGraphPoints);
            RRGraph.AddPoints(RRSuspensionGraphPoints);
        }
        else if (onCarVizType == OnCarVizType.TractionCircle)
        {
            packet = DataPoints.GetPoint(packetIndex).GetPacket();

            DrawTractionCircles(packet, packetIndex);
        }

        packet = DataPoints.GetPoint(packetIndex).GetPacket();

        DrawTireSuspensionTravel(packet);

        FLUiGraph.AddPoints(FLSuspensionGraphPoints);
        FRUiGraph.AddPoints(FRSuspensionGraphPoints);
        RLUiGraph.AddPoints(RLSuspensionGraphPoints);
        RRUiGraph.AddPoints(RRSuspensionGraphPoints);
    }
示例#8
0
    public static void SaveCSV(string path)
    {
        if (allPoints.Count == 0)
        {
            return;
        }

        StreamWriter file = new StreamWriter(path, false);

        foreach (DataPoint datapoint in allPoints)
        {
            ForzaPacket p = datapoint.GetPacket();

            file.WriteLine(
                p.IsRaceOn + "," +
                p.TimestampMS + "," +
                p.EngineMaxRpm + "," +
                p.EngineIdleRpm + "," +
                p.CurrentEngineRpm + "," +
                p.AccelerationX + "," +
                p.AccelerationY + "," +
                p.AccelerationZ + "," +
                p.VelocityX + "," +
                p.VelocityY + "," +
                p.VelocityZ + "," +
                p.AngularVelocityX + "," +
                p.AngularVelocityY + "," +
                p.AngularVelocityZ + "," +
                p.Yaw + "," +
                p.Pitch + "," +
                p.Roll + "," +
                p.NormalizedSuspensionTravelFrontLeft + "," +
                p.NormalizedSuspensionTravelFrontRight + "," +
                p.NormalizedSuspensionTravelRearLeft + "," +
                p.NormalizedSuspensionTravelRearRight + "," +
                p.TireSlipRatioFrontLeft + "," +
                p.TireSlipRatioFrontRight + "," +
                p.TireSlipRatioRearLeft + "," +
                p.TireSlipRatioRearRight + "," +
                p.WheelRotationSpeedFrontLeft + "," +
                p.WheelRotationSpeedFrontRight + "," +
                p.WheelRotationSpeedRearLeft + "," +
                p.WheelRotationSpeedRearRight + "," +
                p.WheelOnRumbleStripFrontLeft + "," +
                p.WheelOnRumbleStripFrontRight + "," +
                p.WheelOnRumbleStripRearLeft + "," +
                p.WheelOnRumbleStripRearRight + "," +
                p.WheelInPuddleDepthFrontLeft + "," +
                p.WheelInPuddleDepthFrontRight + "," +
                p.WheelInPuddleDepthRearLeft + "," +
                p.WheelInPuddleDepthRearRight + "," +
                p.SurfaceRumbleFrontLeft + "," +
                p.SurfaceRumbleFrontRight + "," +
                p.SurfaceRumbleRearLeft + "," +
                p.SurfaceRumbleRearRight + "," +
                p.TireSlipAngleFrontLeft + "," +
                p.TireSlipAngleFrontRight + "," +
                p.TireSlipAngleRearLeft + "," +
                p.TireSlipAngleRearRight + "," +
                p.TireCombinedSlipFrontLeft + "," +
                p.TireCombinedSlipFrontRight + "," +
                p.TireCombinedSlipRearLeft + "," +
                p.TireCombinedSlipRearRight + "," +
                p.SuspensionTravelMetersFrontLeft + "," +
                p.SuspensionTravelMetersFrontRight + "," +
                p.SuspensionTravelMetersRearLeft + "," +
                p.SuspensionTravelMetersRearRight + "," +
                p.CarOrdinal + "," +
                p.CarClass + "," +
                p.CarPerformanceIndex + "," +
                p.DrivetrainType + "," +
                p.NumCylinders
                );
        }

        file.Close();
    }
示例#9
0
    public void DrawUIAtIndex(int packetIndex)
    {
        ForzaPacket packet = DataPoints.GetPoint(packetIndex).GetPacket();

        speed.text = Mathf.RoundToInt(packet.VelocityZ * 2.237f).ToString();
    }
示例#10
0
 public DataPoint(ForzaPacket packet, Vector3 position, Quaternion rotation)
 {
     this.packet   = packet;
     this.position = position;
     this.rotation = rotation;
 }
示例#11
0
    void ListenPackets()
    {
        receivingUdpClient = new UdpClient(listenPort);
        IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);

        bool recordRace  = true;
        bool raceStarted = false;

        Debug.Log("Starting UDP listener on port " + listenPort);

        try
        {
            while (recordRace)
            {
                // Blocks until a message returns on this socket from a remote host.
                Byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);

                int index = 0;

                ForzaPacket packet = new ForzaPacket
                {
                    IsRaceOn = BitConverter.ToInt32(receiveBytes, index),          // = 1 when race is on. = 0 when in menus/race stopped …

                    TimestampMS = BitConverter.ToUInt32(receiveBytes, index += 4), //Can overflow to 0 eventually

                    EngineMaxRpm     = BitConverter.ToSingle(receiveBytes, index += 4),
                    EngineIdleRpm    = BitConverter.ToSingle(receiveBytes, index += 4),
                    CurrentEngineRpm = BitConverter.ToSingle(receiveBytes, index += 4),

                    AccelerationX = BitConverter.ToSingle(receiveBytes, index += 4), //In the car's local space; X = right, Y = up, Z = forward
                    AccelerationY = BitConverter.ToSingle(receiveBytes, index += 4),
                    AccelerationZ = BitConverter.ToSingle(receiveBytes, index += 4),

                    VelocityX = BitConverter.ToSingle(receiveBytes, index += 4), //In the car's local space; X = right, Y = up, Z = forward
                    VelocityY = BitConverter.ToSingle(receiveBytes, index += 4),
                    VelocityZ = BitConverter.ToSingle(receiveBytes, index += 4),

                    AngularVelocityX = BitConverter.ToSingle(receiveBytes, index += 4), //In the car's local space; X = pitch, Y = yaw, Z = roll
                    AngularVelocityY = BitConverter.ToSingle(receiveBytes, index += 4),
                    AngularVelocityZ = BitConverter.ToSingle(receiveBytes, index += 4),

                    Yaw   = BitConverter.ToSingle(receiveBytes, index += 4),
                    Pitch = BitConverter.ToSingle(receiveBytes, index += 4),
                    Roll  = BitConverter.ToSingle(receiveBytes, index += 4),

                    NormalizedSuspensionTravelFrontLeft  = BitConverter.ToSingle(receiveBytes, index += 4), // Suspension travel normalized: 0.0f = max stretch; 1.0 = max compression
                    NormalizedSuspensionTravelFrontRight = BitConverter.ToSingle(receiveBytes, index += 4),
                    NormalizedSuspensionTravelRearLeft   = BitConverter.ToSingle(receiveBytes, index += 4),
                    NormalizedSuspensionTravelRearRight  = BitConverter.ToSingle(receiveBytes, index += 4),

                    TireSlipRatioFrontLeft  = BitConverter.ToSingle(receiveBytes, index += 4), // Tire normalized slip ratio, = 0 means 100% grip and |ratio| > 1.0 means loss of grip.
                    TireSlipRatioFrontRight = BitConverter.ToSingle(receiveBytes, index += 4),
                    TireSlipRatioRearLeft   = BitConverter.ToSingle(receiveBytes, index += 4),
                    TireSlipRatioRearRight  = BitConverter.ToSingle(receiveBytes, index += 4),

                    WheelRotationSpeedFrontLeft  = BitConverter.ToSingle(receiveBytes, index += 4), // Wheel rotation speed radians/sec.
                    WheelRotationSpeedFrontRight = BitConverter.ToSingle(receiveBytes, index += 4),
                    WheelRotationSpeedRearLeft   = BitConverter.ToSingle(receiveBytes, index += 4),
                    WheelRotationSpeedRearRight  = BitConverter.ToSingle(receiveBytes, index += 4),

                    WheelOnRumbleStripFrontLeft  = BitConverter.ToInt32(receiveBytes, index += 4), // = 1 when wheel is on rumble strip, = 0 when off.
                    WheelOnRumbleStripFrontRight = BitConverter.ToInt32(receiveBytes, index += 4),
                    WheelOnRumbleStripRearLeft   = BitConverter.ToInt32(receiveBytes, index += 4),
                    WheelOnRumbleStripRearRight  = BitConverter.ToInt32(receiveBytes, index += 4),

                    WheelInPuddleDepthFrontLeft  = BitConverter.ToSingle(receiveBytes, index += 4), // = from 0 to 1, where 1 is the deepest puddle
                    WheelInPuddleDepthFrontRight = BitConverter.ToSingle(receiveBytes, index += 4),
                    WheelInPuddleDepthRearLeft   = BitConverter.ToSingle(receiveBytes, index += 4),
                    WheelInPuddleDepthRearRight  = BitConverter.ToSingle(receiveBytes, index += 4),

                    SurfaceRumbleFrontLeft  = BitConverter.ToSingle(receiveBytes, index += 4), // Non-dimensional surface rumble values passed to controller force feedback
                    SurfaceRumbleFrontRight = BitConverter.ToSingle(receiveBytes, index += 4),
                    SurfaceRumbleRearLeft   = BitConverter.ToSingle(receiveBytes, index += 4),
                    SurfaceRumbleRearRight  = BitConverter.ToSingle(receiveBytes, index += 4),

                    TireSlipAngleFrontLeft  = BitConverter.ToSingle(receiveBytes, index += 4), // Tire normalized slip angle, = 0 means 100% grip and |angle| > 1.0 means loss of grip.
                    TireSlipAngleFrontRight = BitConverter.ToSingle(receiveBytes, index += 4),
                    TireSlipAngleRearLeft   = BitConverter.ToSingle(receiveBytes, index += 4),
                    TireSlipAngleRearRight  = BitConverter.ToSingle(receiveBytes, index += 4),

                    TireCombinedSlipFrontLeft  = BitConverter.ToSingle(receiveBytes, index += 4), // Tire normalized combined slip, = 0 means 100% grip and |slip| > 1.0 means loss of grip.
                    TireCombinedSlipFrontRight = BitConverter.ToSingle(receiveBytes, index += 4),
                    TireCombinedSlipRearLeft   = BitConverter.ToSingle(receiveBytes, index += 4),
                    TireCombinedSlipRearRight  = BitConverter.ToSingle(receiveBytes, index += 4),

                    SuspensionTravelMetersFrontLeft  = BitConverter.ToSingle(receiveBytes, index += 4), // Actual suspension travel in meters
                    SuspensionTravelMetersFrontRight = BitConverter.ToSingle(receiveBytes, index += 4),
                    SuspensionTravelMetersRearLeft   = BitConverter.ToSingle(receiveBytes, index += 4),
                    SuspensionTravelMetersRearRight  = BitConverter.ToSingle(receiveBytes, index += 4),

                    CarOrdinal          = BitConverter.ToInt32(receiveBytes, index += 4), //Unique ID of the car make/model
                    CarClass            = BitConverter.ToInt32(receiveBytes, index += 4), //Between 0 (D -- worst cars) and 7 (X class -- best cars) inclusive
                    CarPerformanceIndex = BitConverter.ToInt32(receiveBytes, index += 4), //Between 100 (slowest car) and 999 (fastest car) inclusive
                    DrivetrainType      = BitConverter.ToInt32(receiveBytes, index += 4), //Corresponds to EDrivetrainType; 0 = FWD, 1 = RWD, 2 = AWD
                    NumCylinders        = BitConverter.ToInt32(receiveBytes, index += 4)  //Number of cylinders in the engine
                };

                if (packet.TimestampMS == lastTimestamp)
                {
                    Debug.Log("Same packet received, dropping.");
                    continue;
                }
                else
                {
                    lastTimestamp = packet.TimestampMS;
                }

                packetCount++;

                if (packet.IsRaceOn == 1)
                {
                    raceStarted = true;

                    packetQueue.Enqueue(packet);
                }

                if (raceStarted && packet.IsRaceOn == 0)
                {
                    recordRace = false; // Stop recording after one race
                }
            }
        }
        catch (Exception e)
        {
            Debug.Log(e.ToString());
        }

        receivingUdpClient.Close();
        Debug.Log("UDP listener stopped. Final packet count: " + packetCount);
    }
示例#12
0
    public void LoadCSV()
    {
        receivingUdpClient.Close();

        string path = FileBrowser.OpenSingleFile("Open CSV", lastSavePath, "csv");

        if (path != "")
        {
            Debug.Log("Reading packet info from " + path);

            visualizations.ResetVisualizations();
            uiVisualizations.ResetUIVisualizations();

            using (StreamReader reader = new StreamReader(path))
            {
                while (!reader.EndOfStream)
                {
                    string   line   = reader.ReadLine();
                    string[] values = line.Split(',');

                    ForzaPacket p = new ForzaPacket
                    {
                        IsRaceOn         = int.Parse(values[0]),
                        TimestampMS      = uint.Parse(values[1]),
                        EngineMaxRpm     = float.Parse(values[2]),
                        EngineIdleRpm    = float.Parse(values[3]),
                        CurrentEngineRpm = float.Parse(values[4]),
                        AccelerationX    = float.Parse(values[5]),
                        AccelerationY    = float.Parse(values[6]),
                        AccelerationZ    = float.Parse(values[7]),
                        VelocityX        = float.Parse(values[8]),
                        VelocityY        = float.Parse(values[9]),
                        VelocityZ        = float.Parse(values[10]),
                        AngularVelocityX = float.Parse(values[11]),
                        AngularVelocityY = float.Parse(values[12]),
                        AngularVelocityZ = float.Parse(values[13]),
                        Yaw   = float.Parse(values[14]),
                        Pitch = float.Parse(values[15]),
                        Roll  = float.Parse(values[16]),
                        NormalizedSuspensionTravelFrontLeft  = float.Parse(values[17]),
                        NormalizedSuspensionTravelFrontRight = float.Parse(values[18]),
                        NormalizedSuspensionTravelRearLeft   = float.Parse(values[19]),
                        NormalizedSuspensionTravelRearRight  = float.Parse(values[20]),
                        TireSlipRatioFrontLeft           = float.Parse(values[21]),
                        TireSlipRatioFrontRight          = float.Parse(values[22]),
                        TireSlipRatioRearLeft            = float.Parse(values[23]),
                        TireSlipRatioRearRight           = float.Parse(values[24]),
                        WheelRotationSpeedFrontLeft      = float.Parse(values[25]),
                        WheelRotationSpeedFrontRight     = float.Parse(values[26]),
                        WheelRotationSpeedRearLeft       = float.Parse(values[27]),
                        WheelRotationSpeedRearRight      = float.Parse(values[28]),
                        WheelOnRumbleStripFrontLeft      = int.Parse(values[29]),
                        WheelOnRumbleStripFrontRight     = int.Parse(values[30]),
                        WheelOnRumbleStripRearLeft       = int.Parse(values[31]),
                        WheelOnRumbleStripRearRight      = int.Parse(values[32]),
                        WheelInPuddleDepthFrontLeft      = float.Parse(values[33]),
                        WheelInPuddleDepthFrontRight     = float.Parse(values[34]),
                        WheelInPuddleDepthRearLeft       = float.Parse(values[35]),
                        WheelInPuddleDepthRearRight      = float.Parse(values[36]),
                        SurfaceRumbleFrontLeft           = float.Parse(values[37]),
                        SurfaceRumbleFrontRight          = float.Parse(values[38]),
                        SurfaceRumbleRearLeft            = float.Parse(values[39]),
                        SurfaceRumbleRearRight           = float.Parse(values[40]),
                        TireSlipAngleFrontLeft           = float.Parse(values[41]),
                        TireSlipAngleFrontRight          = float.Parse(values[42]),
                        TireSlipAngleRearLeft            = float.Parse(values[43]),
                        TireSlipAngleRearRight           = float.Parse(values[44]),
                        TireCombinedSlipFrontLeft        = float.Parse(values[45]),
                        TireCombinedSlipFrontRight       = float.Parse(values[46]),
                        TireCombinedSlipRearLeft         = float.Parse(values[47]),
                        TireCombinedSlipRearRight        = float.Parse(values[48]),
                        SuspensionTravelMetersFrontLeft  = float.Parse(values[49]),
                        SuspensionTravelMetersFrontRight = float.Parse(values[50]),
                        SuspensionTravelMetersRearLeft   = float.Parse(values[51]),
                        SuspensionTravelMetersRearRight  = float.Parse(values[52]),
                        CarOrdinal          = int.Parse(values[53]),
                        CarClass            = int.Parse(values[54]),
                        CarPerformanceIndex = int.Parse(values[55]),
                        DrivetrainType      = int.Parse(values[56]),
                        NumCylinders        = int.Parse(values[57])
                    };

                    if (p.TimestampMS == lastTimestamp)
                    {
                        Debug.Log("Same packet received, dropping.");
                        continue;
                    }
                    else
                    {
                        lastTimestamp = p.TimestampMS;
                    }

                    DataPoint datapoint = DataPoints.AddPoint(p);

                    int lapNum = trackInfo.CheckNewLap(DataPoints.GetLatestPacketIndex());
                    p.LapNum = (uint)lapNum;

                    visualizations.DrawTrail(datapoint, lapNum, true);
                }

                reader.Close();

                Debug.Log("Read complete");
            }

            MainCamera mainCamera = Camera.main.GetComponent <MainCamera>();
            mainCamera.GoToPoint(0);
        }
    }