private int readFromOffset(int offset, byte[] rawData)
        {
            totalPacketCount++;
            if (totalPacketCount == estimateRateStartPacket)
            {
                ticksAtRateEstimateStart = DateTime.Now.Ticks;
            }
            else if (totalPacketCount == estimateRateEndPacket && ticksAtRateEstimateStart > 0)
            {
                rateEstimate = (float)(TimeSpan.TicksPerSecond * (estimateRateEndPacket - estimateRateStartPacket)) / (float)(DateTime.Now.Ticks - ticksAtRateEstimateStart);
            }
            // the first 2 bytes are the version - discard it for now
            int frameTypeAndSequence = rawData[offset + 2];
            int frameType            = frameTypeAndSequence & 3;
            int sequence             = frameTypeAndSequence >> 2;
            int frameLength          = 0;

            if (frameType == 0)
            {
                telemPacketCount++;
                frameLength = sTelemetryData_PacketSize;
                Boolean sequenceCheckOK = isNextInSequence(sequence);
                if (strictPacketOrdering && !sequenceCheckOK)
                {
                    discardedTelemCount++;
                }
                else
                {
                    handle = GCHandle.Alloc(rawData.Skip(offset).Take(frameLength).ToArray(), GCHandleType.Pinned);
                    sTelemetryData telem = (sTelemetryData)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(sTelemetryData));
                    if (sequenceCheckOK || !telemIsOutOfSequence(telem))
                    {
                        //buttonsState = ConvertByteToBoolArray(telem.sDPad);
                        lastSequenceNumberForTelemPacket = sequence;
                        workingGameState = StructHelper.MergeWithExistingState(workingGameState, telem);
                        // newSpotterData = workingGameState.hasNewPositionData;
                        handle.Free();
                    }
                }
            }
            else if (frameType == 1)
            {
                frameLength = sParticipantInfoStrings_PacketSize;
                handle      = GCHandle.Alloc(rawData.Skip(offset).Take(frameLength).ToArray(), GCHandleType.Pinned);
                sParticipantInfoStrings strings = (sParticipantInfoStrings)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(sParticipantInfoStrings));
                workingGameState = StructHelper.MergeWithExistingState(workingGameState, strings);
                handle.Free();
            }
            else if (frameType == 2)
            {
                frameLength = sParticipantInfoStringsAdditional_PacketSize;
                handle      = GCHandle.Alloc(rawData.Skip(offset).Take(frameLength).ToArray(), GCHandleType.Pinned);
                sParticipantInfoStringsAdditional additional = (sParticipantInfoStringsAdditional)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(sParticipantInfoStringsAdditional));
                workingGameState = StructHelper.MergeWithExistingState(workingGameState, additional);
                handle.Free();
            }
            return(frameLength + offset);
        }
 private Boolean telemIsOutOfSequence(sTelemetryData telem)
 {
     if (telem.sViewedParticipantIndex >= 0 && telem.sParticipantInfo.Length > telem.sViewedParticipantIndex)
     {
         int   lapsCompletedInTelem = telem.sParticipantInfo[telem.sViewedParticipantIndex].sLapsCompleted;
         float lapTimeInTelem       = telem.sCurrentTime;
         if (lapTimeInTelem > 0 && lastValidTelemCurrentLapTime > 0)
         {
             // if the number of completed laps has decreased, or our laptime has decreased without starting
             // a new lap then we need to discard the packet. The lapsCompleted is unreliable, this may end badly
             if (lastValidTelemLapsCompleted > lapsCompletedInTelem ||
                 (lapTimeInTelem < lastValidTelemCurrentLapTime && lastValidTelemLapsCompleted == lapsCompletedInTelem))
             {
                 discardedTelemCount++;
                 return(true);
             }
         }
         lastValidTelemCurrentLapTime = lapTimeInTelem;
         lastValidTelemLapsCompleted  = lapsCompletedInTelem;
         acceptedOutOfSequenceTelemCount++;
     }
     return(false);
 }
