Beispiel #1
0
 public void SdkOnTelemetryUpdated(iRacingData telemetry)
 {
     // Cache info
     _telemetry = telemetry;
     // Update drivers telemetry
     this.UpdateDriverTelemetry(telemetry);
 }
Beispiel #2
0
        public void ParseTelemetry(iRacingData e, Boolean isRaceOrQualifying)
        {
            LapDistance = Math.Abs(e.CarIdxLapDistPct[Driver.Id]);
            // We need to make sure we start the first lap(IsNewLap), we cant use LapsCompleted for this as it does not increas first time we cross the s/f line but CarIdxLap does so we use that.
            // we only wanna do this for opponents in race and qualifying
            insertStartLap = (startLapInserted == false && Lap < e.CarIdxLap[Driver.Id] && e.CarIdxLap[Driver.Id] > 0) && ((Driver.IsCurrentDriver) || (isRaceOrQualifying && !Driver.IsCurrentDriver));
            Lap            = e.CarIdxLap[Driver.Id];
            TrackSurface   = e.CarIdxTrackSurface[Driver.Id];
            // turns out that the other method for setting the current sector was flawed as when first connecting it would set sector to 1 and then it would be unable to switch to sector 3 when gridding up,
            // so catching crossing the s/f line when the light goes green to set a HasCrossedSFLine didnt happen.

            CurrentSector = GetCurrentSector();
            IsNewLap      = false;

            if (_prevSector == 3 && (CurrentSector == 1) ||
                (LapsCompleted < _driver.CurrentResults.LapsComplete && !_driver.IsCurrentDriver && TrackSurface == TrackSurfaces.NotInWorld) || (insertStartLap && !Driver.IsCurrentDriver && !hasCrossedSFLineToStartRace))
            {
                // make sure we dont this on the opening lap more then once
                hasCrossedSFLineToStartRace = true;

                HasCrossedSFLine = true;
                // This is not accurate for playes that are not in live telemetry but its not used in any calculations in this case.
                GameTimeWhenLastCrossedSFLine = (float)e.SessionTime;
            }
            else
            {
                HasCrossedSFLine = false;
            }
            if (_prevSector != CurrentSector)
            {
                _prevSector = CurrentSector;
            }
            if (_driver.CurrentResults.LapsComplete > LapsCompleted || insertStartLap)
            {
                startLapInserted = true;
                IsNewLap         = true;
            }
            LapsCompleted = _driver.CurrentResults.LapsComplete;

            CorrectedLapDistance = FixPercentagesOnLapChange(LapDistance);
            LiveLapsCompleted    = e.CarIdxLapCompleted[Driver.Id] < LapsCompleted ? LapsCompleted : e.CarIdxLapCompleted[Driver.Id];

            Gear = e.CarIdxGear[Driver.Id];
            Rpm  = e.CarIdxRPM[Driver.Id];

            //for local player we use data from telemetry as its updated faster then session info,
            //we do not have lastlaptime from opponents available in telemetry so we use data from sessioninfo.
            if (Driver.Id == e.PlayerCarIdx)
            {
                LapTimePrevious = e.LapLastLapTime;
            }
            else
            {
                LapTimePrevious = _driver.CurrentResults.LastTime;
            }
            PreviousLapWasValid       = LapTimePrevious > 1;
            TotalLapDistanceCorrected = TotalLapDistance;
        }
Beispiel #3
0
 private void UpdateDriverTelemetry(iRacingData info)
 {
     foreach (var driver in _drivers)
     {
         driver.Live.CalculateSpeed(info, _sessionData.Track.Length);
         driver.UpdateLiveInfo(info, _isRaceOrQualifying);
     }
     this.CalculateLivePositions(info);
 }
Beispiel #4
0
 internal void UpdateLiveInfo(iRacingData e, Boolean isRaceOrQualifying)
 {
     this.Live.ParseTelemetry(e, isRaceOrQualifying);
 }
