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); } } }
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); }
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(); }
// Update is called once per frame public void DrawUI(ForzaPacket packet) { if (mainCamera.IsFollowing()) { speed.text = Mathf.RoundToInt(packet.VelocityZ * 2.237f).ToString(); } }
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); }
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; }
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); }
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(); }
public void DrawUIAtIndex(int packetIndex) { ForzaPacket packet = DataPoints.GetPoint(packetIndex).GetPacket(); speed.text = Mathf.RoundToInt(packet.VelocityZ * 2.237f).ToString(); }
public DataPoint(ForzaPacket packet, Vector3 position, Quaternion rotation) { this.packet = packet; this.position = position; this.rotation = rotation; }
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); }
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); } }