英文:
How can I unpack a F1 23 Telemetry Packet to a dictionary correctly?
问题
为了提供上下文,我正在尝试调整f1_22_telemetry代码以适应新的F1 23 UDP套接字(我的存储库).
这是EA的文档链接: https://answers.ea.com/t5/General-Discussion/F1-23-UDP-Specification/td-p/12632888?attachment-id=704910
一些数据包似乎工作正常,但在转换为字典后,其他数据包显示奇怪/错误的值。
我使用了Chris Hannam的代码,并根据EA文档中的信息更改了数据包的类。因此,逻辑应该是相同的,并且在F1 22套接字中正常工作。
示例: PacketSessionData
我的类:
class PacketSessionData(Packet):
    _fields_ = [
        ("header", PacketHeader),
        ("weather", ctypes.c_uint8),
        ("trackTemperature", ctypes.c_int8),
        ("airTemperature", ctypes.c_int8),
        ("totalLaps", ctypes.c_uint8),
        ("trackLength", ctypes.c_uint16),
        ("sessionType", ctypes.c_uint8),
        ("trackId", ctypes.c_int8),
        ("formula", ctypes.c_uint8),
        ("sessionTimeLeft", ctypes.c_uint16),
        ("sessionDuration", ctypes.c_uint16),
        ("pitSpeedLimit", ctypes.c_uint8),
        ("gamePaused", ctypes.c_uint8),
        ("isSpectating", ctypes.c_uint8),
        ("spectatorCarIndex", ctypes.c_uint8),
        ("sliProNativeSupport", ctypes.c_uint8),
        ("numMarshalZones", ctypes.c_uint8),
        ("marshalZones", MarshalZone * 21),
        ("safetyCarStatus", ctypes.c_uint8),
        ("networkGame", ctypes.c_uint8),
        ("numWeatherForecastSamples", ctypes.c_uint8),
        ("forecastAccuracy", ctypes.c_uint8),
        ("aiDifficulty", ctypes.c_uint8),
        ("seasonLinkIdentifier", ctypes.c_uint32),
        ("weekendLinkIdentifier", ctypes.c_uint32),
        ("sessionLinkIdentifier", ctypes.c_uint32),
        ("pitStopWindowIdealLap", ctypes.c_uint8),
        ("pitStopWindowLatestLap", ctypes.c_uint8),
        ("pitStopRejoinPosition", ctypes.c_uint8),
        ("steeringAssist", ctypes.c_uint8),
        ("brakingAssist", ctypes.c_uint8),
        ("gearboxAssist", ctypes.c_uint8),
        ("pitAssist", ctypes.c_uint8),
        ("pitReleaseAssist", ctypes.c_uint8),
        ("ERSAssist", ctypes.c_uint8),
        ("DRSAssist", ctypes.c_uint8),
        ("dynamicRacingLine", ctypes.c_uint8),
        ("dynamicRacingLineType", ctypes.c_uint8),
        ("gameMode", ctypes.c_uint8),
        ("ruleSet", ctypes.c_uint8),
        ("timeOfDay", ctypes.c_uint32),
        ("sessionLength", ctypes.c_uint8),
        ("speedUnitsLeadPlayer", ctypes.c_uint8),
        ("speedUnitsSecondaryPlayer", ctypes.c_uint8),
        ("temperatureUnitsLeadPlayer", ctypes.c_uint8),
        ("temperatureUnitsSecondaryPlayer", ctypes.c_uint8),
        ("numSafetyCarPeriods", ctypes.c_uint8),
        ("numVirtualSafetyCarPeriods", ctypes.c_uint8),
        ("numRedFlagPeriods", ctypes.c_uint8),
    ]