Beispiel #5
0
        public override Object ReadGameData(Boolean forSpotter)
        {
            lock (this)
            {
                if (!initialised)
                {
                    if (!InitialiseInternal())
                    {
                        throw new GameDataReadException("Failed to initialise shared memory");
                    }
                }
                try
                {
                    if (sdk.IsConnected())
                    {
                        if (sim == null)
                        {
                            sim = new Sim();
                        }
                        if (forSpotter)
                        {
                            return((int)sdk.GetData("CarLeftRight"));
                        }

                        _DriverId = (int)sdk.GetData("PlayerCarIdx");

                        int  newUpdate         = sdk.Header.SessionInfoUpdate;
                        bool hasNewSessionData = false;
                        bool isNewSession      = false;
                        if (newUpdate != lastUpdate)
                        {
                            var sessionNum = TryGetSessionNum();
                            if (sessionNum != null)
                            {
                                string sessionInfoUnFiltred = sdk.GetSessionInfoString();
                                if (sessionInfoUnFiltred == null)
                                {
                                    return(null);
                                }
                                string sessionInfoFiltred = new SessionInfo(sessionInfoUnFiltred).Yaml;
                                isNewSession      = sim.SdkOnSessionInfoUpdated(sessionInfoFiltred, (int)sessionNum, DriverId);
                                lastUpdate        = newUpdate;
                                hasNewSessionData = true;
                            }
                            else
                            {
                                return(null);
                            }
                        }
                        iRacingData irData = new iRacingData(sdk, hasNewSessionData && dumpToFile, isNewSession);

                        sim.SdkOnTelemetryUpdated(irData);

                        iRacingStructWrapper structWrapper = new iRacingStructWrapper();
                        structWrapper.ticksWhenRead = DateTime.UtcNow.Ticks;
                        structWrapper.data          = sim;

                        if (dumpToFile && dataToDump != null)
                        {
                            dataToDump.Add(new iRacingStructDumpWrapper()
                            {
                                ticksWhenRead = structWrapper.ticksWhenRead, data = irData
                            });
                        }
                        return(structWrapper);
                    }
                    else
                    {
                        return(null);
                    }
                }
                catch (Exception ex)
                {
                    throw new GameDataReadException(ex.Message, ex);
                }
            }
        }
Beispiel #6
0
        public void CalculateSpeed(iRacingData telemetry, double?trackLengthKm)
        {
            if (telemetry == null)
            {
                return;
            }
            if (trackLengthKm == null)
            {
                return;
            }

            try
            {
                var t1   = telemetry.SessionTime;
                var t0   = _prevSpeedUpdateTime;
                var time = t1 - t0;

                if (time < SPEED_CALC_INTERVAL)
                {
                    // Ignore
                    return;
                }

                var p1 = telemetry.CarIdxLapDistPct[this.Driver.Id];
                var p0 = _prevSpeedUpdateDist;

                if (p1 < -0.5 || _driver.Live.TrackSurface == TrackSurfaces.NotInWorld)
                {
                    // Not in world?
                    return;
                }

                if (p0 - p1 > 0.5)
                {
                    // Lap crossing
                    p1 += 1;
                }
                var distancePct = p1 - p0;

                var distance = distancePct * trackLengthKm.GetValueOrDefault() * 1000; //meters


                if (time >= Double.Epsilon)
                {
                    this.Speed = distance / (time); // m/s
                }
                else
                {
                    if (distance < 0)
                    {
                        this.Speed = Double.NegativeInfinity;
                    }
                    else
                    {
                        this.Speed = Double.PositiveInfinity;
                    }
                }
                this.SpeedKph = this.Speed * 3.6;

                _prevSpeedUpdateTime = t1;
                _prevSpeedUpdateDist = p1;
            }
            catch (Exception)
            {
                //Log.Instance.LogError("Calculating speed of car " + this.Driver.Id, ex);
                this.Speed = 0;
            }
        }
