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);
        }
        public override Object ReadGameData()
        {
            PCarsStructWrapper structWrapper = new PCarsStructWrapper();

            lock (this)
            {
                if (!initialised)
                {
                    if (!InitialiseInternal())
                    {
                        throw new Exception("Failed to initialise UDP client");
                    }
                }
                //previousGameState = StructHelper.Clone(currentGameState);
                currentGameState = StructHelper.Clone(workingGameState);
            }
            structWrapper.data = currentGameState;

            return(structWrapper);
        }