文档:
struct PacketSessionData
{
    PacketHeader    m_header;               	// Header
    uint8           m_weather;              	// 天气 - 0 = 晴朗, 1 = 轻云, 2 = 阴天, 3 = 小雨, 4 = 大雨, 5 = 暴风雨
    int8	        m_trackTemperature;    	// 赛道温度(摄氏度)
    int8	        m_airTemperature;      	// 空气温度(摄氏度)
    uint8           m_totalLaps;           	// 此比赛的总圈数
    uint16          m_trackLength;           // 赛道长度(米)
    uint8           m_sessionType;         	// 0 = 未知, 1 = P1, 2 = P2, 3 = P3, 4 = 短P
                                          	// 5 = Q1, 6 = Q2, 7 = Q3, 8 = 短Q, 9 = OSQ
                                          	// 10 = R, 11 = R2, 12 = R3, 13 = 计时赛
    int8            m_trackId;         		// -1表示未知,见附录
    uint8           m_formula;              	// Formula, 0 = 现代F1, 1 = 经典F1, 2 = F2,
                                             // 3 = 通用F1, 4 = Beta, 5 = 超级跑车
                                             // 6 = 电子竞技, 7 = F2 2021
    uint16          m_sessionTimeLeft;    	// 会话剩余时间(秒)
    uint16          m_sessionDuration;     	// 会话持续时间(秒)
    uint8           m_pitSpeedLimit;      	// 限制坑道速度(千米每小时)
    uint8           m_gamePaused;          	// 游戏是否暂停(仅限网络游戏)
    uint8           m_isSpectating;        	// 玩家是否在旁观
    uint8           m_spectatorCarIndex;  	// 正在旁观的赛车索引
    uint8           m_sliProNativeSupport;	// SLI Pro支持,0 = 不活动,1 = 活动
    uint8           m_numMarshalZones;      // 要跟踪的警告区域数量
    MarshalZone     m_marshalZones[21];      // 警告区域列表 - 最多21个
    uint8           m_safetyCarStatus;       // 0 = 无安全车,1 = 完全安全车
                                             // 2 = 虚拟安全车,3 = 成组环节
    uint8           m_networkGame;           // 0 = 离线,1 = 在线
    uint8           m_numWeatherForecastSamples; //
<details>
<summary>英文:</summary>
For context: I'm trying to adjust the [f1_22_telemetry](https://github.com/chrishannam/f1-22-telemetry) code to the new F1 23 UDP Socket ([my repo](https://github.com/JulMai/f1_telemetry_socket)).<br>
Here is the Documentation by EA: https://answers.ea.com/t5/General-Discussion/F1-23-UDP-Specification/td-p/12632888?attachment-id=704910
<br>
Some packets appear to be working fine, but others show weird/wrong values after being converted to a dictionary.
I took Chris Hannam's Code and changed the classes for the Packets with the information in the EA documentation. So the logic should be the same and worked fine with the F1 22 Socket.
Example: PacketSessionData<br>
My Class:
class PacketSessionData(Packet):
fields = [
("header", PacketHeader),
("weather", ctypes.c_uint8),
("trackTemperature", ctypes.c_int8),
("airTemperature", ctypes.c_int8),
("totalLaps", ctypes.c_uint8),
("trackLength", ctypes.c_uint16),
("sessionType", ctypes.c_uint8),
("trackId", ctypes.c_int8),
("formula", ctypes.c_uint8),
("sessionTimeLeft", ctypes.c_uint16),
("sessionDuration", ctypes.c_uint16),
("pitSpeedLimit", ctypes.c_uint8),
("gamePaused", ctypes.c_uint8),
("isSpectating", ctypes.c_uint8),
("spectatorCarIndex", ctypes.c_uint8),
("sliProNativeSupport", ctypes.c_uint8),
("numMarshalZones", ctypes.c_uint8),
("marshalZones", MarshalZone * 21),
("safetyCarStatus", ctypes.c_uint8),
("networkGame", ctypes.c_uint8),
("numWeatherForecastSamples", ctypes.c_uint8),
("forecastAccuracy", ctypes.c_uint8),
("aiDifficulty", ctypes.c_uint8),
("seasonLinkIdentifier", ctypes.c_uint32),
("weekendLinkIdentifier", ctypes.c_uint32),
("sessionLinkIdentifier", ctypes.c_uint32),
("pitStopWindowIdealLap", ctypes.c_uint8),
("pitStopWindowLatestLap", ctypes.c_uint8),
("pitStopRejoinPosition", ctypes.c_uint8),
("steeringAssist", ctypes.c_uint8),
("brakingAssist", ctypes.c_uint8),
("gearboxAssist", ctypes.c_uint8),
("pitAssist", ctypes.c_uint8),
("pitReleaseAssist", ctypes.c_uint8),
("ERSAssist", ctypes.c_uint8),
("DRSAssist", ctypes.c_uint8),
("dynamicRacingLine", ctypes.c_uint8),
("dynamicRacingLineType", ctypes.c_uint8),
("gameMode", ctypes.c_uint8),
("ruleSet", ctypes.c_uint8),
("timeOfDay", ctypes.c_uint32),
("sessionLength", ctypes.c_uint8),
("speedUnitsLeadPlayer", ctypes.c_uint8),
("speedUnitsSecondaryPlayer", ctypes.c_uint8),
("numSafetyCarPeriods", ctypes.c_uint8),
("numRedFlagPeriods", ctypes.c_uint8),
]
The Documentation:
struct PacketSessionData
{
PacketHeader    m_header;               	// Header
uint8           m_weather;              	// Weather - 0 = clear, 1 = light cloud, 2 = overcast
                                        	// 3 = light rain, 4 = heavy rain, 5 = storm
int8	            m_trackTemperature;    	// Track temp. in degrees celsius
int8	            m_airTemperature;      	// Air temp. in degrees celsius
uint8           m_totalLaps;           	// Total number of laps in this race
uint16          m_trackLength;           	// Track length in metres
uint8           m_sessionType;         	// 0 = unknown, 1 = P1, 2 = P2, 3 = P3, 4 = Short P
                                        	// 5 = Q1, 6 = Q2, 7 = Q3, 8 = Short Q, 9 = OSQ
                                        	// 10 = R, 11 = R2, 12 = R3, 13 = Time Trial
int8            m_trackId;         		// -1 for unknown, see appendix
uint8           m_formula;                  	// Formula, 0 = F1 Modern, 1 = F1 Classic, 2 = F2,
                                             // 3 = F1 Generic, 4 = Beta, 5 = Supercars
// 6 = Esports, 7 = F2 2021
uint16          m_sessionTimeLeft;    	// Time left in session in seconds
uint16          m_sessionDuration;     	// Session duration in seconds
uint8           m_pitSpeedLimit;      	// Pit speed limit in kilometres per hour
uint8           m_gamePaused;                // Whether the game is paused – network game only
uint8           m_isSpectating;        	// Whether the player is spectating
uint8           m_spectatorCarIndex;  	// Index of the car being spectated
uint8           m_sliProNativeSupport;	// SLI Pro support, 0 = inactive, 1 = active
uint8           m_numMarshalZones;         	// Number of marshal zones to follow
MarshalZone     m_marshalZones[21];         	// List of marshal zones – max 21
uint8           m_safetyCarStatus;           // 0 = no safety car, 1 = full
// 2 = virtual, 3 = formation lap
uint8           m_networkGame;               // 0 = offline, 1 = online
uint8           m_numWeatherForecastSamples; // Number of weather samples to follow
WeatherForecastSample m_weatherForecastSamples[56];   // Array of weather forecast samples
uint8           m_forecastAccuracy;          // 0 = Perfect, 1 = Approximate
uint8           m_aiDifficulty;              // AI Difficulty rating – 0-110
uint32          m_seasonLinkIdentifier;      // Identifier for season - persists across saves
uint32          m_weekendLinkIdentifier;     // Identifier for weekend - persists across saves
uint32          m_sessionLinkIdentifier;     // Identifier for session - persists across saves
uint8           m_pitStopWindowIdealLap;     // Ideal lap to pit on for current strategy (player)
uint8           m_pitStopWindowLatestLap;    // Latest lap to pit on for current strategy (player)
uint8           m_pitStopRejoinPosition;     // Predicted position to rejoin at (player)
uint8           m_steeringAssist;            // 0 = off, 1 = on
uint8           m_brakingAssist;             // 0 = off, 1 = low, 2 = medium, 3 = high
uint8           m_gearboxAssist;             // 1 = manual, 2 = manual & suggested gear, 3 = auto
uint8           m_pitAssist;                 // 0 = off, 1 = on
uint8           m_pitReleaseAssist;          // 0 = off, 1 = on
uint8           m_ERSAssist;                 // 0 = off, 1 = on
uint8           m_DRSAssist;                 // 0 = off, 1 = on
uint8           m_dynamicRacingLine;         // 0 = off, 1 = corners only, 2 = full
uint8           m_dynamicRacingLineType;     // 0 = 2D, 1 = 3D
uint8           m_gameMode;                  // Game mode id - see appendix
uint8           m_ruleSet;                   // Ruleset - see appendix
uint32          m_timeOfDay;                 // Local time of day - minutes since midnight
uint8           m_sessionLength;             // 0 = None, 2 = Very Short, 3 = Short, 4 = Medium
// 5 = Medium Long, 6 = Long, 7 = Full
uint8           m_speedUnitsLeadPlayer;             // 0 = MPH, 1 = KPH
uint8           m_temperatureUnitsLeadPlayer;       // 0 = Celsius, 1 = Fahrenheit
uint8           m_speedUnitsSecondaryPlayer;        // 0 = MPH, 1 = KPH
uint8           m_temperatureUnitsSecondaryPlayer;  // 0 = Celsius, 1 = Fahrenheit
uint8           m_numSafetyCarPeriods;              // Number of safety cars called during session
uint8           m_numVirtualSafetyCarPeriods;       // Number of virtual safety cars called
uint8           m_numRedFlagPeriods;                // Number of red flags called during session
};
When I receive a SessionData-Packet, there are values that should be impossible to get according to the documentation.
e.g.: <br>
gearBoxAssist: 33<br>
pitReleaseAssist: 27<br>
So I assume something goes wrong during translating the bytes to the dictionary.
</details>
# 答案1
**得分**: 1
以下是代码的翻译部分:
```py
import ctypes as ct
class PacketHeader(ct.LittleEndianStructure):
    _pack_ = 1
    _fields_ = (('m_packetFormat', ct.c_uint16),
                ('m_gameYear', ct.c_uint8),
                ('m_gameMajorVersion', ct.c_uint8),
                ('m_gameMinorVersion', ct.c_uint8),
                ('m_packetVersion', ct.c_uint8),
                ('m_packetId', ct.c_uint8),
                ('m_sessionUID', ct.c_uint64),
                ('m_sessionTime', ct.c_float),
                ('m_frameIdentifier', ct.c_uint32),
                ('m_overallFrameIdentifier', ct.c_uint32),
                ('m_playerCarIndex', ct.c_uint8),
                ('m_secondaryPlayerCarIndex', ct.c_uint8))
class MarshalZone(ct.LittleEndianStructure):
    _pack_ = 1
    _fields_ = ('m_zoneStart', ct.c_float),
                ('m_zoneFlag', ct.c_int8))
class WeatherForecastSample(ct.LittleEndianStructure):
    _pack_ = 1
    _fields_ = (('m_sessionType', ct.c_uint8),
                ('m_timeOffset', ct.c_uint8),
                ('m_weather', ct.c_uint8),
                ('m_trackTemperature', ct.c_int8),
                ('m_trackTemperatureChange', ct.c_int8),
                ('m_airTemperature', ct.c_int8),
                ('m_airTemperatureChange', ct.c_int8),
                ('m_rainPercentage', ct.c_uint8))
class PacketSessionData(ct.LittleEndianStructure):
    _pack_ = 1
    _fields_ = (('m_header', PacketHeader),
                ('m_weather', ct.c_uint8),
                ('m_trackTemperature', ct.c_int8),
                ('m_airTemperature', ct.c_int8),
                ('m_totalLaps', ct.c_uint8),
                ('m_trackLength', ct.c_uint16),
                ('m_sessionType', ct.c_uint8),
                ('m_trackId', ct.c_int8),
                ('m_formula', ct.c_uint8),
                ('m_sessionTimeLeft', ct.c_uint16),
                ('m_sessionDuration', ct.c_uint16),
                ('m_pitSpeedLimit', ct.c_uint8),
                ('m_gamePaused', ct.c_uint8),
                ('m_isSpectating', ct.c_uint8),
                ('m_spectatorCarIndex', ct.c_uint8),
                ('m_sliProNativeSupport', ct.c_uint8),
                ('m_numMarshalZones', ct.c_uint8),
                ('m_marshalZones', MarshalZone * 21),
                ('m_safetyCarStatus', ct.c_uint8),
                ('m_networkGame', ct.c_uint8),
                ('m_numWeatherForecastSamples', ct.c_uint8),
                ('m_weatherForecastSamples', WeatherForecastSample * 56),
                ('m_forecastAccuracy', ct.c_uint8),
                ('m_aiDifficulty', ct.c_uint8),
                ('m_seasonLinkIdentifier', ct.c_uint32),
                ('m_weekendLinkIdentifier', ct.c_uint32),
                ('m_sessionLinkIdentifier', ct.c_uint32),
                ('m_pitStopWindowIdealLap', ct.c_uint8),
                ('m_pitStopWindowLatestLap', ct.c_uint8),
                ('m_pitStopRejoinPosition', ct.c_uint8),
                ('m_steeringAssist', ct.c_uint8),
                ('m_brakingAssist', ct.c_uint8),
                ('m_gearboxAssist', ct.c_uint8),
                ('m_pitAssist', ct.c_uint8),
                ('m_pitReleaseAssist', ct.c_uint8),
                ('m_ERSAssist', ct.c_uint8),
                ('m_DRSAssist', ct.c_uint8),
                ('m_dynamicRacingLine', ct.c_uint8),
                ('m_dynamicRacingLineType', ct.c_uint8),
                ('m_gameMode', ct.c_uint8),
                ('m_ruleSet', ct.c_uint8),
                ('m_timeOfDay', ct.c_uint32),
                ('m_sessionLength', ct.c_uint8),
                ('m_speedUnitsLeadPlayer', ct.c_uint8),
                ('m_temperatureUnitsLeadPlayer', ct.c_uint8),
                ('m_speedUnitsSecondaryPlayer', ct.c_uint8),
                ('m_temperatureUnitsSecondaryPlayer', ct.c_uint8),
                ('m_numSafetyCarPeriods', ct.c_uint8),
                ('m_numVirtualSafetyCarPeriods', ct.c_uint8),
                ('m_numRedFlagPeriods', ct.c_uint8))
assert ct.sizeof(PacketSessionData) == 644  # 用于验证
英文:
From the documentation:
> Packet Types
>
> [...] Please note that all values are encoded using Little Endian format. All data is packed.
> Session Packet
>
> [...] Size: 644 bytes
Here's a complete definition that matches the documented structure size of 644 bytes.
The problems were:
- Sub-structures missing.
 - Structures weren't packed.
 - Four members of 
PacketSessionDatawere missing. 
LittleEndianStructure is used in the off-chance portability is needed.
import ctypes as ct
class PacketHeader(ct.LittleEndianStructure):
    _pack_ = 1
    _fields_ = (('m_packetFormat', ct.c_uint16),
                ('m_gameYear', ct.c_uint8),
                ('m_gameMajorVersion', ct.c_uint8),
                ('m_gameMinorVersion', ct.c_uint8),
                ('m_packetVersion', ct.c_uint8),
                ('m_packetId', ct.c_uint8),
                ('m_sessionUID', ct.c_uint64),
                ('m_sessionTime', ct.c_float),
                ('m_frameIdentifier', ct.c_uint32),
                ('m_overallFrameIdentifier', ct.c_uint32),
                ('m_playerCarIndex', ct.c_uint8),
                ('m_secondaryPlayerCarIndex', ct.c_uint8))
class MarshalZone(ct.LittleEndianStructure):
    _pack_ = 1
    _fields_ = (('m_zoneStart', ct.c_float),
                ('m_zoneFlag', ct.c_int8))
class WeatherForecastSample(ct.LittleEndianStructure):
    _pack_ = 1
    _fields_ = (('m_sessionType', ct.c_uint8),
                ('m_timeOffset', ct.c_uint8),
                ('m_weather', ct.c_uint8),
                ('m_trackTemperature', ct.c_int8),
                ('m_trackTemperatureChange', ct.c_int8),
                ('m_airTemperature', ct.c_int8),
                ('m_airTemperatureChange', ct.c_int8),
                ('m_rainPercentage', ct.c_uint8))
class PacketSessionData(ct.LittleEndianStructure):
    _pack_ = 1
    _fields_ = (('m_header', PacketHeader),
                ('m_weather', ct.c_uint8),
                ('m_trackTemperature', ct.c_int8),
                ('m_airTemperature', ct.c_int8),
                ('m_totalLaps', ct.c_uint8),
                ('m_trackLength', ct.c_uint16),
                ('m_sessionType', ct.c_uint8),
                ('m_trackId', ct.c_int8),
                ('m_formula', ct.c_uint8),
                ('m_sessionTimeLeft', ct.c_uint16),
                ('m_sessionDuration', ct.c_uint16),
                ('m_pitSpeedLimit', ct.c_uint8),
                ('m_gamePaused', ct.c_uint8),
                ('m_isSpectating', ct.c_uint8),
                ('m_spectatorCarIndex', ct.c_uint8),
                ('m_sliProNativeSupport', ct.c_uint8),
                ('m_numMarshalZones', ct.c_uint8),
                ('m_marshalZones', MarshalZone * 21),
                ('m_safetyCarStatus', ct.c_uint8),
                ('m_networkGame', ct.c_uint8),
                ('m_numWeatherForecastSamples', ct.c_uint8),
                ('m_weatherForecastSamples', WeatherForecastSample * 56),  # missing
                ('m_forecastAccuracy', ct.c_uint8),
                ('m_aiDifficulty', ct.c_uint8),
                ('m_seasonLinkIdentifier', ct.c_uint32),
                ('m_weekendLinkIdentifier', ct.c_uint32),
                ('m_sessionLinkIdentifier', ct.c_uint32),
                ('m_pitStopWindowIdealLap', ct.c_uint8),
                ('m_pitStopWindowLatestLap', ct.c_uint8),
                ('m_pitStopRejoinPosition', ct.c_uint8),
                ('m_steeringAssist', ct.c_uint8),
                ('m_brakingAssist', ct.c_uint8),
                ('m_gearboxAssist', ct.c_uint8),
                ('m_pitAssist', ct.c_uint8),
                ('m_pitReleaseAssist', ct.c_uint8),
                ('m_ERSAssist', ct.c_uint8),
                ('m_DRSAssist', ct.c_uint8),
                ('m_dynamicRacingLine', ct.c_uint8),
                ('m_dynamicRacingLineType', ct.c_uint8),
                ('m_gameMode', ct.c_uint8),
                ('m_ruleSet', ct.c_uint8),
                ('m_timeOfDay', ct.c_uint32),
                ('m_sessionLength', ct.c_uint8),
                ('m_speedUnitsLeadPlayer', ct.c_uint8),
                ('m_temperatureUnitsLeadPlayer', ct.c_uint8),  # missing
                ('m_speedUnitsSecondaryPlayer', ct.c_uint8),
                ('m_temperatureUnitsSecondaryPlayer', ct.c_uint8),  # missing
                ('m_numSafetyCarPeriods', ct.c_uint8),
                ('m_numVirtualSafetyCarPeriods', ct.c_uint8),  # missing
                ('m_numRedFlagPeriods', ct.c_uint8))
assert ct.sizeof(PacketSessionData) == 644  # for verification
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论