public string PrintTelemetryToCsvContent(byte driver_index) { CsvFile csv = new CsvFile(); #region "Write headers" //Write headers DataRow dr_header = csv.AddNewRow(); //Time dr_header.Values.Add("Session Time"); //Motion packet dr_header.Values.Add("Position X"); dr_header.Values.Add("Position Y"); dr_header.Values.Add("Position Z"); dr_header.Values.Add("Velocity X"); dr_header.Values.Add("Velocity Y"); dr_header.Values.Add("Velocity Z"); dr_header.Values.Add("Forward Direction X"); dr_header.Values.Add("Forward Direction Y"); dr_header.Values.Add("Forward Direction Z"); dr_header.Values.Add("Right Direction X"); dr_header.Values.Add("Right Direction Y"); dr_header.Values.Add("Right Direction Z"); dr_header.Values.Add("Lateral G Force"); dr_header.Values.Add("Longitudinal G Force"); dr_header.Values.Add("Vertical G Force"); dr_header.Values.Add("Yaw"); dr_header.Values.Add("Pitch"); dr_header.Values.Add("Roll"); //Motion packet (only for player data, not for other drivers. Will be blank if requesting for other players) dr_header.Values.Add("Suspension Position - Rear Left"); dr_header.Values.Add("Suspension Position - Rear Right"); dr_header.Values.Add("Suspennsion Position - Front Left"); dr_header.Values.Add("Suspension Position - Front Right"); dr_header.Values.Add("Suspension Velocity - Rear Left"); dr_header.Values.Add("Suspension Velocity - Rear Right"); dr_header.Values.Add("Suspension Velocity - Front Left"); dr_header.Values.Add("Suspension Velocity - Front Right"); dr_header.Values.Add("Suspension Acceleration - Rear Left"); dr_header.Values.Add("Suspension Acceleration - Rear Right"); dr_header.Values.Add("Suspension Acceleration - Front Left"); dr_header.Values.Add("Suspension Acceleration - Front Right"); dr_header.Values.Add("Wheel Speed - Rear Left"); dr_header.Values.Add("Wheel Speed - Rear Right"); dr_header.Values.Add("Wheel Speed - Front Left"); dr_header.Values.Add("Wheel Speed - Front Right"); dr_header.Values.Add("Wheel Slip - Rear Left"); dr_header.Values.Add("Wheel Slip - Rear Right"); dr_header.Values.Add("Wheel Slip - Front Left"); dr_header.Values.Add("Wheel Slip - Front Right"); dr_header.Values.Add("Local Velocity X"); dr_header.Values.Add("Local Velocity Y"); dr_header.Values.Add("Local Velocity Z"); dr_header.Values.Add("Angular Velocity X"); dr_header.Values.Add("Angular Velocity Y"); dr_header.Values.Add("Angular Velocity Z"); dr_header.Values.Add("Angular Acceleration X"); dr_header.Values.Add("Angular Acceleration Y"); dr_header.Values.Add("Angular Acceleration Z"); dr_header.Values.Add("Front Wheels Angle"); //Lap Packet dr_header.Values.Add("Lap Distance"); dr_header.Values.Add("Total Distance"); dr_header.Values.Add("Safety Car Delta"); dr_header.Values.Add("Car Race Position"); dr_header.Values.Add("Current Lap Number"); dr_header.Values.Add("Pit Status"); dr_header.Values.Add("Sector"); dr_header.Values.Add("Current Lap Invalid"); dr_header.Values.Add("Driver Status"); //Telemetry Packet dr_header.Values.Add("Speed KPH"); dr_header.Values.Add("Speed MPH"); dr_header.Values.Add("Throttle"); dr_header.Values.Add("Steer"); dr_header.Values.Add("Brake"); dr_header.Values.Add("Clutch"); dr_header.Values.Add("Gear"); dr_header.Values.Add("Engine RPM"); dr_header.Values.Add("DRS Active"); dr_header.Values.Add("Rev Lights Percentage"); dr_header.Values.Add("Brake Temperature - Rear Left"); dr_header.Values.Add("Brake Temperature - Rear Right"); dr_header.Values.Add("Brake Temperature - Front Left"); dr_header.Values.Add("Brake Temperature - Front Right"); dr_header.Values.Add("Tyre Surface Temperature - Rear Left"); dr_header.Values.Add("Tyre Surface Temperature - Rear Right"); dr_header.Values.Add("Tyre Surface Temperature - Front Left"); dr_header.Values.Add("Tyre Surface Temperature - Front Right"); dr_header.Values.Add("Tyre Inner Temperature - Rear Left"); dr_header.Values.Add("Tyre Inner Temperature - Rear Right"); dr_header.Values.Add("Tyre Inner Temperature - Front Left"); dr_header.Values.Add("Tyre Inner Temperature - Front Right"); dr_header.Values.Add("Engine Temperature Celsius"); dr_header.Values.Add("Tyre Pressure - Rear Left"); dr_header.Values.Add("Tyre Pressure - Rear Right"); dr_header.Values.Add("Tyre Pressure - Front Left"); dr_header.Values.Add("Tyre Pressure - Front Right"); dr_header.Values.Add("Surface Type - Rear Left"); dr_header.Values.Add("Surface Type - Rear Right"); dr_header.Values.Add("Surface Type - Front Left"); dr_header.Values.Add("Surface Type - Front Right"); //Car Status dr_header.Values.Add("Traction Control Level"); dr_header.Values.Add("Anti Lock Brakes Active"); dr_header.Values.Add("Fuel Mix"); dr_header.Values.Add("Front Brake Bias Percentage"); dr_header.Values.Add("Pit Limiter Active"); dr_header.Values.Add("Fuel In Tank"); dr_header.Values.Add("Max Fuel Capacity"); dr_header.Values.Add("Fuel Remaining in Laps"); dr_header.Values.Add("Max RPM"); dr_header.Values.Add("Idle RPM"); dr_header.Values.Add("Max Gears"); dr_header.Values.Add("DRS Allowed"); dr_header.Values.Add("DRS Activation Distance"); dr_header.Values.Add("Tyre Wear - Rear Left"); dr_header.Values.Add("Tyre Wear - Rear Right"); dr_header.Values.Add("Tyre Wear - Front Left"); dr_header.Values.Add("Tyre Wear - Front Right"); dr_header.Values.Add("Actual Tyre Compound"); dr_header.Values.Add("Visual Tyre Compound"); dr_header.Values.Add("Tyre Age in Laps"); dr_header.Values.Add("Tyre Damage Percentage - Rear Left"); dr_header.Values.Add("Tyre Damage Percentage - Rear Right"); dr_header.Values.Add("Tyre Damage Percentage - Front Left"); dr_header.Values.Add("Tyre Damage Percentage - Front Right"); dr_header.Values.Add("Front Left Wing Damage Percentage"); dr_header.Values.Add("Front Right Wing Damage Percentage"); dr_header.Values.Add("Rear Wing Damage Percentage"); dr_header.Values.Add("DRS Failure"); dr_header.Values.Add("Engine Damage Percentage"); dr_header.Values.Add("Gear Box Damage Percentage"); dr_header.Values.Add("FIA Flag"); dr_header.Values.Add("ERS Stored Energy Joules"); dr_header.Values.Add("ERS Deploy Mode"); dr_header.Values.Add("ERS Harvested This Lap by MGUK"); dr_header.Values.Add("ERS Harvested This Lap by MGUH"); dr_header.Values.Add("ERS Deployed This Lap"); #endregion //Write the data foreach (PacketFrame frame in Frames) { //Create the new row DataRow row = csv.AddNewRow(); //Write the session time row.Values.Add(frame.Telemetry.SessionTime.ToString()); //Write Write motion data MotionPacket.CarMotionData cmd = frame.Motion.FieldMotionData[driver_index]; row.Values.Add(cmd.PositionX.ToString()); row.Values.Add(cmd.PositionY.ToString()); row.Values.Add(cmd.PositionZ.ToString()); row.Values.Add(cmd.VelocityX.ToString()); row.Values.Add(cmd.VelocityY.ToString()); row.Values.Add(cmd.VelocityZ.ToString()); row.Values.Add(cmd.ForwardDirectionX.ToString()); row.Values.Add(cmd.ForwardDirectionY.ToString()); row.Values.Add(cmd.ForwardDirectionZ.ToString()); row.Values.Add(cmd.RightDirectionX.ToString()); row.Values.Add(cmd.RightDirectionY.ToString()); row.Values.Add(cmd.RightDirectionZ.ToString()); row.Values.Add(cmd.gForceLateral.ToString()); row.Values.Add(cmd.gForceLongitudinal.ToString()); row.Values.Add(cmd.gForceVertical.ToString()); row.Values.Add(cmd.Yaw.ToString()); row.Values.Add(cmd.Pitch.ToString()); row.Values.Add(cmd.Roll.ToString()); //Write motion data player-specific (if they are requesting for the player, return the data. Otherwise, return 0's!) if (driver_index == frame.Motion.PlayerCarIndex) { row.Values.Add(frame.Motion.SuspensionPosition.RearLeft.ToString()); row.Values.Add(frame.Motion.SuspensionPosition.RearRight.ToString()); row.Values.Add(frame.Motion.SuspensionPosition.FrontLeft.ToString()); row.Values.Add(frame.Motion.SuspensionPosition.FrontRight.ToString()); row.Values.Add(frame.Motion.SuspensionVelocity.RearLeft.ToString()); row.Values.Add(frame.Motion.SuspensionVelocity.RearRight.ToString()); row.Values.Add(frame.Motion.SuspensionVelocity.FrontLeft.ToString()); row.Values.Add(frame.Motion.SuspensionVelocity.FrontRight.ToString()); row.Values.Add(frame.Motion.SuspensionAcceleration.RearLeft.ToString()); row.Values.Add(frame.Motion.SuspensionAcceleration.RearRight.ToString()); row.Values.Add(frame.Motion.SuspensionAcceleration.FrontLeft.ToString()); row.Values.Add(frame.Motion.SuspensionAcceleration.FrontRight.ToString()); row.Values.Add(frame.Motion.WheelSpeed.RearLeft.ToString()); row.Values.Add(frame.Motion.WheelSpeed.RearRight.ToString()); row.Values.Add(frame.Motion.WheelSpeed.FrontLeft.ToString()); row.Values.Add(frame.Motion.WheelSpeed.FrontRight.ToString()); row.Values.Add(frame.Motion.WheelSlip.RearLeft.ToString()); row.Values.Add(frame.Motion.WheelSlip.RearRight.ToString()); row.Values.Add(frame.Motion.WheelSlip.FrontLeft.ToString()); row.Values.Add(frame.Motion.WheelSlip.FrontRight.ToString()); row.Values.Add(frame.Motion.VelocityX.ToString()); row.Values.Add(frame.Motion.VelocityY.ToString()); row.Values.Add(frame.Motion.VelocityZ.ToString()); row.Values.Add(frame.Motion.AngularVelocityX.ToString()); row.Values.Add(frame.Motion.AngularVelocityY.ToString()); row.Values.Add(frame.Motion.AngularVelocityZ.ToString()); row.Values.Add(frame.Motion.AngularAccelerationX.ToString()); row.Values.Add(frame.Motion.AngularAccelerationY.ToString()); row.Values.Add(frame.Motion.AngularAccelerationZ.ToString()); row.Values.Add(frame.Motion.FrontWheelAngle.ToString()); } else { row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); row.Values.Add("-"); } //Lap Packet LapPacket.LapData ld = frame.Lap.FieldLapData[driver_index]; row.Values.Add(ld.LapDistance.ToString()); row.Values.Add(ld.TotalDistance.ToString()); row.Values.Add(ld.SafetyCarDelta.ToString()); row.Values.Add(ld.CarPosition.ToString()); row.Values.Add(ld.CurrentLapNumber.ToString()); row.Values.Add(ld.CurrentPitStatus.ToString()); row.Values.Add(ld.Sector.ToString()); row.Values.Add(ld.CurrentLapInvalid.ToString()); row.Values.Add(ld.CurrentDriverStatus.ToString()); //Telemetry packet TelemetryPacket.CarTelemetryData ctd = frame.Telemetry.FieldTelemetryData[driver_index]; row.Values.Add(ctd.SpeedKph.ToString()); row.Values.Add(ctd.SpeedMph.ToString()); row.Values.Add(ctd.Throttle.ToString()); row.Values.Add(ctd.Steer.ToString()); row.Values.Add(ctd.Brake.ToString()); row.Values.Add(ctd.Clutch.ToString()); row.Values.Add(ctd.Gear.ToString()); row.Values.Add(ctd.EngineRpm.ToString()); row.Values.Add(ctd.DrsActive.ToString()); row.Values.Add(ctd.RevLightsPercentage.ToString()); row.Values.Add(ctd.BrakeTemperature.RearLeft.ToString()); row.Values.Add(ctd.BrakeTemperature.RearRight.ToString()); row.Values.Add(ctd.BrakeTemperature.FrontLeft.ToString()); row.Values.Add(ctd.BrakeTemperature.FrontRight.ToString()); row.Values.Add(ctd.TyreSurfaceTemperature.RearLeft.ToString()); row.Values.Add(ctd.TyreSurfaceTemperature.RearRight.ToString()); row.Values.Add(ctd.TyreSurfaceTemperature.FrontLeft.ToString()); row.Values.Add(ctd.TyreSurfaceTemperature.FrontRight.ToString()); row.Values.Add(ctd.TyreInnerTemperature.RearLeft.ToString()); row.Values.Add(ctd.TyreInnerTemperature.RearRight.ToString()); row.Values.Add(ctd.TyreInnerTemperature.FrontLeft.ToString()); row.Values.Add(ctd.TyreInnerTemperature.FrontRight.ToString()); row.Values.Add(ctd.EngineTemperature.ToString()); row.Values.Add(ctd.TyrePressure.RearLeft.ToString()); row.Values.Add(ctd.TyrePressure.RearRight.ToString()); row.Values.Add(ctd.TyrePressure.FrontLeft.ToString()); row.Values.Add(ctd.TyrePressure.FrontRight.ToString()); row.Values.Add(ctd.SurfaceType_RearLeft.ToString()); row.Values.Add(ctd.SurfaceType_RearRight.ToString()); row.Values.Add(ctd.SurfaceType_FrontLeft.ToString()); row.Values.Add(ctd.SurfaceType_FrontRight.ToString()); //Car status CarStatusPacket.CarStatusData csd = frame.CarStatus.FieldCarStatusData[driver_index]; row.Values.Add(csd.TractionControlStatus.ToString()); row.Values.Add(csd.AntiLockBrakesOn.ToString()); row.Values.Add(csd.SelectedFuelMix.ToString()); row.Values.Add(csd.FrontBrakeBiasPercentage.ToString()); row.Values.Add(csd.PitLimiterOn.ToString()); row.Values.Add(csd.FuelLevel.ToString()); row.Values.Add(csd.FuelCapacity.ToString()); row.Values.Add(csd.FuelRemainingLaps.ToString()); row.Values.Add(csd.MaxRpm.ToString()); row.Values.Add(csd.IdleRpm.ToString()); row.Values.Add(csd.MaxGears.ToString()); row.Values.Add(csd.DrsAllowed.ToString()); row.Values.Add(csd.DrsActivationDistance.ToString()); row.Values.Add(csd.TyreWearPercentage.RearLeft.ToString()); row.Values.Add(csd.TyreWearPercentage.RearRight.ToString()); row.Values.Add(csd.TyreWearPercentage.FrontLeft.ToString()); row.Values.Add(csd.TyreWearPercentage.FrontRight.ToString()); row.Values.Add(csd.EquippedTyreCompound.ToString()); row.Values.Add(csd.EquippedVisualTyreCompoundId.ToString()); row.Values.Add(csd.TyreAgeLaps.ToString()); row.Values.Add(csd.TyreDamagePercentage.RearLeft.ToString()); row.Values.Add(csd.TyreDamagePercentage.RearRight.ToString()); row.Values.Add(csd.TyreDamagePercentage.FrontLeft.ToString()); row.Values.Add(csd.TyreDamagePercentage.FrontRight.ToString()); row.Values.Add(csd.FrontLeftWingDamagePercent.ToString()); row.Values.Add(csd.FrontRightWingDamagePercent.ToString()); row.Values.Add(csd.RearWingDamagePercent.ToString()); row.Values.Add(csd.DrsFailure.ToString()); row.Values.Add(csd.EngineDamagePercent.ToString()); row.Values.Add(csd.GearBoxDamagePercent.ToString()); row.Values.Add(csd.VehicleFiaFlag.ToString()); row.Values.Add(csd.ErsStoredEnergyJoules.ToString()); row.Values.Add(csd.SelectedErsDeployMode.ToString()); row.Values.Add(csd.ErsHarvestedThisLapByMGUK.ToString()); row.Values.Add(csd.ErsHarvestedThisLapByMGUH.ToString()); row.Values.Add(csd.ErsDeployedThisLap.ToString()); } return(csv.GenerateAsCsvFileContent()); }
public void Load(Packet[] packets, byte driver_index) { PercentLoadComplete = 0; LoadComplete = false; //Load the session summary data first LoadSummary(packets, driver_index); //Summon this track Track ToLoad = Track.Melbourne; foreach (Packet p in packets) { if (p.PacketType == PacketType.Session) { SessionPacket sp = (SessionPacket)p; ToLoad = sp.SessionTrack; SessionId = sp.UniqueSessionId; break; } } TrackDataContainer tdc = TrackDataContainer.LoadTrack(ToLoad); //Get list of all laps List <byte> AllLaps = new List <byte>(); foreach (Packet p in packets) { if (p.PacketType == PacketType.Lap) { LapPacket lp = (LapPacket)p; byte this_lap_num = lp.FieldLapData[driver_index].CurrentLapNumber; if (AllLaps.Contains(this_lap_num) == false) { AllLaps.Add(this_lap_num); } } } //Set the total number of corners that need to be analyzed (this is used only for progress reporting purposes) int number_of_corners = tdc.Corners.Length * AllLaps.Count; //Set the % complete to 5% at this point PercentLoadComplete = 0.05f; //Generate the frames PacketFrame[] frames = PacketFrame.CreateAll(packets); PercentLoadComplete = 0.15f; //Set it to 15% complete at this point //Create the lap analysis objects and fill it with corner analysis data. //This process also fills in the LapNumber property //This process below should take up the % complete from 15% to 90% List <Lap> _Lap = new List <Lap>(); foreach (byte lap_num in AllLaps) { Lap this_lap_analysis = new Lap(); //Fill in the lap number this_lap_analysis.LapNumber = lap_num; //Get all of the frames for this lap List <PacketFrame> ThisLapFrames = new List <PacketFrame>(); foreach (PacketFrame pf in frames) { if (pf.Lap.FieldLapData[driver_index].CurrentLapNumber == lap_num) { ThisLapFrames.Add(pf); } } //Find the best packetframe for each corner List <TelemetrySnapshot> _TelemetrySnapshot = new List <TelemetrySnapshot>(); int c = 1; for (c = 1; c <= tdc.Corners.Length; c++) { //Find the best packetframe for this corner TrackLocation this_corner = tdc.Corners[c - 1]; PacketFrame winner = ThisLapFrames[0]; float min_distance_found = float.MaxValue; foreach (PacketFrame pf in ThisLapFrames) { TrackLocation this_location = new TrackLocation(); this_location.PositionX = pf.Motion.FieldMotionData[driver_index].PositionX; this_location.PositionY = pf.Motion.FieldMotionData[driver_index].PositionY; this_location.PositionZ = pf.Motion.FieldMotionData[driver_index].PositionZ; this_location.Sector = pf.Lap.FieldLapData[driver_index].Sector; if (this_location.Sector == this_corner.Sector) //Only consider packets that are in the same sector { float this_distance = ApexVisualToolkit.DistanceBetweenTwoPoints(this_corner, this_location); if (this_distance < min_distance_found && this_distance < 40) //It has to be be within 40. If it isn't, it is not considered a corner hit! { winner = pf; min_distance_found = this_distance; } } } //Add the corner analysis TelemetrySnapshot ca = new TelemetrySnapshot(); ca.LocationNumber = (byte)c; if (min_distance_found < float.MaxValue) //we found a suitable packet, so therefore the min distance shold be less than max. Fill in the details { //Position ca.PositionX = winner.Motion.FieldMotionData[driver_index].PositionX; ca.PositionY = winner.Motion.FieldMotionData[driver_index].PositionY; ca.PositionZ = winner.Motion.FieldMotionData[driver_index].PositionZ; //Velocity ca.VelocityX = winner.Motion.FieldMotionData[driver_index].VelocityX; ca.VelocityY = winner.Motion.FieldMotionData[driver_index].VelocityY; ca.VelocityZ = winner.Motion.FieldMotionData[driver_index].VelocityZ; //gForce ca.gForceLateral = winner.Motion.FieldMotionData[driver_index].gForceLateral; ca.gForceLongitudinal = winner.Motion.FieldMotionData[driver_index].gForceLongitudinal; ca.gForceVertical = winner.Motion.FieldMotionData[driver_index].gForceVertical; //Yaw, pitch, roll ca.Yaw = winner.Motion.FieldMotionData[driver_index].Yaw; ca.Pitch = winner.Motion.FieldMotionData[driver_index].Pitch; ca.Roll = winner.Motion.FieldMotionData[driver_index].Roll; //Lap data ca.CurrentLapTime = winner.Lap.FieldLapData[driver_index].CurrentLapTime; ca.CarPosition = winner.Lap.FieldLapData[driver_index].CarPosition; ca.LapInvalid = winner.Lap.FieldLapData[driver_index].CurrentLapInvalid; ca.Penalties = winner.Lap.FieldLapData[driver_index].Penalties; //Telemetry data ca.SpeedKph = winner.Telemetry.FieldTelemetryData[driver_index].SpeedKph; ca.Throttle = winner.Telemetry.FieldTelemetryData[driver_index].Throttle; ca.Steer = winner.Telemetry.FieldTelemetryData[driver_index].Steer; ca.Brake = winner.Telemetry.FieldTelemetryData[driver_index].Brake; ca.Clutch = winner.Telemetry.FieldTelemetryData[driver_index].Clutch; ca.Gear = winner.Telemetry.FieldTelemetryData[driver_index].Gear; ca.EngineRpm = winner.Telemetry.FieldTelemetryData[driver_index].EngineRpm; ca.DrsActive = winner.Telemetry.FieldTelemetryData[driver_index].DrsActive; //Wheel data arrays ca.BrakeTemperature = winner.Telemetry.FieldTelemetryData[driver_index].BrakeTemperature; ca.TyreSurfaceTemperature = winner.Telemetry.FieldTelemetryData[driver_index].TyreSurfaceTemperature; ca.TyreInnerTemperature = winner.Telemetry.FieldTelemetryData[driver_index].TyreInnerTemperature; //Other data ca.EngineTemperature = winner.Telemetry.FieldTelemetryData[driver_index].EngineTemperature; //Car status ca.SelectedFuelMix = winner.CarStatus.FieldCarStatusData[driver_index].SelectedFuelMix; ca.FuelLevel = winner.CarStatus.FieldCarStatusData[driver_index].FuelLevel; //Other wheel data arrays ca.TyreWearPercent = winner.CarStatus.FieldCarStatusData[driver_index].TyreWearPercentage; ca.TyreDamagePercent = winner.CarStatus.FieldCarStatusData[driver_index].TyreDamagePercentage; //Other data ca.FrontLeftWingDamage = winner.CarStatus.FieldCarStatusData[driver_index].FrontLeftWingDamagePercent; ca.FrontRightWingDamage = winner.CarStatus.FieldCarStatusData[driver_index].FrontRightWingDamagePercent; ca.RearWingDamage = winner.CarStatus.FieldCarStatusData[driver_index].RearWingDamagePercent; ca.ErsStored = winner.CarStatus.FieldCarStatusData[driver_index].ErsStoredEnergyJoules; //Add it to the list. (This is in this block because nothing should be added to the list if the corner was not found. So NO EMPTY TelemetrySnapshots in this array to represent a corner that was not found) _TelemetrySnapshot.Add(ca); } else //if we were not able to find a suitable packet for that corner, populate it with just a blank PacketFrame as a place holder. { //Do nothing (that Lap just won't have data for that corner. It can scan through all of the track locations ) } } this_lap_analysis.Corners = _TelemetrySnapshot.ToArray(); //Get the tyre compound that is being used for this lap List <TyreCompound> CompoundsUsedThisLap = new List <TyreCompound>(); foreach (PacketFrame pf in ThisLapFrames) { //Add the compound to the list TyreCompound this_comp = pf.CarStatus.FieldCarStatusData[driver_index].EquippedTyreCompound; if (CompoundsUsedThisLap.Contains(this_comp) == false) { CompoundsUsedThisLap.Add(this_comp); } } if (CompoundsUsedThisLap.Count == 1) //If there is only one tyre compound that was used this lap, plug that one in { this_lap_analysis.EquippedTyreCompound = CompoundsUsedThisLap[0]; } else //If there were multiple compounds that were used, check which one was used more { //Find the one that is used most int HighestSeen = 0; TyreCompound winner = CompoundsUsedThisLap[0]; foreach (TyreCompound tc in CompoundsUsedThisLap) { //Count it int this_times = 0; foreach (PacketFrame pf in ThisLapFrames) { TyreCompound thistc = pf.CarStatus.FieldCarStatusData[driver_index].EquippedTyreCompound; if (thistc == tc) { this_times = this_times + 1; } } //Is it greater? if so, kick out the winner if (this_times >= HighestSeen) { HighestSeen = this_times; winner = tc; } } //Plug it in this_lap_analysis.EquippedTyreCompound = winner; } //Add this to the list of lap analyses _Lap.Add(this_lap_analysis); //Update the percent complete float AdditionalPercentCompletePerLap = (0.90f - 0.15f) / (float)AllLaps.Count; PercentLoadComplete = PercentLoadComplete + AdditionalPercentCompletePerLap; } //Sort the packets by time - this is not required for the above process, so I am doing it here for the timing stuff List <PacketFrame> frames_aslist = frames.ToList(); List <PacketFrame> frames_sorted = new List <PacketFrame>(); while (frames_aslist.Count > 0) { PacketFrame winner = frames_aslist[0]; foreach (PacketFrame pf in frames_aslist) { if (pf.Telemetry.SessionTime < winner.Telemetry.SessionTime) { winner = pf; } } frames_sorted.Add(winner); frames_aslist.Remove(winner); } //Plug in the sector times and lap times PacketFrame last_frame = null; foreach (PacketFrame this_frame in frames_sorted) { if (last_frame != null) { float S1_Time_S = 0; float S2_Time_S = 0; float S3_Time_S = 0; float LapTime_S = 0; bool Lap_Invalid_In_Last_Frame = false; if (last_frame.Lap.FieldLapData[driver_index].Sector == 1 && this_frame.Lap.FieldLapData[driver_index].Sector == 2) //We went from sector 1 to sector 2 { S1_Time_S = (float)this_frame.Lap.FieldLapData[driver_index].Sector1TimeMilliseconds / 1000f; } else if (last_frame.Lap.FieldLapData[driver_index].Sector == 2 && this_frame.Lap.FieldLapData[driver_index].Sector == 3) //We went from sector 2 to sector 3 { S2_Time_S = (float)this_frame.Lap.FieldLapData[driver_index].Sector2TimeMilliseconds / 1000f; } else if (last_frame.Lap.FieldLapData[driver_index].CurrentLapNumber < this_frame.Lap.FieldLapData[driver_index].CurrentLapNumber) //We just finished the lap, and thus sector 3 { float last_s1_seconds = (float)last_frame.Lap.FieldLapData[driver_index].Sector1TimeMilliseconds / 1000f; float last_s2_seconds = (float)last_frame.Lap.FieldLapData[driver_index].Sector2TimeMilliseconds / 1000f; LapTime_S = this_frame.Lap.FieldLapData[driver_index].LastLapTime; S3_Time_S = LapTime_S - last_s1_seconds - last_s2_seconds; } if (last_frame.Lap.FieldLapData[driver_index].CurrentLapInvalid) { Lap_Invalid_In_Last_Frame = true; } //If any of the numbers up there changed (are not 0), it means that we either changed sector or changed lap. If we did, we need to plug that data into the Lap if (S1_Time_S > 0 || S2_Time_S > 0 || S3_Time_S > 0 || Lap_Invalid_In_Last_Frame) { //Find the lap analysis foreach (Lap la in _Lap) { if (la.LapNumber == last_frame.Lap.FieldLapData[driver_index].CurrentLapNumber) { if (S1_Time_S > 0) { la.Sector1Time = S1_Time_S; } if (S2_Time_S > 0) { la.Sector2Time = S2_Time_S; } if (S3_Time_S > 0) { la.Sector3Time = S3_Time_S; } if (Lap_Invalid_In_Last_Frame) { la.LapInvalid = true; } } } } } last_frame = this_frame; } PercentLoadComplete = 0.95f; //Mark the percent complete as 95% #region "Get fuel consumption for each lap" foreach (byte lapnum in AllLaps) { //Get all packets for this lap List <PacketFrame> lap_frames = new List <PacketFrame>(); foreach (PacketFrame frame in frames_sorted) { if (frame.Lap.FieldLapData[driver_index].CurrentLapNumber == lapnum) { lap_frames.Add(frame); } } //Get the min and max and then plug it in if (lap_frames.Count > 0) { float fuel_start = lap_frames[0].CarStatus.FieldCarStatusData[driver_index].FuelLevel; float fuel_end = lap_frames[lap_frames.Count - 1].CarStatus.FieldCarStatusData[driver_index].FuelLevel; float fuel_used = fuel_end - fuel_start; fuel_used = fuel_used * -1; foreach (Lap la in _Lap) { if (la.LapNumber == lapnum) { la.FuelConsumed = fuel_used; } } } } #endregion #region "Get percent on throttle/brake/coasting/max throttle/max brake" foreach (byte lapnum in AllLaps) { //Get all packets for this lap List <PacketFrame> lap_frames = new List <PacketFrame>(); foreach (PacketFrame frame in frames_sorted) { if (frame.Lap.FieldLapData[driver_index].CurrentLapNumber == lapnum) { lap_frames.Add(frame); } } //Set up counter variables int OnThrottle = 0; int OnBrake = 0; int Coasting = 0; int Overlap = 0; int FullThrottle = 0; int FullBrake = 0; //Do the calculations foreach (PacketFrame frame in lap_frames) { TelemetryPacket.CarTelemetryData ctd = frame.Telemetry.FieldTelemetryData[driver_index]; //Basics if (ctd.Throttle > 0 && ctd.Brake == 0) { OnThrottle = OnThrottle + 1; } else if (ctd.Brake > 0 && ctd.Throttle == 0) { OnBrake = OnBrake + 1; } else if (ctd.Throttle == 0 && ctd.Brake == 0) { Coasting = Coasting + 1; } else if (ctd.Throttle > 0 && ctd.Brake > 0) { Overlap = Overlap + 1; } //Full pressures if (ctd.Throttle == 1) { FullThrottle = FullThrottle + 1; } if (ctd.Brake == 1) { FullBrake = FullBrake + 1; } } //Do the calculations float percent_on_throttle = (float)OnThrottle / (float)lap_frames.Count; float percent_on_brake = (float)OnBrake / (float)lap_frames.Count; float percent_coasting = (float)Coasting / (float)lap_frames.Count; float percent_overlap = (float)Overlap / (float)lap_frames.Count; float full_throttle = (float)FullThrottle / (float)lap_frames.Count; float full_brake = (float)FullBrake / (float)lap_frames.Count; //plug them in foreach (Lap la in _Lap) { if (la.LapNumber == lapnum) { la.PercentOnThrottle = percent_on_throttle; la.PercentOnBrake = percent_on_brake; la.PercentCoasting = percent_coasting; la.PercentThrottleBrakeOverlap = percent_overlap; la.PercentOnMaxThrottle = full_throttle; la.PercentOnMaxBrake = full_brake; } } } #endregion #region "Get ERS deployed + harvested" foreach (byte lapnum in AllLaps) { //Get all packets for this lap List <PacketFrame> lap_frames = new List <PacketFrame>(); foreach (PacketFrame frame in frames_sorted) { if (frame.Lap.FieldLapData[driver_index].CurrentLapNumber == lapnum) { lap_frames.Add(frame); } } float ers_dep = lap_frames[lap_frames.Count - 1].CarStatus.FieldCarStatusData[driver_index].ErsDeployedThisLap; float ers_har = lap_frames[lap_frames.Count - 1].CarStatus.FieldCarStatusData[driver_index].ErsHarvestedThisLapByMGUH + lap_frames[lap_frames.Count - 1].CarStatus.FieldCarStatusData[driver_index].ErsHarvestedThisLapByMGUK; //plug them in foreach (Lap la in _Lap) { if (la.LapNumber == lapnum) { la.ErsDeployed = ers_dep; la.ErsHarvested = ers_har; } } } #endregion #region "Get gear changes" foreach (byte lapnum in AllLaps) { //Get all packets for this lap List <PacketFrame> lap_frames = new List <PacketFrame>(); foreach (PacketFrame frame in frames_sorted) { if (frame.Lap.FieldLapData[driver_index].CurrentLapNumber == lapnum) { lap_frames.Add(frame); } } //Count the number of gear changes int gear_changes = 0; PacketFrame last_frame_ = null; foreach (PacketFrame frame in lap_frames) { if (last_frame_ != null) { sbyte last_gear = last_frame_.Telemetry.FieldTelemetryData[driver_index].Gear; sbyte this_gear = frame.Telemetry.FieldTelemetryData[driver_index].Gear; if (this_gear != last_gear) { gear_changes = gear_changes + 1; } } last_frame_ = frame; } //Plug it in foreach (Lap la in _Lap) { if (la.LapNumber == lapnum) { la.GearChanges = gear_changes; } } } #endregion #region "Get Top Speed" foreach (byte lapnum in AllLaps) { //Get all packets for this lap List <PacketFrame> lap_frames = new List <PacketFrame>(); foreach (PacketFrame frame in frames_sorted) { if (frame.Lap.FieldLapData[driver_index].CurrentLapNumber == lapnum) { lap_frames.Add(frame); } } //Get values ushort max_kph = 0; ushort max_mph = 0; foreach (PacketFrame frame in lap_frames) { TelemetryPacket.CarTelemetryData ctd = frame.Telemetry.FieldTelemetryData[driver_index]; if (ctd.SpeedKph > max_kph) { max_kph = ctd.SpeedKph; } if (ctd.SpeedMph > max_mph) { max_mph = ctd.SpeedMph; } } //Plug it in foreach (Lap la in _Lap) { if (la.LapNumber == lapnum) { la.TopSpeedKph = max_kph; } } } #endregion #region "Get average incremental tyre wear" foreach (byte lapnum in AllLaps) { //Get all packets for this lap List <PacketFrame> lap_frames = new List <PacketFrame>(); foreach (PacketFrame frame in frames_sorted) { if (frame.Lap.FieldLapData[driver_index].CurrentLapNumber == lapnum) { lap_frames.Add(frame); } } WheelDataArray tyrewear_start = lap_frames[0].CarStatus.FieldCarStatusData[driver_index].TyreWearPercentage; WheelDataArray tyrewear_end = lap_frames[lap_frames.Count - 1].CarStatus.FieldCarStatusData[driver_index].TyreWearPercentage; float AvgTyreWear_Start = (tyrewear_start.RearLeft + tyrewear_start.RearRight + tyrewear_start.FrontLeft + tyrewear_start.FrontRight) / 4f; float AvgTyreWear_End = (tyrewear_end.RearLeft + tyrewear_end.RearRight + tyrewear_end.FrontLeft + tyrewear_end.FrontRight) / 4f; float avginctyrewear = AvgTyreWear_End - AvgTyreWear_Start; //Plug it in foreach (Lap la in _Lap) { if (la.LapNumber == lapnum) { //Incremental tyre wear la.IncrementalTyreWear = new WheelDataArray(); la.IncrementalTyreWear.RearLeft = tyrewear_end.RearLeft - tyrewear_start.RearLeft; la.IncrementalTyreWear.RearRight = tyrewear_end.RearRight - tyrewear_start.RearRight; la.IncrementalTyreWear.FrontLeft = tyrewear_end.FrontLeft - tyrewear_start.FrontLeft; la.IncrementalTyreWear.FrontRight = tyrewear_end.FrontRight - tyrewear_start.FrontRight; //Beginning (snapshot) tyre wear la.BeginningTyreWear = new WheelDataArray(); la.BeginningTyreWear.RearLeft = tyrewear_start.RearLeft; la.BeginningTyreWear.RearRight = tyrewear_start.RearRight; la.BeginningTyreWear.FrontLeft = tyrewear_start.FrontLeft; la.BeginningTyreWear.FrontRight = tyrewear_start.FrontRight; } } } #endregion //Close off the laps Laps = _Lap.ToArray(); #region "Now that we have the lap analyses, generate the corner performance analyses (Lap Analyses MUST BE DONE before this)" //Generate all of the corner performances List <LocationPerformanceAnalysis> corner_performances = new List <LocationPerformanceAnalysis>(); for (int c = 0; c < tdc.Corners.Length; c++) { LocationPerformanceAnalysis cpa = new LocationPerformanceAnalysis(); //Copy over the data from the TrackLocationOptima (by doing a quick Json serialization/deserialization) cpa = JsonConvert.DeserializeObject <LocationPerformanceAnalysis>(JsonConvert.SerializeObject(tdc.Corners[c])); //Plug in the corner # cpa.LocationNumber = (byte)(c + 1); List <ushort> Speeds = new List <ushort>(); //A list of speeds that were carried through this corner List <sbyte> Gears = new List <sbyte>(); //A list of gears that the driver used through this corner List <float> Distances = new List <float>(); //Collect the data for each lap foreach (Lap la in Laps) { //Go through all of the corners for this lap. If you find a corner that matches the corner number that we are analyzing, add the details to the list. foreach (TelemetrySnapshot ts in la.Corners) { if (ts.LocationNumber == (c + 1)) { Speeds.Add(ts.SpeedKph); Gears.Add(ts.Gear); } } // Had to comment out the below on 11/24/2020. The distance to apex method is removed. // //Collect the distance to apex // TelemetrySnapshot ca = la.Corners[c]; // if (ca.Motion != null) //The motion packet is required to calculate distance to apex BECAUSE the motion packet contains position data // { // Distances.Add(ca.DistanceToApex()); // } } //Get the average speed float speed_avg = 0; if (Speeds.Count > 0) { foreach (ushort us in Speeds) { speed_avg = speed_avg + (float)us; } speed_avg = speed_avg / (float)Speeds.Count; cpa.AverageSpeedKph = speed_avg; } else { cpa.AverageSpeedKph = float.NaN; } //Get the average gear float gear_avg = 0; if (Gears.Count > 0) { foreach (sbyte sb in Gears) { gear_avg = gear_avg + (float)sb; } gear_avg = gear_avg / (float)Gears.Count; cpa.AverageGear = gear_avg; } else { cpa.AverageGear = float.NaN; } //Get average distance to apex float distance_avg = 0; if (Distances.Count > 0) { foreach (float f in Distances) { distance_avg = distance_avg + f; } distance_avg = distance_avg / (float)Distances.Count; cpa.AverageDistanceToApex = distance_avg; } else { cpa.AverageDistanceToApex = float.NaN; } #region "Consistency rating" if (Speeds.Count > 0 && Gears.Count > 0 && Distances.Count > 0) //Only do the consistency rating if we have at least SOME data from all 3 parts { //Get a list of floats - speeds List <float> Speeds_float = new List <float>(); foreach (ushort us in Speeds) { Speeds_float.Add((float)us); } //Get a list of floats - gears List <float> Gears_float = new List <float>(); foreach (sbyte sb in Gears) { Gears_float.Add((float)sb); } //Calculate the standard deviations float stdev_Speeds = MathToolkit.StandardDeviation(Speeds_float.ToArray()); float stdev_Gears = MathToolkit.StandardDeviation(Gears_float.ToArray()); float stdev_Distances = MathToolkit.StandardDeviation(Distances.ToArray()); //Turn them into ratings by turning them into percentages of the average (if you don't do this, the higher numbers on corners would result in higher st deviation) float rating_Speeds = stdev_Speeds / speed_avg; float rating_Gears = stdev_Gears / gear_avg; float rating_Distances = stdev_Distances / distance_avg; //Calculate the consistency rating List <float> ConsistencyRatingAgg = new List <float>(); ConsistencyRatingAgg.Add(rating_Speeds); ConsistencyRatingAgg.Add(rating_Gears * 0.5f); //The float is a weight (higher = this category deserves more weight) ConsistencyRatingAgg.Add(rating_Distances * 0.35f); //Plug in the consistency rating cpa.InconsistencyRating = ConsistencyRatingAgg.Sum(); } else { cpa.InconsistencyRating = float.NaN; } #endregion corner_performances.Add(cpa); } //Plug in all of the corner performances Corners = corner_performances.ToArray(); #endregion //Shut down PercentLoadComplete = 1; //Mark the percent complete as 100% LoadComplete = true; }