Example #3
0
        public static pCarsAPIStruct MergeWithExistingState(pCarsAPIStruct existingState, sTelemetryData udpTelemetryData)
        {
            existingState.hasNewPositionData = false;
            existingState.mGameState         = (uint)udpTelemetryData.sGameSessionState & 7;
            existingState.mSessionState      = (uint)udpTelemetryData.sGameSessionState >> 4;
            existingState.mRaceState         = (uint)udpTelemetryData.sRaceStateFlags & 7;

            // Participant Info
            existingState.mViewedParticipantIndex = udpTelemetryData.sViewedParticipantIndex;
            existingState.mNumParticipants        = udpTelemetryData.sNumParticipants;

            // Unfiltered Input
            existingState.mUnfilteredThrottle = (float)udpTelemetryData.sUnfilteredThrottle / 255f;
            existingState.mUnfilteredBrake    = (float)udpTelemetryData.sUnfilteredBrake / 255f;
            existingState.mUnfilteredSteering = (float)udpTelemetryData.sUnfilteredSteering / 127f;
            existingState.mUnfilteredClutch   = (float)udpTelemetryData.sUnfilteredClutch / 255f;

            existingState.mLapsInEvent = udpTelemetryData.sLapsInEvent;
            existingState.mTrackLength = udpTelemetryData.sTrackLength;

            // Timing & Scoring
            existingState.mLapInvalidated             = (udpTelemetryData.sRaceStateFlags >> 3 & 1) == 1;
            existingState.mSessionFastestLapTime      = udpTelemetryData.sBestLapTime;
            existingState.mLastLapTime                = udpTelemetryData.sLastLapTime;
            existingState.mCurrentTime                = udpTelemetryData.sCurrentTime;
            existingState.mSplitTimeAhead             = udpTelemetryData.sSplitTimeAhead;
            existingState.mSplitTimeBehind            = udpTelemetryData.sSplitTimeBehind;
            existingState.mSplitTime                  = udpTelemetryData.sSplitTime;
            existingState.mEventTimeRemaining         = udpTelemetryData.sEventTimeRemaining;
            existingState.mPersonalFastestLapTime     = udpTelemetryData.sPersonalFastestLapTime;
            existingState.mWorldFastestLapTime        = udpTelemetryData.sWorldFastestLapTime;
            existingState.mCurrentSector1Time         = udpTelemetryData.sCurrentSector1Time;
            existingState.mCurrentSector2Time         = udpTelemetryData.sCurrentSector2Time;
            existingState.mCurrentSector3Time         = udpTelemetryData.sCurrentSector3Time;
            existingState.mSessionFastestSector1Time  = udpTelemetryData.sFastestSector1Time;
            existingState.mSessionFastestSector2Time  = udpTelemetryData.sFastestSector2Time;
            existingState.mSessionFastestSector3Time  = udpTelemetryData.sFastestSector3Time;
            existingState.mPersonalFastestSector1Time = udpTelemetryData.sPersonalFastestSector1Time;
            existingState.mPersonalFastestSector2Time = udpTelemetryData.sPersonalFastestSector2Time;
            existingState.mPersonalFastestSector3Time = udpTelemetryData.sPersonalFastestSector3Time;
            existingState.mWorldFastestSector1Time    = udpTelemetryData.sWorldFastestSector1Time;
            existingState.mWorldFastestSector2Time    = udpTelemetryData.sWorldFastestSector2Time;
            existingState.mWorldFastestSector3Time    = udpTelemetryData.sWorldFastestSector3Time;

            // Flags
            existingState.mHighestFlagColour = (uint)udpTelemetryData.sHighestFlag & 7;
            existingState.mHighestFlagReason = (uint)udpTelemetryData.sHighestFlag >> 3 & 3;

            // Pit Info
            existingState.mPitMode     = (uint)udpTelemetryData.sPitModeSchedule & 7;
            existingState.mPitSchedule = (uint)udpTelemetryData.sPitModeSchedule >> 3 & 3;

            // Car State
            existingState.mCarFlags         = udpTelemetryData.sCarFlags;
            existingState.mOilTempCelsius   = udpTelemetryData.sOilTempCelsius;
            existingState.mOilPressureKPa   = udpTelemetryData.sOilPressureKPa;
            existingState.mWaterTempCelsius = udpTelemetryData.sWaterTempCelsius;
            existingState.mWaterPressureKPa = udpTelemetryData.sWaterPressureKpa;
            existingState.mFuelPressureKPa  = udpTelemetryData.sFuelPressureKpa;
            existingState.mFuelLevel        = udpTelemetryData.sFuelLevel;
            existingState.mFuelCapacity     = udpTelemetryData.sFuelCapacity;
            existingState.mSpeed            = udpTelemetryData.sSpeed;
            existingState.mRPM            = udpTelemetryData.sRpm;
            existingState.mMaxRPM         = udpTelemetryData.sMaxRpm;
            existingState.mBrake          = (float)udpTelemetryData.sBrake / 255f;
            existingState.mThrottle       = (float)udpTelemetryData.sThrottle / 255f;
            existingState.mClutch         = (float)udpTelemetryData.sClutch / 255f;
            existingState.mSteering       = (float)udpTelemetryData.sSteering / 127f;
            existingState.mGear           = udpTelemetryData.sGearNumGears & 15;
            existingState.mNumGears       = udpTelemetryData.sGearNumGears >> 4;
            existingState.mOdometerKM     = udpTelemetryData.sOdometerKM;
            existingState.mAntiLockActive = (udpTelemetryData.sRaceStateFlags >> 4 & 1) == 1;
            existingState.mBoostActive    = (udpTelemetryData.sRaceStateFlags >> 5 & 1) == 1;
            existingState.mBoostAmount    = udpTelemetryData.sBoostAmount;

            // Motion & Device Related
            existingState.mOrientation       = udpTelemetryData.sOrientation;
            existingState.mLocalVelocity     = udpTelemetryData.sLocalVelocity;
            existingState.mWorldVelocity     = udpTelemetryData.sWorldVelocity;
            existingState.mAngularVelocity   = udpTelemetryData.sAngularVelocity;
            existingState.mLocalAcceleration = udpTelemetryData.sLocalAcceleration;
            existingState.mWorldAcceleration = udpTelemetryData.sWorldAcceleration;
            existingState.mExtentsCentre     = udpTelemetryData.sExtentsCentre;


            existingState.mTyreFlags             = toUIntArray(udpTelemetryData.sTyreFlags);
            existingState.mTerrain               = toUIntArray(udpTelemetryData.sTerrain);
            existingState.mTyreY                 = udpTelemetryData.sTyreY;
            existingState.mTyreRPS               = udpTelemetryData.sTyreRPS;
            existingState.mTyreSlipSpeed         = udpTelemetryData.sTyreSlipSpeed;
            existingState.mTyreTemp              = toFloatArray(udpTelemetryData.sTyreTemp, 255);
            existingState.mTyreGrip              = toFloatArray(udpTelemetryData.sTyreGrip, 255);
            existingState.mTyreHeightAboveGround = udpTelemetryData.sTyreHeightAboveGround;
            existingState.mTyreLateralStiffness  = udpTelemetryData.sTyreLateralStiffness;
            existingState.mTyreWear              = toFloatArray(udpTelemetryData.sTyreWear, 255);
            existingState.mBrakeDamage           = toFloatArray(udpTelemetryData.sBrakeDamage, 255);
            existingState.mSuspensionDamage      = toFloatArray(udpTelemetryData.sSuspensionDamage, 255);
            existingState.mBrakeTempCelsius      = toFloatArray(udpTelemetryData.sBrakeTempCelsius, 1);
            existingState.mTyreTreadTemp         = toFloatArray(udpTelemetryData.sTyreTreadTemp, 1);
            existingState.mTyreLayerTemp         = toFloatArray(udpTelemetryData.sTyreLayerTemp, 1);
            existingState.mTyreCarcassTemp       = toFloatArray(udpTelemetryData.sTyreCarcassTemp, 1);
            existingState.mTyreRimTemp           = toFloatArray(udpTelemetryData.sTyreRimTemp, 1);
            existingState.mTyreInternalAirTemp   = toFloatArray(udpTelemetryData.sTyreInternalAirTemp, 1);
            existingState.mWheelLocalPosition    = udpTelemetryData.sWheelLocalPositionY;
            existingState.mRideHeight            = udpTelemetryData.sRideHeight;
            existingState.mSuspensionTravel      = udpTelemetryData.sSuspensionTravel;
            existingState.mSuspensionVelocity    = udpTelemetryData.sSuspensionVelocity;
            existingState.mAirPressure           = toFloatArray(udpTelemetryData.sAirPressure, 1);

            existingState.mEngineSpeed        = udpTelemetryData.sEngineSpeed;
            existingState.mEngineTorque       = udpTelemetryData.sEngineTorque;
            existingState.mEnforcedPitStopLap = (uint)udpTelemetryData.sEnforcedPitStopLap;

            // Car Damage
            existingState.mCrashState   = udpTelemetryData.sCrashState;
            existingState.mAeroDamage   = (float)udpTelemetryData.sAeroDamage / 255f;
            existingState.mEngineDamage = (float)udpTelemetryData.sEngineDamage / 255f;

            // Weather
            existingState.mAmbientTemperature = udpTelemetryData.sAmbientTemperature;
            existingState.mTrackTemperature   = udpTelemetryData.sTrackTemperature;
            existingState.mRainDensity        = (float)udpTelemetryData.sRainDensity / 255f;
            existingState.mWindSpeed          = udpTelemetryData.sWindSpeed * 2;
            existingState.mWindDirectionX     = (float)udpTelemetryData.sWindDirectionX / 127f;
            existingState.mWindDirectionY     = (float)udpTelemetryData.sWindDirectionY / 127f;
            //existingState.mCloudBrightness = udpTelemetryData.sCloudBrightness / 255;

            if (existingState.mParticipantData == null)
            {
                existingState.mParticipantData = new pCarsAPIParticipantStruct[udpTelemetryData.sParticipantInfo.Count()];
            }
            for (int i = 0; i < udpTelemetryData.sParticipantInfo.Count(); i++)
            {
                sParticipantInfo          newPartInfo      = udpTelemetryData.sParticipantInfo[i];
                Boolean                   isActive         = (newPartInfo.sRacePosition >> 7) == 1;
                pCarsAPIParticipantStruct existingPartInfo = existingState.mParticipantData[i];
                existingPartInfo.mIsActive = isActive;
                if (isActive)
                {
                    existingPartInfo.mCurrentLap         = newPartInfo.sCurrentLap;
                    existingPartInfo.mCurrentLapDistance = newPartInfo.sCurrentLapDistance;
                    existingPartInfo.mLapsCompleted      = (uint)newPartInfo.sLapsCompleted & 127;
                    // TODO: there's a 'lapInvalidated' flag here but nowhere to put it in the existing struct
                    Boolean lapInvalidated = (newPartInfo.sLapsCompleted >> 7) == 1;
                    existingPartInfo.mRacePosition  = (uint)newPartInfo.sRacePosition & 127;
                    existingPartInfo.mCurrentSector = (uint)newPartInfo.sSector & 7;

                    // and now the bit magic for the extra position precision...
                    float[] newWorldPositions = toFloatArray(newPartInfo.sWorldPosition, 1);
                    float   xAdjustment       = ((float)((uint)newPartInfo.sSector >> 3 & 3)) / 4f;
                    float   zAdjustment       = ((float)((uint)newPartInfo.sSector >> 5 & 3)) / 4f;
                    newWorldPositions[0] = newWorldPositions[0] + xAdjustment;
                    newWorldPositions[2] = newWorldPositions[2] + zAdjustment;
                    if (!existingState.hasNewPositionData && i != udpTelemetryData.sViewedParticipantIndex &&
                        (existingPartInfo.mWorldPosition == null || (newWorldPositions[0] != existingPartInfo.mWorldPosition[0] || newWorldPositions[2] != existingPartInfo.mWorldPosition[2])))
                    {
                        existingState.hasNewPositionData = true;
                    }
                    existingPartInfo.mWorldPosition = newWorldPositions;

                    // TODO: LastSectorTime is now in the UDP data, but there's no slot for this in the participants struct
                    // existingPartInfo.mLastSectorTime = newPartInfo.sLastSectorTime;
                }
                else
                {
                    existingPartInfo.mWorldPosition = new float[] { 0, 0, 0 };
                }
                existingState.mParticipantData[i] = existingPartInfo;
            }

            // TODO: buttons
            return(existingState);
        }