Package: granturismo
GranTurismoThis package wraps the Gran Turismo 7's unofficial telemetry API. By providing this module with you're PlayStation's IP address (as found in the menu) you will be able to retrieve packets containing telemetry data.UsageThe main function is the Listener, which is a closing object. You can use awith Listener(ip_address) as ...clause to open and close the listener.
The Listener will spin up a background thread to maintain a heartbeat connection with the PlayStation, so it's important to always close the object.Quickstart exampleGrab a single packet from GranTurismo and print it.You can run this by callingpython3 examples/quickstart.py <your PS's IP address>fromgranturismoimportFeedimportsysif__name__=='__main__':ip_address=sys.argv[1]# IP address to the PlayStation# Create a new Listener session and print the first packet that Gran Turismo sends.withFeed(ip_address)asfeed:print(feed.get())Streaming suspension data to the consoleStream all incoming data from Gran Turismo and print it to the terminalYou can run this by callingpython3 examples/stream_suspension.py <your PS's IP address>fromgranturismoimportFeedfromgranturismo.modelimportWheelsimportdatetimeasdtimporttime,sysimportcursesstdscr=curses.initscr()# This function is used to rewrite multiple lines on the terminaldefreport_suspension(wheels:Wheels)->None:curr_time=dt.datetime.fromtimestamp(time.time()).isoformat()stdscr.addstr(0,0,f'[{curr_time}] Suspension Height')stdscr.addstr(1,0,f'\t{wheels.front_left.suspension_height:.3f}{wheels.front_right.suspension_height:.3f}')stdscr.addstr(2,0,f'\t{wheels.rear_left.suspension_height:.3f}{wheels.rear_right.suspension_height:.3f}')stdscr.refresh()if__name__=='__main__':ip_address=sys.argv[1]# To use the Listener session without a `with` clause, you'll need to call the `.start()` function.feed=Feed(ip_address)feed.start()try:whileTrue:# get the latest packet from PlayStationpacket=feed.get()# If the game isn't paused or in a loading state, we'll update the terminal with the latest suspension info.ifnotpacket.flags.loading_or_processingandnotpacket.flags.paused:report_suspension(packet.wheels)finally:# If you don't use a `with` clause, then you'll need to close the session afterwords. Session will also successfully close with CTRL+Ccurses.echo()curses.nocbreak()curses.endwin()feed.close()Streaming position data directly to a plotStream the car's position directly to a plot window. Here we'll plot the x and z axis, which track the horizontal plane. We also add a "heatmap" color to the plot to show the car's speed compared to it's max potential speed.You can run this by callingpython3 examples/stream_position.py <your PS's IP address>importsysfromgranturismoimportFeedimportmatplotlib.pyplotaspltif__name__=='__main__':ip_address=sys.argv[1]# setup the plot stylingplt.ion()# allows us to continue to update the plotfig,ax=plt.subplots(figsize=(8,8))ax.axis('off')# hides the black border around the axis.plt.xticks([])plt.yticks([])# this will be the previous x and z points. We don't want to re-plot all our points because# that'll be too slow and the graph cant keep up with our stream. We're only gonna plot the newest segment.px,pz=None,Nonecount=0withFeed(ip_address)asfeed:whileTrue:count+=1# only update graph every 10th of a second just cuz it doesn't matter for us, and it's easier on the computer# but we still need to grab the packet even if we're not using itpacket=feed.get()# note, we're negating z so the map will appear int he same orientation as it does in the game's minimapx,z=packet.position.x,-packet.position.zifpxisNone:px,pz=x,zcontinue# here we're getting the ratio of how fast the car's going compared to it's max speed.# we're multiplying by 3 to boost the colorization range.speed=min(1,packet.car_speed/packet.car_max_speed)*3# Now use the "speed" ratio to select the color from the Matplotlib palletcolor=plt.cm.plasma(speed)# plot the current stepplt.plot([px,x],[pz,z],color=color)# set the aspect ratios to be equal for x/z axis, this way the map doesn't look skewedplt.gca().set_aspect('equal',adjustable='box')# pause for a freakishly shot amount of time. We need a pause so that it'll trigger a graph updateplt.pause(0.00000000000000000001)# set the previous (x, z) to the current (x, z)px,pz=x,zDataBecause this is an unofficial API, the range expected min/max of each value is still unknown. As more effort is put into understanding this API, better information will be available. For now, here is what we know.int:packet_idfloat:received_timethe timestamp when the packet was received, before any decrypting or processing.int:car_idcars with more than 8 gears will overwrite this value with a gear ratioOptional(int):lap_countNone if not in raceOptional(int):laps_in_raceNone if not in raceOptional(int):best_lap_timeIn milliseconds. None if not in race, or no lap complete.Optional(int):last_lap_timeIn milliseconds. None if no lap completedVector:positionfloat:xfloat:yfloat:zVector:velocityin meters per secondfloat:xfloat:yfloat:zVector:angular_velocityradians per secondfloat:xfloat:yfloat:zRotation:rotationSeems to be the real part of a unit quaternion that gives the rotation of the car relative to the track coordinate systemfloat:pitchfloat:yawfloat:rollVector:road_planefloat:xfloat:yfloat:zfloat:road_distance: Should match thebody_heightwhen car is on the groundWheels:wheelsWheel:front_leftfloat:suspension_height: between 0-1. Lower number equates to uncompressed, higher number to a more compressed suspensionfloat:radius: Radius of the tire in metersfloat:rps: Rotations per secondfloat:ground_speed: The speed the tire is traveling on the ground in meters per second.float:temperature: The surface temperature of the tire in celsiusWheel:front_rightfloat:suspension_height: between 0-1. Lower number equates to uncompressed, higher number to a more compressed suspensionfloat:radius: Radius of the tire in metersfloat:rps: Rotations per secondfloat:ground_speed: The speed the tire is traveling on the ground in meters per second.float:temperature: The surface temperature of the tire in celsiusWheel:rear_leftfloat:suspension_height: between 0-1. Lower number equates to uncompressed, higher number to a more compressed suspensionfloat:radius: Radius of the tire in metersfloat:rps: Rotations per secondfloat:ground_speed: The speed the tire is traveling on the ground in meters per second.float:temperature: The surface temperature of the tire in celsiusWheel:rear_rightfloat:suspension_height: between 0-1. Lower number equates to uncompressed, higher number to a more compressed suspensionfloat:radius: Radius of the tire in metersfloat:rps: Rotations per secondfloat:ground_speed: The speed the tire is traveling on the ground in meters per second.float:temperature: The surface temperature of the tire in celsiusFlags:flagsbool:in_racebool:pausedbool:loading_or_processingbool:in_gear0 when shifting or out of gear, standingbool:has_turbobool:rev_limiter_alert_activebool:hand_brake_activebool:lights_activebool:lights_high_beams_activebool:lights_low_beams_activebool:asm_activebool:tcs_activebool:unused1always Falsebool:unused2always Falsebool:unused3always Falsebool:unused4always Falsefloat:orientationfloat:body_heightin metersfloat:engine_rpm0-?float:gas_level0-100float:gas_capacity100 for gas cars, 5 for karts, 0 for electricfloat:car_speedin meters per secondfloat:turbo_boostthis value - 1 gives the Turbo Boost displayfloat:oil_pressurein bars?float:oil_temperaturein celsius. Seems to always be 85.0float:water_temperaturein celsius. Seems to always be 110.0int:time_of_daymillisecond timestamp, time of day indicates race start time of day, affected by Variable Time Speed Ratio, useless for timing when time speed ratio is not 1Optional(int):start_positiononly available before race starts, otherwise NoneOptional(int):cars_in_raceonly available before race starts, otherwise NoneBounds:rpm_alertfloat:min0-?float:max0-?int:car_max_speedin meters per secondfloat:transmission_max_speedcorresponds to the Top Speed setting of a customizable gear box in the car settings, given as gear ratioint:throttlebetween 0-255int:brakebetween 0-255float:clutchbetween 0-1. This seems to correlate with the clutch peddlefloat:clutch_engagementbetween 0-1float:clutch_gearbox_rpmOptional(int):current_gear0-4: current gear, 0 is reverse, None is neutralOptional(int):suggested_gear. None if there's no suggested gearList(float):gear_ratios1st - Nth gear.int:unused_0x93always 0int:unused_0xD4always 0ReferencesNenkaiis the original discoverer of this API and how to decrypt and communicate with it, as well as a significant amount of research into each value.tarnheldfor their work in identifying data values/ranges.