diff --git a/gt7dashboard/gt7communication.py b/gt7dashboard/gt7communication.py index 4b8a881..e79d1f2 100644 --- a/gt7dashboard/gt7communication.py +++ b/gt7dashboard/gt7communication.py @@ -13,140 +13,11 @@ from salsa20 import Salsa20_xor -from gt7dashboard.gt7helper import seconds_to_lap_time +from gt7dashboard.gt7data import GTData +from gt7dashboard.gt7helper import divide_laps, seconds_to_lap_time, should_save_lap from gt7dashboard.gt7lap import Lap -class GTData: - def __init__(self, ddata): - if not ddata: - return - - self.package_id = struct.unpack('i', ddata[0x70:0x70 + 4])[0] - self.best_lap = struct.unpack('i', ddata[0x78:0x78 + 4])[0] - self.last_lap = struct.unpack('i', ddata[0x7C:0x7C + 4])[0] - self.current_lap = struct.unpack('h', ddata[0x74:0x74 + 2])[0] - self.current_gear = struct.unpack('B', ddata[0x90:0x90 + 1])[0] & 0b00001111 - self.suggested_gear = struct.unpack('B', ddata[0x90:0x90 + 1])[0] >> 4 - self.fuel_capacity = struct.unpack('f', ddata[0x48:0x48 + 4])[0] - self.current_fuel = struct.unpack('f', ddata[0x44:0x44 + 4])[0] # fuel - self.boost = struct.unpack('f', ddata[0x50:0x50 + 4])[0] - 1 - - self.tyre_diameter_FL = struct.unpack('f', ddata[0xB4:0xB4 + 4])[0] - self.tyre_diameter_FR = struct.unpack('f', ddata[0xB8:0xB8 + 4])[0] - self.tyre_diameter_RL = struct.unpack('f', ddata[0xBC:0xBC + 4])[0] - self.tyre_diameter_RR = struct.unpack('f', ddata[0xC0:0xC0 + 4])[0] - - self.type_speed_FL = abs(3.6 * self.tyre_diameter_FL * struct.unpack('f', ddata[0xA4:0xA4 + 4])[0]) - self.type_speed_FR = abs(3.6 * self.tyre_diameter_FR * struct.unpack('f', ddata[0xA8:0xA8 + 4])[0]) - self.type_speed_RL = abs(3.6 * self.tyre_diameter_RL * struct.unpack('f', ddata[0xAC:0xAC + 4])[0]) - self.tyre_speed_RR = abs(3.6 * self.tyre_diameter_RR * struct.unpack('f', ddata[0xB0:0xB0 + 4])[0]) - - self.car_speed = 3.6 * struct.unpack('f', ddata[0x4C:0x4C + 4])[0] - - if self.car_speed > 0: - self.tyre_slip_ratio_FL = '{:6.2f}'.format(self.type_speed_FL / self.car_speed) - self.tyre_slip_ratio_FR = '{:6.2f}'.format(self.type_speed_FR / self.car_speed) - self.tyre_slip_ratio_RL = '{:6.2f}'.format(self.type_speed_RL / self.car_speed) - self.tyre_slip_ratio_RR = '{:6.2f}'.format(self.tyre_speed_RR / self.car_speed) - - self.time_on_track = timedelta( - seconds=round(struct.unpack('i', ddata[0x80:0x80 + 4])[0] / 1000)) # time of day on track - - self.total_laps = struct.unpack('h', ddata[0x76:0x76 + 2])[0] # total laps - - self.current_position = struct.unpack('h', ddata[0x84:0x84 + 2])[0] # current position - self.total_positions = struct.unpack('h', ddata[0x86:0x86 + 2])[0] # total positions - - self.car_id = struct.unpack('i', ddata[0x124:0x124 + 4])[0] # car id - - self.throttle = struct.unpack('B', ddata[0x91:0x91 + 1])[0] / 2.55 # throttle - self.rpm = struct.unpack('f', ddata[0x3C:0x3C + 4])[0] # rpm - self.rpm_rev_warning = struct.unpack('H', ddata[0x88:0x88 + 2])[0] # rpm rev warning - - self.brake = struct.unpack('B', ddata[0x92:0x92 + 1])[0] / 2.55 # brake - - self.boost = struct.unpack('f', ddata[0x50:0x50 + 4])[0] - 1 # boost - - self.rpm_rev_limiter = struct.unpack('H', ddata[0x8A:0x8A + 2])[0] # rpm rev limiter - - self.estimated_top_speed = struct.unpack('h', ddata[0x8C:0x8C + 2])[0] # estimated top speed - - self.clutch = struct.unpack('f', ddata[0xF4:0xF4 + 4])[0] # clutch - self.clutch_engaged = struct.unpack('f', ddata[0xF8:0xF8 + 4])[0] # clutch engaged - self.rpm_after_clutch = struct.unpack('f', ddata[0xFC:0xFC + 4])[0] # rpm after clutch - - self.oil_temp = struct.unpack('f', ddata[0x5C:0x5C + 4])[0] # oil temp - self.water_temp = struct.unpack('f', ddata[0x58:0x58 + 4])[0] # water temp - - self.oil_pressure = struct.unpack('f', ddata[0x54:0x54 + 4])[0] # oil pressure - self.ride_height = 1000 * struct.unpack('f', ddata[0x38:0x38 + 4])[0] # ride height - - self.tyre_temp_FL = struct.unpack('f', ddata[0x60:0x60 + 4])[0] # tyre temp FL - self.tyre_temp_FR = struct.unpack('f', ddata[0x64:0x64 + 4])[0] # tyre temp FR - - self.suspension_fl = struct.unpack('f', ddata[0xC4:0xC4 + 4])[0] # suspension FL - self.suspension_fr = struct.unpack('f', ddata[0xC8:0xC8 + 4])[0] # suspension FR - - self.tyre_temp_rl = struct.unpack('f', ddata[0x68:0x68 + 4])[0] # tyre temp RL - self.tyre_temp_rr = struct.unpack('f', ddata[0x6C:0x6C + 4])[0] # tyre temp RR - - self.suspension_rl = struct.unpack('f', ddata[0xCC:0xCC + 4])[0] # suspension RL - self.suspension_rr = struct.unpack('f', ddata[0xD0:0xD0 + 4])[0] # suspension RR - - self.gear_1 = struct.unpack('f', ddata[0x104:0x104 + 4])[0] # 1st gear - self.gear_2 = struct.unpack('f', ddata[0x108:0x108 + 4])[0] # 2nd gear - self.gear_3 = struct.unpack('f', ddata[0x10C:0x10C + 4])[0] # 3rd gear - self.gear_4 = struct.unpack('f', ddata[0x110:0x110 + 4])[0] # 4th gear - self.gear_5 = struct.unpack('f', ddata[0x114:0x114 + 4])[0] # 5th gear - self.gear_6 = struct.unpack('f', ddata[0x118:0x118 + 4])[0] # 6th gear - self.gear_7 = struct.unpack('f', ddata[0x11C:0x11C + 4])[0] # 7th gear - self.gear_8 = struct.unpack('f', ddata[0x120:0x120 + 4])[0] # 8th gear - - # self.struct.unpack('f', ddata[0x100:0x100+4])[0] # ??? gear - - self.position_x = struct.unpack('f', ddata[0x04:0x04 + 4])[0] # pos X - self.position_y = struct.unpack('f', ddata[0x08:0x08 + 4])[0] # pos Y - self.position_z = struct.unpack('f', ddata[0x0C:0x0C + 4])[0] # pos Z - - self.velocity_x = struct.unpack('f', ddata[0x10:0x10 + 4])[0] # velocity X - self.velocity_y = struct.unpack('f', ddata[0x14:0x14 + 4])[0] # velocity Y - self.velocity_z = struct.unpack('f', ddata[0x18:0x18 + 4])[0] # velocity Z - - self.rotation_pitch = struct.unpack('f', ddata[0x1C:0x1C + 4])[0] # rot Pitch - self.rotation_yaw = struct.unpack('f', ddata[0x20:0x20 + 4])[0] # rot Yaw - self.rotation_roll = struct.unpack('f', ddata[0x24:0x24 + 4])[0] # rot Roll - - self.angular_velocity_x = struct.unpack('f', ddata[0x2C:0x2C + 4])[0] # angular velocity X - self.angular_velocity_y = struct.unpack('f', ddata[0x30:0x30 + 4])[0] # angular velocity Y - self.angular_velocity_z = struct.unpack('f', ddata[0x34:0x34 + 4])[0] # angular velocity Z - - self.is_paused = bin(struct.unpack('B', ddata[0x8E:0x8E + 1])[0])[-2] == '1' - self.in_race = bin(struct.unpack('B', ddata[0x8E:0x8E + 1])[0])[-1] == '1' - - # struct.unpack('f', ddata[0x28:0x28+4])[0] # rot ??? - - # bin(struct.unpack('B', ddata[0x8E:0x8E+1])[0])[2:] # various flags (see https://github.com/Nenkai/PDTools/blob/master/PDTools.SimulatorInterface/SimulatorPacketG7S0.cs) - # bin(struct.unpack('B', ddata[0x8F:0x8F+1])[0])[2:] # various flags (see https://github.com/Nenkai/PDTools/blob/master/PDTools.SimulatorInterface/SimulatorPacketG7S0.cs) - # bin(struct.unpack('B', ddata[0x93:0x93+1])[0])[2:] # 0x93 = ??? - - # struct.unpack('f', ddata[0x94:0x94+4])[0] # 0x94 = ??? - # struct.unpack('f', ddata[0x98:0x98+4])[0] # 0x98 = ??? - # struct.unpack('f', ddata[0x9C:0x9C+4])[0] # 0x9C = ??? - # struct.unpack('f', ddata[0xA0:0xA0+4])[0] # 0xA0 = ??? - - # struct.unpack('f', ddata[0xD4:0xD4+4])[0] # 0xD4 = ??? - # struct.unpack('f', ddata[0xD8:0xD8+4])[0] # 0xD8 = ??? - # struct.unpack('f', ddata[0xDC:0xDC+4])[0] # 0xDC = ??? - # struct.unpack('f', ddata[0xE0:0xE0+4])[0] # 0xE0 = ??? - - # struct.unpack('f', ddata[0xE4:0xE4+4])[0] # 0xE4 = ??? - # struct.unpack('f', ddata[0xE8:0xE8+4])[0] # 0xE8 = ??? - # struct.unpack('f', ddata[0xEC:0xEC+4])[0] # 0xEC = ??? - # struct.unpack('f', ddata[0xF0:0xF0+4])[0] # 0xF0 = ??? - - def to_json(self): - return json.dumps(self, indent=4, sort_keys=True, default=str) class Session(): def __init__(self): @@ -179,6 +50,7 @@ def __init__(self, playstation_ip): self.current_lap = Lap() self.session = Session() self.laps = [] + self.package_id = 0 self.last_data = GTData(None) # This is used to record race data in any case. This will override the "in_race" flag. @@ -201,49 +73,64 @@ def run(self): s.bind(('0.0.0.0', self.receive_port)) self._send_hb(s) s.settimeout(10) - previous_lap = -1 package_id = 0 package_nr = 0 while not self._shall_restart and self._shall_run: try: + # Receive data from the socket data, address = s.recvfrom(4096) + # Increment the package number package_nr = package_nr + 1 - ddata = salsa20_dec(data) - if len(ddata) > 0 and struct.unpack('i', ddata[0x70:0x70 + 4])[0] > package_id: - self.last_data = GTData(ddata) - self._last_time_data_received = time.time() + # Decrypt the received data using Salsa20 + ddata = salsa20_dec(data) - package_id = struct.unpack('i', ddata[0x70:0x70 + 4])[0] - bstlap = struct.unpack('i', ddata[0x78:0x78 + 4])[0] - lstlap = struct.unpack('i', ddata[0x7C:0x7C + 4])[0] - curlap = struct.unpack('h', ddata[0x74:0x74 + 2])[0] + # Check if the decrypted data length is greater than 0 and if the package ID is greater than the current package ID + if len(ddata) > 0 and struct.unpack('i', ddata[0x70:0x70 + 4])[0] > package_id: + # Update the last time data was received to the current time + self._last_time_data_received = time.time() + new_data_tl = GTData(ddata) - if curlap == 0: - self.session.special_packet_time = 0 + package_id = new_data_tl.package_id - if curlap > 0 and (self.last_data.in_race or self.always_record_data): + is_new_lap = divide_laps(self.last_data, new_data_tl) - if curlap != previous_lap: - # New lap - previous_lap = curlap + bstlap = new_data_tl.best_lap + lstlap = new_data_tl.last_lap - self.session.special_packet_time += lstlap - self.current_lap.lap_ticks * 1000.0 / 60.0 - self.session.best_lap = bstlap + self.current_lap.is_replay = self.always_record_data + if new_data_tl.current_lap == 0: + self.session.special_packet_time = 0 + + #(new_data_tl.in_race or self.always_record_data) + + if is_new_lap: + self.session.special_packet_time += lstlap - self.current_lap.lap_ticks * 1000.0 / 60.0 + self.session.best_lap = bstlap + + if new_data_tl.last_lap > 0: + # Regular finished laps (crossing the finish line in races or time trials) + # have their lap time stored in last_lap + self.current_lap.lap_finish_time = new_data_tl.last_lap + else: + # Manual laps have no time assigned, so take current live time as lap finish time. + # Finish time is tracked in seconds while live time is tracked in ms + self.current_lap.lap_finish_time = self.current_lap.lap_live_time * 1000 + + if should_save_lap(self.last_data, new_data_tl, self.current_lap): self.finish_lap() - - else: - curLapTime = 0 - # Reset lap self.current_lap = Lap() - self._log_data(self.last_data) + # Update the last received data with the new GTData + self.last_data = new_data_tl + self._log_data(new_data_tl) if package_nr > 100: self._send_hb(s) package_nr = 0 + except (OSError, TimeoutError) as e: # Handler for package exceptions self._send_hb(s) @@ -279,6 +166,10 @@ def get_last_data(self) -> GTData: if time.time() > timeout: break + def get_last_data_once(self) -> GTData: + if self.last_data is not None: + return self.last_data + def get_laps(self) -> List[Lap]: return self.laps @@ -292,8 +183,8 @@ def load_laps(self, laps: List[Lap], to_last_position = False, to_first_position def _log_data(self, data): - if not (data.in_race or self.always_record_data): - return + # if not (data.in_race or self.always_record_data): + # return if data.is_paused: return @@ -373,32 +264,23 @@ def _log_data(self, data): # Adapted from https://www.gtplanet.net/forum/threads/gt7-is-compatible-with-motion-rig.410728/post-13810797 self.current_lap.lap_live_time = (self.current_lap.lap_ticks * 1. / 60.) - (self.session.special_packet_time / 1000.) - self.current_lap.data_time.append(self.current_lap.lap_live_time) self.current_lap.car_id = data.car_id + def finish_lap(self, manual=False): """ Finishes a lap with info we only know after crossing the line after each lap """ - if manual: - # Manual laps have no time assigned, so take current live time as lap finish time. - # Finish time is tracked in seconds while live time is tracked in ms - self.current_lap.lap_finish_time = self.current_lap.lap_live_time * 1000 - else: - # Regular finished laps (crossing the finish line in races or time trials) - # have their lap time stored in last_lap - self.current_lap.lap_finish_time = self.last_data.last_lap - # Track recording meta data self.current_lap.is_replay = self.always_record_data self.current_lap.is_manual = manual self.current_lap.fuel_at_end = self.last_data.current_fuel self.current_lap.fuel_consumed = self.current_lap.fuel_at_start - self.current_lap.fuel_at_end - self.current_lap.lap_finish_time = self.current_lap.lap_finish_time + # self.current_lap.lap_finish_time = self.current_lap.lap_finish_time self.current_lap.total_laps = self.last_data.total_laps self.current_lap.title = seconds_to_lap_time(self.current_lap.lap_finish_time / 1000) self.current_lap.car_id = self.last_data.car_id @@ -406,18 +288,16 @@ def finish_lap(self, manual=False): # TODO Proper pythonic name self.current_lap.EstimatedTopSpeed = self.last_data.estimated_top_speed + self.current_lap.lap_end_timestamp = datetime.datetime.now() + # if len(self.laps) > 1: + # self.current_lap = equalizer_lap(self.laps[0], self.current_lap) - # Race is not in 0th lap, which is before starting the race. - # We will only persist those laps that have crossed the starting line at least once - # And those laps which have data for speed logged. This will prevent empty laps. - # TODO Correct this comment, this is about Laptime not lap numbers - if self.current_lap.lap_finish_time > 0 and len(self.current_lap.data_speed) > 0: - self.laps.insert(0, self.current_lap) + self.laps.insert(0, self.current_lap) - # Make a copy of this lap and call the callback function if set - if self.lap_callback_function: - self.lap_callback_function(copy.deepcopy(self.current_lap)) + # Make a copy of this lap and call the callback function if set + if self.lap_callback_function: + self.lap_callback_function(copy.deepcopy(self.current_lap)) # Reset current lap with an empty one self.current_lap = Lap() diff --git a/gt7dashboard/gt7data.py b/gt7dashboard/gt7data.py new file mode 100644 index 0000000..64b95c1 --- /dev/null +++ b/gt7dashboard/gt7data.py @@ -0,0 +1,140 @@ +from datetime import timedelta +import json +import struct + + +class GTData: + def __init__(self, ddata): + if not ddata: + ddata = bytearray(0x124 + 4) + + self.package_id = struct.unpack('i', ddata[0x70:0x70 + 4])[0] + self.best_lap = struct.unpack('i', ddata[0x78:0x78 + 4])[0] + self.last_lap = struct.unpack('i', ddata[0x7C:0x7C + 4])[0] + self.current_lap = struct.unpack('h', ddata[0x74:0x74 + 2])[0] + self.current_gear = struct.unpack('B', ddata[0x90:0x90 + 1])[0] & 0b00001111 + self.suggested_gear = struct.unpack('B', ddata[0x90:0x90 + 1])[0] >> 4 + self.fuel_capacity = struct.unpack('f', ddata[0x48:0x48 + 4])[0] + self.current_fuel = struct.unpack('f', ddata[0x44:0x44 + 4])[0] # fuel + self.boost = struct.unpack('f', ddata[0x50:0x50 + 4])[0] - 1 + + self.tyre_diameter_FL = struct.unpack('f', ddata[0xB4:0xB4 + 4])[0] + self.tyre_diameter_FR = struct.unpack('f', ddata[0xB8:0xB8 + 4])[0] + self.tyre_diameter_RL = struct.unpack('f', ddata[0xBC:0xBC + 4])[0] + self.tyre_diameter_RR = struct.unpack('f', ddata[0xC0:0xC0 + 4])[0] + + self.type_speed_FL = abs(3.6 * self.tyre_diameter_FL * struct.unpack('f', ddata[0xA4:0xA4 + 4])[0]) + self.type_speed_FR = abs(3.6 * self.tyre_diameter_FR * struct.unpack('f', ddata[0xA8:0xA8 + 4])[0]) + self.type_speed_RL = abs(3.6 * self.tyre_diameter_RL * struct.unpack('f', ddata[0xAC:0xAC + 4])[0]) + self.tyre_speed_RR = abs(3.6 * self.tyre_diameter_RR * struct.unpack('f', ddata[0xB0:0xB0 + 4])[0]) + + self.car_speed = 3.6 * struct.unpack('f', ddata[0x4C:0x4C + 4])[0] + + self.tyre_slip_ratio_FL = 0 + self.tyre_slip_ratio_FR = 0 + self.tyre_slip_ratio_RL = 0 + self.tyre_slip_ratio_RR = 0 + + if self.car_speed > 0: + self.tyre_slip_ratio_FL = '{:6.2f}'.format(self.type_speed_FL / self.car_speed) + self.tyre_slip_ratio_FR = '{:6.2f}'.format(self.type_speed_FR / self.car_speed) + self.tyre_slip_ratio_RL = '{:6.2f}'.format(self.type_speed_RL / self.car_speed) + self.tyre_slip_ratio_RR = '{:6.2f}'.format(self.tyre_speed_RR / self.car_speed) + + self.time_on_track = timedelta( + seconds=round(struct.unpack('i', ddata[0x80:0x80 + 4])[0] / 1000)) # time of day on track + + self.total_laps = struct.unpack('h', ddata[0x76:0x76 + 2])[0] # total laps + + self.current_position = struct.unpack('h', ddata[0x84:0x84 + 2])[0] # current position + self.total_positions = struct.unpack('h', ddata[0x86:0x86 + 2])[0] # total positions + + self.car_id = struct.unpack('i', ddata[0x124:0x124 + 4])[0] # car id + + self.throttle = struct.unpack('B', ddata[0x91:0x91 + 1])[0] / 2.55 # throttle + self.rpm = struct.unpack('f', ddata[0x3C:0x3C + 4])[0] # rpm + self.rpm_rev_warning = struct.unpack('H', ddata[0x88:0x88 + 2])[0] # rpm rev warning + + self.brake = struct.unpack('B', ddata[0x92:0x92 + 1])[0] / 2.55 # brake + + self.boost = struct.unpack('f', ddata[0x50:0x50 + 4])[0] - 1 # boost + + self.rpm_rev_limiter = struct.unpack('H', ddata[0x8A:0x8A + 2])[0] # rpm rev limiter + + self.estimated_top_speed = struct.unpack('h', ddata[0x8C:0x8C + 2])[0] # estimated top speed + + self.clutch = struct.unpack('f', ddata[0xF4:0xF4 + 4])[0] # clutch + self.clutch_engaged = struct.unpack('f', ddata[0xF8:0xF8 + 4])[0] # clutch engaged + self.rpm_after_clutch = struct.unpack('f', ddata[0xFC:0xFC + 4])[0] # rpm after clutch + + self.oil_temp = struct.unpack('f', ddata[0x5C:0x5C + 4])[0] # oil temp + self.water_temp = struct.unpack('f', ddata[0x58:0x58 + 4])[0] # water temp + + self.oil_pressure = struct.unpack('f', ddata[0x54:0x54 + 4])[0] # oil pressure + self.ride_height = 1000 * struct.unpack('f', ddata[0x38:0x38 + 4])[0] # ride height + + self.tyre_temp_FL = struct.unpack('f', ddata[0x60:0x60 + 4])[0] # tyre temp FL + self.tyre_temp_FR = struct.unpack('f', ddata[0x64:0x64 + 4])[0] # tyre temp FR + + self.suspension_fl = struct.unpack('f', ddata[0xC4:0xC4 + 4])[0] # suspension FL + self.suspension_fr = struct.unpack('f', ddata[0xC8:0xC8 + 4])[0] # suspension FR + + self.tyre_temp_rl = struct.unpack('f', ddata[0x68:0x68 + 4])[0] # tyre temp RL + self.tyre_temp_rr = struct.unpack('f', ddata[0x6C:0x6C + 4])[0] # tyre temp RR + + self.suspension_rl = struct.unpack('f', ddata[0xCC:0xCC + 4])[0] # suspension RL + self.suspension_rr = struct.unpack('f', ddata[0xD0:0xD0 + 4])[0] # suspension RR + + self.gear_1 = struct.unpack('f', ddata[0x104:0x104 + 4])[0] # 1st gear + self.gear_2 = struct.unpack('f', ddata[0x108:0x108 + 4])[0] # 2nd gear + self.gear_3 = struct.unpack('f', ddata[0x10C:0x10C + 4])[0] # 3rd gear + self.gear_4 = struct.unpack('f', ddata[0x110:0x110 + 4])[0] # 4th gear + self.gear_5 = struct.unpack('f', ddata[0x114:0x114 + 4])[0] # 5th gear + self.gear_6 = struct.unpack('f', ddata[0x118:0x118 + 4])[0] # 6th gear + self.gear_7 = struct.unpack('f', ddata[0x11C:0x11C + 4])[0] # 7th gear + self.gear_8 = struct.unpack('f', ddata[0x120:0x120 + 4])[0] # 8th gear + + # self.struct.unpack('f', ddata[0x100:0x100+4])[0] # ??? gear + + self.position_x = struct.unpack('f', ddata[0x04:0x04 + 4])[0] # pos X + self.position_y = struct.unpack('f', ddata[0x08:0x08 + 4])[0] # pos Y + self.position_z = struct.unpack('f', ddata[0x0C:0x0C + 4])[0] # pos Z + + self.velocity_x = struct.unpack('f', ddata[0x10:0x10 + 4])[0] # velocity X + self.velocity_y = struct.unpack('f', ddata[0x14:0x14 + 4])[0] # velocity Y + self.velocity_z = struct.unpack('f', ddata[0x18:0x18 + 4])[0] # velocity Z + + self.rotation_pitch = struct.unpack('f', ddata[0x1C:0x1C + 4])[0] # rot Pitch + self.rotation_yaw = struct.unpack('f', ddata[0x20:0x20 + 4])[0] # rot Yaw + self.rotation_roll = struct.unpack('f', ddata[0x24:0x24 + 4])[0] # rot Roll + + self.angular_velocity_x = struct.unpack('f', ddata[0x2C:0x2C + 4])[0] # angular velocity X + self.angular_velocity_y = struct.unpack('f', ddata[0x30:0x30 + 4])[0] # angular velocity Y + self.angular_velocity_z = struct.unpack('f', ddata[0x34:0x34 + 4])[0] # angular velocity Z + + self.is_paused = bin(struct.unpack('B', ddata[0x8E:0x8E + 1])[0])[-2] == '1' + self.in_race = bin(struct.unpack('B', ddata[0x8E:0x8E + 1])[0])[-1] == '1' + + # struct.unpack('f', ddata[0x28:0x28+4])[0] # rot ??? + + # bin(struct.unpack('B', ddata[0x8E:0x8E+1])[0])[2:] # various flags (see https://github.com/Nenkai/PDTools/blob/master/PDTools.SimulatorInterface/SimulatorPacketG7S0.cs) + # bin(struct.unpack('B', ddata[0x8F:0x8F+1])[0])[2:] # various flags (see https://github.com/Nenkai/PDTools/blob/master/PDTools.SimulatorInterface/SimulatorPacketG7S0.cs) + # bin(struct.unpack('B', ddata[0x93:0x93+1])[0])[2:] # 0x93 = ??? + + # struct.unpack('f', ddata[0x94:0x94+4])[0] # 0x94 = ??? + # struct.unpack('f', ddata[0x98:0x98+4])[0] # 0x98 = ??? + # struct.unpack('f', ddata[0x9C:0x9C+4])[0] # 0x9C = ??? + # struct.unpack('f', ddata[0xA0:0xA0+4])[0] # 0xA0 = ??? + + # struct.unpack('f', ddata[0xD4:0xD4+4])[0] # 0xD4 = ??? + # struct.unpack('f', ddata[0xD8:0xD8+4])[0] # 0xD8 = ??? + # struct.unpack('f', ddata[0xDC:0xDC+4])[0] # 0xDC = ??? + # struct.unpack('f', ddata[0xE0:0xE0+4])[0] # 0xE0 = ??? + + # struct.unpack('f', ddata[0xE4:0xE4+4])[0] # 0xE4 = ??? + # struct.unpack('f', ddata[0xE8:0xE8+4])[0] # 0xE8 = ??? + # struct.unpack('f', ddata[0xEC:0xEC+4])[0] # 0xEC = ??? + # struct.unpack('f', ddata[0xF0:0xF0+4])[0] # 0xF0 = ??? + + def to_json(self): + return json.dumps(self, indent=4, sort_keys=True, default=str) \ No newline at end of file diff --git a/gt7dashboard/gt7diagrams.py b/gt7dashboard/gt7diagrams.py index 68cb161..aaa452b 100644 --- a/gt7dashboard/gt7diagrams.py +++ b/gt7dashboard/gt7diagrams.py @@ -2,7 +2,7 @@ import bokeh from bokeh.layouts import layout -from bokeh.models import ColumnDataSource, Label, Scatter, Column, Line, TableColumn, DataTable, Range1d +from bokeh.models import ColumnDataSource, Label, Scatter, Column, Line, TableColumn, DataTable, Range1d, WheelZoomTool, PanTool from bokeh.plotting import figure from gt7dashboard import gt7helper @@ -39,6 +39,7 @@ def get_throttle_braking_race_line_diagram(): data={"raceline_z_throttle": [], "raceline_x_throttle": []} ), ) + breaking_line = s_race_line.line( x="raceline_x_braking", y="raceline_z_braking", @@ -185,6 +186,8 @@ def __init__(self, width=400): # This is the number of default laps, # last lap, best lap and median lap self.number_of_default_laps = 3 + self.wheel_zoom_x = WheelZoomTool(dimensions='width') + self.x_pan = PanTool(dimensions='width') tooltips = [ @@ -220,8 +223,11 @@ def __init__(self, width=400): width=width, height=250, tooltips=tooltips, - active_drag="box_zoom", + active_drag="auto" ) + self.f_speed.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_speed.toolbar.active_scroll = self.wheel_zoom_x + self.f_speed.toolbar.active_drag = self.x_pan self.f_speed_variance = figure( y_axis_label="Spd.Dev.", @@ -230,8 +236,11 @@ def __init__(self, width=400): width=width, height=int(self.f_speed.height / 4), tooltips=self.tooltips_speed_variance, - active_drag="box_zoom", + active_drag="auto", ) + self.f_speed_variance.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_speed_variance.toolbar.active_scroll = self.wheel_zoom_x + self.f_speed_variance.toolbar.active_drag = self.x_pan self.f_time_diff = figure( title="Time Diff - Last, Reference", @@ -240,8 +249,11 @@ def __init__(self, width=400): width=width, height=int(self.f_speed.height / 2), tooltips=tooltips_timedelta, - active_drag="box_zoom", + active_drag="auto" ) + self.f_time_diff.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_time_diff.toolbar.active_scroll = self.wheel_zoom_x + self.f_time_diff.toolbar.active_drag = self.x_pan self.f_throttle = figure( x_range=self.f_speed.x_range, @@ -249,16 +261,23 @@ def __init__(self, width=400): width=width, height=int(self.f_speed.height / 2), tooltips=tooltips, - active_drag="box_zoom", + active_drag="auto" ) + self.f_throttle.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_throttle.toolbar.active_scroll = self.wheel_zoom_x + self.f_throttle.toolbar.active_drag = self.x_pan + self.f_braking = figure( x_range=self.f_speed.x_range, y_axis_label="Braking", width=width, height=int(self.f_speed.height / 2), tooltips=tooltips, - active_drag="box_zoom", + active_drag="auto" ) + self.f_braking.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_braking.toolbar.active_scroll = self.wheel_zoom_x + self.f_braking.toolbar.active_drag = self.x_pan self.f_coasting = figure( x_range=self.f_speed.x_range, @@ -266,8 +285,11 @@ def __init__(self, width=400): width=width, height=int(self.f_speed.height / 2), tooltips=tooltips, - active_drag="box_zoom", + active_drag="auto", ) + self.f_coasting.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_coasting.toolbar.active_scroll = self.wheel_zoom_x + self.f_coasting.toolbar.active_drag = self.x_pan self.f_tires = figure( x_range=self.f_speed.x_range, @@ -275,8 +297,11 @@ def __init__(self, width=400): width=width, height=int(self.f_speed.height / 2), tooltips=tooltips, - active_drag="box_zoom", + active_drag="auto", ) + self.f_tires.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_tires.toolbar.active_scroll = self.wheel_zoom_x + self.f_tires.toolbar.active_drag = self.x_pan self.f_rpm = figure( x_range=self.f_speed.x_range, @@ -284,8 +309,11 @@ def __init__(self, width=400): width=width, height=int(self.f_speed.height / 2), tooltips=tooltips, - active_drag="box_zoom", + active_drag="auto", ) + self.f_rpm.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_rpm.toolbar.active_scroll = self.wheel_zoom_x + self.f_rpm.toolbar.active_drag = self.x_pan self.f_gear = figure( x_range=self.f_speed.x_range, @@ -293,8 +321,11 @@ def __init__(self, width=400): width=width, height=int(self.f_speed.height / 2), tooltips=tooltips, - active_drag="box_zoom", + active_drag="auto", ) + self.f_gear.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_gear.toolbar.active_scroll = self.wheel_zoom_x + self.f_gear.toolbar.active_drag = self.x_pan self.f_boost = figure( x_range=self.f_speed.x_range, @@ -302,8 +333,11 @@ def __init__(self, width=400): width=width, height=int(self.f_speed.height / 2), tooltips=tooltips, - active_drag="box_zoom", + active_drag="auto", ) + self.f_boost.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_boost.toolbar.active_scroll = self.wheel_zoom_x + self.f_boost.toolbar.active_drag = self.x_pan self.f_yaw_rate = figure( x_range=self.f_speed.x_range, @@ -311,8 +345,11 @@ def __init__(self, width=400): width=width, height=int(self.f_speed.height / 2), tooltips=tooltips, - active_drag="box_zoom", + active_drag="auto", ) + self.f_yaw_rate.add_tools(self.wheel_zoom_x,self.x_pan) + self.f_yaw_rate.toolbar.active_scroll = self.wheel_zoom_x + self.f_yaw_rate.toolbar.active_drag = self.x_pan self.f_speed.toolbar.autohide = True diff --git a/gt7dashboard/gt7help.py b/gt7dashboard/gt7help.py index 0efa0ae..248ea0d 100644 --- a/gt7dashboard/gt7help.py +++ b/gt7dashboard/gt7help.py @@ -74,5 +74,5 @@ def get_help_div(help_text_resource): def get_help_text_resource(help_text_resource): return f""" -