diff --git a/ams2.py b/ams2.py index 64e7716..7bd91e6 100644 --- a/ams2.py +++ b/ams2.py @@ -19,6 +19,7 @@ } sg.change_look_and_feel('Default1') +sg.set_options(font="Arial 12") BUTTON_DISABLED = (sg.theme_background_color(), sg.theme_background_color()) BUTTON_ENABLED = (sg.theme_button_color_text(), sg.theme_background_color()) diff --git a/gt7-cli.py b/gt7-cli.py index 6ec2227..8a35d93 100644 --- a/gt7-cli.py +++ b/gt7-cli.py @@ -20,6 +20,7 @@ def main(): parser.add_argument("--session", type=str, default="", help="Session e.g. Practice, Qualify, Race") parser.add_argument("--vehicle", type=str, default="", help="Override name of vehicle") parser.add_argument("--venue", type=str, default="", help="Venue/Track name, MoTeC will not generate a track map without this") + parser.add_argument("--replay", action="store_true", help="log replay telemetry") parser.add_argument("--freq", type=int, default=60, help="frequency to collect samples, currently ignored") parser.add_argument("--saveraw", help="save raw samples to an sqlite3 db for later analysis", action="store_true") parser.add_argument("--loadraw", help="load raw samples from an sqlite3 db", action="store_true") @@ -41,6 +42,7 @@ def main(): rawfile=rawfile, sampler=sampler, filetemplate=filetemplate, + replay=args.replay, driver=args.driver, session=args.session, vehicle=args.vehicle, diff --git a/gt7.py b/gt7.py index 2777185..6bea05b 100644 --- a/gt7.py +++ b/gt7.py @@ -10,23 +10,30 @@ # try and load the sate STATE_FILE = "gt7.cfg" +state = { + "IP": "192.168.1.100", + "PORT": 33740, + "REPLAY": False, + "DRIVER": "", + "SESSION": "" +} + try: with open(STATE_FILE) as f: - state = json.load(f) + state.update(json.load(f)) except Exception as e: - state = { - "IP": "192.168.1.100", - "DRIVER": "", - "SESSION": "" - } + pass sg.change_look_and_feel('Default1') +sg.set_options(font="Arial 12") BUTTON_DISABLED = (sg.theme_background_color(), sg.theme_background_color()) BUTTON_ENABLED = (sg.theme_button_color_text(), sg.theme_background_color()) labels = [ [sg.Text("PS IP Address")], + [sg.Text("Local UDP Port")], + [sg.Text("Capture Replays")], [sg.Text("Driver")], [sg.Text("Session")], [sg.Text("Log File")], @@ -38,6 +45,8 @@ values = [ [sg.Input(state["IP"], key="IP", size=(15,1), enable_events=True)], + [sg.Input(state["PORT"], key="PORT", size=(15,1), enable_events=True), sg.Text("Only change this if using a UDP relay", font="arial 12 italic")], + [sg.Checkbox("", state["REPLAY"], key="REPLAY", enable_events=True)], [sg.Input(state["DRIVER"], key="DRIVER", size=(15,1), enable_events=True )], [sg.Input(state["SESSION"], key="SESSION", size=(15,1), enable_events=True )], [sg.Text("Not Started",key="LOGFILE" )], @@ -82,6 +91,9 @@ if event == "IP" and len(values['IP']) and values['IP'][-1] not in ('.1234567890'): window["IP"].update(values['IP'][:-1]) + if event == "PORT" and len(values['PORT']) and values['PORT'][-1] not in ('1234567890'): + window["PORT"].update(values['PORT'][:-1]) + if event in state: state[event] = values[event] @@ -95,8 +107,9 @@ logger = GT7Logger( rawfile=rawfile, - sampler=GT7Sampler(addr=values['IP'], freq=60), + sampler=GT7Sampler(addr=values["IP"], port=values["PORT"], freq=60), filetemplate=filetemplate, + replay=values["REPLAY"], driver=values["DRIVER"], session=values["SESSION"] ) diff --git a/stm/ams2/logger.py b/stm/ams2/logger.py index 6faf7cc..d9ab3bb 100644 --- a/stm/ams2/logger.py +++ b/stm/ams2/logger.py @@ -14,7 +14,8 @@ class AMS2Logger(BaseLogger): 'throttle', 'brake', 'steer', 'speed', 'lat', 'long', 'glat', 'gvert', 'glong', # g forces - 'suspfl', 'suspfr', 'susprl', 'susprr', # suspension + 'suspfl', 'suspfr', 'susprl', 'susprr', # suspension travel + 'suspvelfl', 'suspvelfr', 'suspvelrl', 'suspvelrr', # suspension velocity 'tyretempfl', 'tyretempfr', 'tyretemprl', 'tyretemprr', # combined tyre temp 'braketempfl', 'braketempfr', 'braketemprl', 'braketemprr', # brake temp 'tyretempflo', 'tyretempfro', # outer temp @@ -22,6 +23,10 @@ class AMS2Logger(BaseLogger): 'tyretempfli', 'tyretempfri', # inner temp #'wheelslipfl', 'wheelslipfr', 'wheelsliprl', 'wheelsliprr', # wheel slip 'rideheightfl', 'rideheightfr', 'rideheightrl', 'rideheightrr', + 'tyrepresfl', 'tyrepresfr', 'tyrepresrl', 'tyrepresrr', # mAirPressure + 'turbopres', + 'brakebias', + 'enginetorque', 'lap', 'laptime', 'racestate', # AMS2 race status ] @@ -102,7 +107,8 @@ def process_packet(self, timestamp, p, lastp): glat, gvert, -glong, - *p.mSuspensionTravel, + *[sp * 100 for sp in p.mSuspensionTravel], + *p.mSuspensionVelocity, *p.mTyreTemp, *p.mBrakeTempCelsius, p.mTyreTempLeft.fl, p.mTyreTempRight.fr, # outer @@ -110,6 +116,10 @@ def process_packet(self, timestamp, p, lastp): p.mTyreTempRight.fl, p.mTyreTempLeft.fr, # inner #*[s * 2.23693629 for s in p.mTyreSlipSpeed], # wheel slip m/s -> mph *p.mRideHeight, + *p.mAirPressure, + p.mTurboBoostPressure, + p.mBrakeBias, + p.mEngineTorque, p.driver.mCurrentLap, p.mCurrentTime, p.mRaceState.value, diff --git a/stm/ams2/shmem.py b/stm/ams2/shmem.py index ea8979b..343f724 100644 --- a/stm/ams2/shmem.py +++ b/stm/ams2/shmem.py @@ -155,10 +155,10 @@ class AMS2SharedMemory: "i" # mSequenceNumber "16x" # mWheelLocalPositionY "4f" # mSuspensionTravel - "16x" # mSuspensionVelocity + "4f" # mSuspensionVelocity "4f" # mAirPressure "4x" # mEngineSpeed - "4x" # mEngineTorque + "f" # mEngineTorque "2f" # mWings "4x" # mHandBrake "256x" # mCurrentSector1Times @@ -248,7 +248,9 @@ def __init__(self, buf): btfl, btfr, btrl, btrr, # mBrakeTempCelsius self.mSequenceNumber, stfl, stfr, strl, sttrr, # mSuspensionTravel + svfl, svfr, svrl, svrr, # mSuspensionVelocity apfl, apfr, aprl, aprr, # mAirPressure, + self.mEngineTorque, wf, wr, # mWings mTranslatedTrackLocation, mTranslatedTrackVariation, @@ -279,6 +281,7 @@ def __init__(self, buf): self.mTyreTemp = Wheels(ttfl, ttfr, ttrl, ttrr) self.mBrakeTempCelsius = Wheels(btfl, btfr, btrl, btrr) self.mSuspensionTravel = Wheels(stfl, stfr, strl, sttrr) + self.mSuspensionVelocity = Wheels(svfl, svfr, svrl, svrr) self.mAirPressure = Wheels(apfl, apfr, aprl, aprr) self.mWings = Wings(wf, wr) self.mTyreTempLeft = Wheels(ttlfl, ttlfr, ttlrl, ttlrr) diff --git a/stm/channels.py b/stm/channels.py index 80bf2e9..8e4f37c 100644 --- a/stm/channels.py +++ b/stm/channels.py @@ -36,14 +36,12 @@ "units": "mph" }, "lat": { - "datatype": 5, "decplaces": 7, "name": "GPS Latitude", "shortname": "GPSLat", "units": "deg" }, "long": { - "datatype": 5, "decplaces": 7, "name": "GPS Longitude", "shortname": "GPSLong", @@ -78,154 +76,180 @@ "units": "rpm" }, "velx": { - "datatype": 5, "decplaces": 3, "name": "Velocity X", "shortname": "VELX", "units": "m" }, "vely": { - "datatype": 5, "decplaces": 3, "name": "Velocity Y", "shortname": "VELY", "units": "m" }, "velz": { - "datatype": 5, "decplaces": 3, "name": "Velocity Z", "shortname": "VELZ", "units": "m" }, "glat": { - "datatype": 5, "decplaces": 2, "name": "G Force Lat", "shortname": "G Lat", "units": "G" }, "gvert": { - "datatype": 5, "decplaces": 2, "name": "G Force Vert", "shortname": "G Vert", "units": "G" }, "glong": { - "datatype": 5, "decplaces": 2, "name": "G Force Long", "shortname": "G Long", "units": "G" }, "wspdfl": { - "datatype": 5, "decplaces": 1, "name": "Wheel Speed FL", "shortname": "WSpd FL", "units": "mph" }, "wspdfr": { - "datatype": 5, "decplaces": 1, "name": "Wheel Speed FR", "shortname": "WSpd FR", "units": "mph" }, "wspdrl": { - "datatype": 5, "decplaces": 1, "name": "Wheel Speed RL", "shortname": "WSpd RL", "units": "mph" }, "wspdrr": { - "datatype": 5, "decplaces": 1, "name": "Wheel Speed RR", "shortname": "WSpd RR", "units": "mph" }, "suspfl": { - "datatype": 5, "decplaces": 3, "name": "Susp Pos FL", "shortname": "SuspFL", - "units": "m" + "units": "cm" }, "suspfr": { - "datatype": 5, "decplaces": 3, "name": "Susp Pos FR", "shortname": "SuspFR", - "units": "m" + "units": "cm" }, "susprl": { - "datatype": 5, "decplaces": 3, "name": "Susp Pos RL", - "shortname": "SuspHRL", - "units": "m" + "shortname": "SuspRL", + "units": "cm" }, "susprr": { - "datatype": 5, "decplaces": 3, "name": "Susp Pos RR", "shortname": "SuspRR", - "units": "m" + "units": "cm" + }, + "suspvelfl": { + "decplaces": 3, + "name": "Susp Vel FL", + "shortname": "SuspVFL", + "units": "m/s" + }, + "suspvelfr": { + "decplaces": 3, + "name": "Susp Vel FR", + "shortname": "SuspVFR", + "units": "m/s" + }, + "suspvelrl": { + "decplaces": 3, + "name": "Susp Vel RL", + "shortname": "SuspVRL", + "units": "m/s" + }, + "suspvelrr": { + "decplaces": 3, + "name": "Susp Vel RR", + "shortname": "SuspVRR", + "units": "m/s" }, "tyretempfl": { - "datatype": 5, "decplaces": 2, "name": "Tyre Temp FL", "shortname": "TTempFL", "units": "C" }, "tyretempfr": { - "datatype": 5, "decplaces": 2, "name": "Tyre Temp FR", "shortname": "TTempFR", "units": "C" }, "tyretemprl": { - "datatype": 5, "decplaces": 2, "name": "Tyre Temp RL", "shortname": "TTempRL", "units": "C" }, "tyretemprr": { - "datatype": 5, "decplaces": 2, "name": "Tyre Temp RR", "shortname": "TTempRR", "units": "C" }, + "tyrepresfl": { + "decplaces": 2, + "name": "Tyre Pressure FL", + "shortname": "TPresFL", + "units": "kPa" + }, + "tyrepresfr": { + "decplaces": 2, + "name": "Tyre Pressure FR", + "shortname": "TPresFR", + "units": "kPa" + }, + "tyrepresrl": { + "decplaces": 2, + "name": "Tyre Pressure RL", + "shortname": "TPresRL", + "units": "kPa" + }, + "tyrepresrr": { + "decplaces": 2, + "name": "Tyre Pressure RR", + "shortname": "TPresRR", + "units": "kPa" + }, "braketempfl": { - "datatype": 5, "decplaces": 2, "name": "Brake Temp FL", "shortname": "BTempFL", "units": "C" }, "braketempfr": { - "datatype": 5, "decplaces": 2, "name": "Brake Temp FR", "shortname": "BTempFR", "units": "C" }, "braketemprl": { - "datatype": 5, "decplaces": 2, "name": "Brake Temp RL", "shortname": "BTempRL", "units": "C" }, "braketemprr": { - "datatype": 5, "decplaces": 2, "name": "Brake Temp RR", "shortname": "BTempRR", @@ -250,70 +274,60 @@ "shortname": "RState", }, "tyretempflo": { - "datatype": 5, "decplaces": 2, "name": "Tyre Temp FL Outer", "shortname": "TTempFLO", "units": "C" }, "tyretempfro": { - "datatype": 5, "decplaces": 2, "name": "Tyre Temp FR Outer", "shortname": "TTempFRO", "units": "C" }, "tyretempflc": { - "datatype": 5, "decplaces": 2, "name": "Tyre Temp FL Centre", "shortname": "TTempFLC", "units": "C" }, "tyretempfrc": { - "datatype": 5, "decplaces": 2, "name": "Tyre Temp FR Centre", "shortname": "TTempFRC", "units": "C" }, "tyretempfli": { - "datatype": 5, "decplaces": 2, "name": "Tyre Temp FL Inner", "shortname": "TTempFLI", "units": "C" }, "tyretempfri": { - "datatype": 5, "decplaces": 2, "name": "Tyre Temp FR Inner", "shortname": "TTempFRI", "units": "C" }, "wheelslipfl": { - "datatype": 5, "decplaces": 1, "name": "Wheel Slip FL", "shortname": "WSLPFL", "units": "mph" }, "wheelslipfr": { - "datatype": 5, "decplaces": 1, "name": "Wheel Slip FR", "shortname": "WSLPFR", "units": "mph" }, "wheelsliprl": { - "datatype": 5, "decplaces": 1, "name": "Wheel Slip RL", "shortname": "WSLPRL", "units": "mph" }, "wheelsliprr": { - "datatype": 5, "decplaces": 1, "name": "Wheel Slip RR", "shortname": "WSLPRR", @@ -353,6 +367,28 @@ "shortname": "RHeightRR", "units": "cm" }, + "rideheight": { + "decplaces": 2, + "name": "Ride Height", + "shortname": "RHeight", + "units": "cm" + }, + "turbopres": { + "decplaces": 2, + "name": "Turbo Boost Pressure", + "shortname": "TurboP" + }, + "brakebias": { + "decplaces": 2, + "name": "Brake Bias", + "shortname": "BrakeBias" + }, + "enginetorque": { + "decplaces": 2, + "name": "Engine Torque", + "shortname": "ETorque", + "units": "Nm" + }, "debug1": { "decplaces": 2, "name": "Debug 1", diff --git a/stm/gt7/logger.py b/stm/gt7/logger.py index 4007263..e879dfa 100644 --- a/stm/gt7/logger.py +++ b/stm/gt7/logger.py @@ -16,12 +16,14 @@ class GT7Logger(BaseLogger): 'suspfl', 'suspfr', 'susprl', 'susprr', 'wspdfl', 'wspdfr', 'wspdrl', 'wspdrr', 'tyretempfl', 'tyretempfr', 'tyretemprl', 'tyretemprr', + 'rideheight' ] def __init__(self, rawfile=None, sampler=None, filetemplate=None, + replay=False, name="", session="", vehicle="", @@ -47,12 +49,14 @@ def __init__(self, self.skip_samples = 0 self.track = None self.track_detector = None + self.replay = replay def process_sample(self, timestamp, sample): p = GT7DataPacket(sample) if not self.last_packet: self.last_packet = p + l.info(f"received first packet from GT7 with ID {p.tick}") # fill in any missing ticks missing = range(self.last_packet.tick + 1, p.tick) @@ -81,7 +85,7 @@ def process_packet(self, timestamp, packet): if currp.paused: return - if not currp.in_race: + if not currp.in_race and not self.replay: self.save_log() return @@ -102,6 +106,11 @@ def process_packet(self, timestamp, packet): event = copy(self.event) event.datetime = then.strftime("%Y-%m-%dT%H:%M:%S") event.vehicle = vehicle + + # mark the session as a replay + if not currp.in_race and not event.session: + event.session = "Replay" + self.current_event = event self.new_log(channels=self.channels, event=event) @@ -124,7 +133,7 @@ def process_packet(self, timestamp, packet): self.track_detector.guess(lastp.position.x, lastp.position.z, currp.position.x, currp.position.z) if self.track_detector.track_name: - self.current_event.venue = self.track_detector.track_name + self.current_event.venue = str(self.track_detector.track_name).replace(" - ", "-") self.update_event(event=self.current_event) else: @@ -152,8 +161,13 @@ def process_packet(self, timestamp, packet): gvert = deltav.y * freq / 9.8 # Y glong = deltav.z * freq / 9.8 # Z - # calculate wheel speed (needs to be inverted) - wheelspeed = [ r * s * -2.23693629 for r,s in zip(currp.wheelradius, currp.wheelspeed) ] + ms_to_mph = 2.23693629 + if currp.in_race: + # calculate wheel speed (needs to be inverted) + wheelspeed = [ r * s * -ms_to_mph for r,s in zip(currp.wheelradius, currp.wheelspeed) ] + else: + # wheelspeed is not inverted in replay + wheelspeed = [ r * s * ms_to_mph for r,s in zip(currp.wheelradius, currp.wheelspeed) ] self.add_samples([ beacon, @@ -162,7 +176,7 @@ def process_packet(self, timestamp, packet): currp.gear, currp.throttle * 100 / 255, currp.brake * 100 / 255, - currp.speed * 2.23693629, # m/s to mph + currp.speed * ms_to_mph, # m/s to mph lat, long, deltav.x, @@ -171,10 +185,9 @@ def process_packet(self, timestamp, packet): glat, gvert, -glong, - *currp.suspension, + *[p * 100 for p in currp.suspension], *wheelspeed, - *currp.tyretemp + *currp.tyretemp, + currp.ride_height * 100 ]) - - diff --git a/stm/gt7/packet.py b/stm/gt7/packet.py index 07b9fa8..518665a 100644 --- a/stm/gt7/packet.py +++ b/stm/gt7/packet.py @@ -35,7 +35,7 @@ class GT7DataPacket: "3f" # VELOCITY / 3f / 12x / 0x0010 "4f" # ROTATION / 4f / 12x / 0x001C "12x" # VELOCITY_ANGULAR / 3f / 12x / 0x002C - "4x" # RIDE_HEIGHT / f / 4x / 0x0038 + "f" # RIDE_HEIGHT / f / 4x / 0x0038 "f" # RPM / f / 4x / 0x003C "4x" # IV / 4B / 4x / 0x0040 "4x" # CURRENT_FUEL / f / 4x / 0x0044 @@ -84,6 +84,7 @@ def __init__(self, buf, encrypted=True): px, py, pz, vx, vy, vz, rw, rx, ry, rz, + self.ride_height, self.rpm, self.speed, ttfl, ttfr, ttrl, ttrr, diff --git a/stm/gt7/sampler.py b/stm/gt7/sampler.py index d2fe9f3..246d88d 100644 --- a/stm/gt7/sampler.py +++ b/stm/gt7/sampler.py @@ -12,12 +12,18 @@ class GT7Sampler(BaseSampler): def __init__(self, addr=None, port=DEFAULT_PORT, hb_port=DEFAULT_HEARTBEAT_PORT, freq=None): super().__init__(freq=freq) - self.hb_addr = (addr, hb_port) + if port != DEFAULT_PORT: + # do not send heartbeats if we are not running on the default ports + # as GT7 will ignore them anyway + self.hb_addr = None + l.info("Relay mode. Not sending heartbeat packets to GT7") + else: + self.hb_addr = (addr, hb_port) # Create a UDP socket for the inbound packets self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Bind to any address - self.socket.bind( ('0.0.0.0', port) ) + self.socket.bind( ('0.0.0.0', int(port)) ) self.socket.settimeout(1) def run(self): @@ -46,6 +52,9 @@ def run(self): def send_hb(self): + if not self.hb_addr: + return + send_data = b'A' try: self.socket.sendto(send_data, self.hb_addr) diff --git a/stm/version.py b/stm/version.py index 4a2bfa8..9e2406e 100644 --- a/stm/version.py +++ b/stm/version.py @@ -1 +1 @@ -__version__ = "1.2.0" \ No newline at end of file +__version__ = "1.3.0" \ No newline at end of file diff --git a/util/gt7-var-check.py b/util/gt7-var-check.py new file mode 100644 index 0000000..f8453c9 --- /dev/null +++ b/util/gt7-var-check.py @@ -0,0 +1,70 @@ +import argparse +import sqlite3 +from stm.gt7 import GT7DataPacket + +parser = argparse.ArgumentParser(description="Analyse GT7 raw samples") +parser.add_argument("db", type=str, help="db file to check") +parser.add_argument("name", default="speed", help="name of the variable") +args = parser.parse_args() + + +con = sqlite3.connect( args.db ) +cur = con.cursor() +res = cur.execute("select * from samples") + +vtype = None +vmin = None +vmax = None +vsum = 0 +samples = 0 +vstr = {} + +def getvar(p, name): + + # split the name + names = name.split(".") + v = p + for n in names: + v = getattr(v, n) + + return v + +for timestamp, data in res: + + if not data: + continue + + p = GT7DataPacket(data) + + try: + samples += 1 + v = getvar(p, args.name) + vtype = type(v) + if isinstance(v, int) or isinstance(v, float): + # get the min and max + if vmax is None or vmax < v: + vmax = v + + if vmin is None or vmin > v: + vmin = v + + vsum += v + + if isinstance(v, str): + if v not in vstr: + vstr[v] = 0 + + vstr[v] += 1 + + except: + pass + +print(f"type: {vtype}") + +if vsum: + print(f"vmin: {vmin}") + print(f"vmax: {vmax}") + print(f"vavg: {vsum/samples}") + + +print(f"vstr: {vstr}") \ No newline at end of file