Beispiel #7
0
        private void CalculateLivePositions(iRacingData telemetry)
        {
            if (!_raceSessionInProgress)
            {
                this._carIdxToGameTimeOffTrack.Clear();
            }

            // Assume race has finished.
            this._raceSessionInProgress = false;

            // In a race that is not yet in checkered flag mode,
            // Live positions are determined from track position (total lap distance)
            // Any other conditions (race finished, P, Q, etc), positions are ordered as result positions
            SessionFlags flag = (SessionFlags)telemetry.SessionFlags;

            if (this.SessionData.SessionType == "Race" && flag.HasFlag(SessionFlags.Checkered))
            {
                // We need to check if player is the first (p1) to cross the s/f line as HasCrossedSFLine is only true for 1 tick
                if (raceEndState == RaceEndState.WAITING_TO_CROSS_LINE || _driver.Live.IsNewLap)
                {
                    if (this._driver.Live.IsNewLap)
                    {
                        Console.WriteLine("Player just crossed line to finish race");
                        raceEndState = RaceEndState.FINISHED;

                        this._driver.FinishStatus = Driver.FinishState.Finished;
                    }
                }
                else if (raceEndState == RaceEndState.NONE)
                {
                    Console.WriteLine("Starting wait for player crossing line");
                    raceEndState = RaceEndState.WAITING_TO_CROSS_LINE;
                }
            }
            else
            {
                raceEndState = RaceEndState.NONE;
            }
            if (this.SessionData.SessionType == "Race" && raceEndState != RaceEndState.FINISHED &&
                (flag.HasFlag(SessionFlags.StartGo) || flag.HasFlag(SessionFlags.StartHidden /*yellow?*/)) &&
                telemetry.PlayerCarPosition > 0)
            {
                this._raceSessionInProgress = true;

                // When driver disconnects (or in other cases I am not sure about yet), TotalLapDitance
                // gets ceiled to the nearest integer.  Because of that, for the reminder of a lap such car is
                // ahead of others by TotalLapDitance, which results incorrect positions announced.
                //
                // To mitigate that, try detecting such cases and using floor(TotalLapDitance) instead.
                //
                // Also, try detecting cars that finished and use YAML reported positions instead for those.

                if (this._gameTimeWhenWhiteFlagTriggered == -1.0 && flag.HasFlag(SessionFlags.White))
                {
                    this._gameTimeWhenWhiteFlagTriggered = telemetry.SessionTime;
                }

                // Correct the distances
                foreach (var driver in _drivers)
                {
                    if (driver.IsSpectator || driver.IsPaceCar || driver.CurrentResults.IsOut)
                    {
                        continue;
                    }

                    if (Math.Floor(driver.Live.TotalLapDistanceCorrected) > driver.Live.LiveLapsCompleted)
                    {
                        driver.Live.TotalLapDistanceCorrected = (float)driver.Live.LiveLapsCompleted;
                    }
                }

                // If leader has not finished yet, check if he just did.
                if (_leaderFinished == null && this._gameTimeWhenWhiteFlagTriggered != -1.0)
                {
                    // See if leading driver crossed s/f line after race time expired.
                    foreach (var driver in _drivers.OrderByDescending(d => d.Live.TotalLapDistanceCorrected))
                    {
                        if (driver.IsSpectator || driver.IsPaceCar || driver.CurrentResults.IsOut)
                        {
                            continue;
                        }

                        if (driver.IsCurrentDriver)
                        {
                            // It appears that player s/f crossing time is set before the actual s/f crossing.  So, for player "Finished" state
                            // rely on logic that waits for new lap crossing.
                            continue;
                        }

                        if (driver.Live.GameTimeWhenLastCrossedSFLine > this._gameTimeWhenWhiteFlagTriggered)
                        {
                            _leaderFinished = driver;
                        }

                        // Only check assumed leader by lapdist (skipping lapped vehicles, spectators etc).
                        break;
                    }
                }

                // Now, detect lapped and retired finishers.
                foreach (var driver in _drivers)
                {
                    if (_leaderFinished != null &&
                        driver.Live.GameTimeWhenLastCrossedSFLine >= _leaderFinished.Live.GameTimeWhenLastCrossedSFLine)
                    {
                        if (driver.IsCurrentDriver || driver.IsPaceCar)
                        {
                            // It appears that player s/f crossing time is set before the actual s/f crossing.  So, for player "Finished" state
                            // rely on logic that waits for new lap crossing.
                            continue;
                        }

                        // Everyone, who crosses s/f after leader finished, finishes too.
                        driver.FinishStatus = Driver.FinishState.Finished;
                    }

                    // Try detecting disconnects.  Save last time seen off world, and mark as disconnect if
                    // stays off world long enough.
                    if (driver.FinishStatus == Driver.FinishState.Unknown)  // Don't do any processing for Finished and Retired.
                    {
                        if (driver.Live.TrackSurface == TrackSurfaces.NotInWorld)
                        {
                            var timeSinceOffWorld = -1.0;
                            if (!this._carIdxToGameTimeOffTrack.TryGetValue(driver.Id, out timeSinceOffWorld))
                            {
                                this._carIdxToGameTimeOffTrack.Add(driver.Id, telemetry.SessionTime);
                            }
                            else if (telemetry.SessionTime - timeSinceOffWorld > SECONDS_OFF_WORLD_TILL_RETIRED)
                            {
                                driver.FinishStatus = Driver.FinishState.Retired;
                                Console.WriteLine("Marking driver: " + driver.Name + " as retired.");
                            }
                        }
                        else
                        {
                            this._carIdxToGameTimeOffTrack.Remove(driver.Id);
                        }
                    }

                    if (driver.FinishStatus == Driver.FinishState.Retired &&
                        driver.Live.TrackSurface != TrackSurfaces.NotInWorld)
                    {
                        driver.FinishStatus = Driver.FinishState.Unknown;
                        Console.WriteLine("Driver: " + driver.Name + " was previously marked as retired, shown up again.");
                    }
                }

                // Determine live position from lap distance.
                int pos = 1;
                foreach (var driver in _drivers.OrderByDescending(d => d.Live.TotalLapDistanceCorrected))
                {
                    if (driver.IsSpectator || driver.IsPaceCar || driver.CurrentResults.IsOut)
                    {
                        driver.Live.Position = 1001;  // Make it obvious those guys are not tracked.
                        continue;
                    }
                    if (driver.FinishStatus == Driver.FinishState.Finished &&
                        driver.Live.TrackSurface == TrackSurfaces.NotInWorld)
                    {
                        // When finished driver disconnects, use game reported position.
                        // This should not mess up order of drivers following, because all the
                        // drivers are ordered by TotalLapDistanceCorrected.
                        driver.Live.Position = driver.CurrentResults.Position;

                        if (driver.Live.Position == 0)
                        {
                            driver.Live.Position = 1000;  // Game sends nonsense again.  Mark those so that they don't interfere with sorting anywhere.
                        }
                    }
                    else
                    {
                        driver.Live.Position = pos;
                    }
                    pos++;
                }
            }
            else  // Not Race or Finished.
            {
                // Clear out cached finished leader.  Ugly data model, ugly workarounds :(
                _leaderFinished = null;
                _gameTimeWhenWhiteFlagTriggered = -1.0;

                // In P or Q, set live position from result position (== best lap according to iRacing)
                foreach (var driver in _drivers)
                {
                    if (driver.IsSpectator || driver.IsPaceCar)
                    {
                        driver.Live.Position = 1001;  // Make it obvious those guys are not tracked.
                        continue;
                    }
                    if (telemetry.CarIdxPosition[driver.Id] > 0 && raceEndState != RaceEndState.FINISHED)
                    {
                        driver.Live.Position = telemetry.CarIdxPosition[driver.Id];
                    }
                    else
                    {
                        driver.Live.Position = driver.CurrentResults.Position;
                    }

                    if (telemetry.CarIdxClassPosition[driver.Id] > 0 && raceEndState != RaceEndState.FINISHED)
                    {
                        driver.Live.ClassPosition = telemetry.CarIdxClassPosition[driver.Id];
                    }
                    else
                    {
                        driver.Live.ClassPosition = driver.CurrentResults.ClassPosition;
                    }

                    if (driver.Live.Position == 0)
                    {
                        driver.Live.Position = 1000;  // Game sends nonsense again.  Mark those so that they don't interfere with sorting anywhere.
                    }

                    if (driver.Live.ClassPosition == 0)
                    {
                        driver.Live.ClassPosition = 1000;  // Game sends nonsense again.  Mark those so that they don't interfere with sorting anywhere.
                    }
                }
            }
        }