コード例 #1
0
    def __init__(self, acd: ACD, resolution: str, wheel):
        self.__calc = TireTemp(
            acd.get_temp_curve(ac.getCarTyreCompound(0), wheel))

        # Initial size is 160x256
        super(Temps, self).__init__(176.0, 0.0, 160.0, 256.0, 16.0)
        self.resize(resolution)
コード例 #2
0
    def __init__(self, acd: ACD, resolution: str, wheel):
        self.__calc = TireTemp(
            acd.get_temp_curve(ac.getCarTyreCompound(0), wheel))

        # Initial size is 160x256
        super(Tire, self).__init__(176.0, 0.0, 160.0, 256.0)
        self._back.color = Colors.white

        if Tire.texture_id == 0:
            Tire.texture_id = ac.newTexture(
                "apps/python/LiveTelemetry/img/tire.png")

        self.resize(resolution)
コード例 #3
0
def loadTireData():
	global Options

	try:
		tyresFile = os.path.join(os.path.dirname(__file__), "tyres-data.json")
		with open(tyresFile, "r") as f:
			tyreData = json.load(f)

	except OSError:
		ac.log("CamberExtravaganza ERROR: loadTireData tyres-data.json not found")

	else:
		try:
			carName = ac.getCarName(0)
			tyreCompound = ac.getCarTyreCompound(0)
			Options["tireRadius"] = tyreData[carName][tyreCompound]["radius"]
			dcamber0 = tyreData[carName][tyreCompound]["dcamber0"]
			dcamber1 = tyreData[carName][tyreCompound]["dcamber1"]
			Options["targetCamber"] = math.degrees(dcamber0 / (2 * dcamber1))
			Options["dcamber0"] = dcamber0
			Options["dcamber1"] = dcamber1
			Options["LS_EXPY"] = tyreData[carName][tyreCompound]["ls_expy"]
			ac.console("Tyre data found for " + carName + " " + tyreCompound)

		except KeyError: # Doesn't exist in official, look for custom data
			try:
				tyresFile = os.path.join(os.path.dirname(__file__), "tyres-data-custom.json")
				with open(tyresFile, "r") as f:
					tyreData = json.load(f)
					Options["tireRadius"] = tyreData[carName][tyreCompound]["radius"]
					dcamber0 = tyreData[carName][tyreCompound]["dcamber0"]
					dcamber1 = tyreData[carName][tyreCompound]["dcamber1"]
					Options["targetCamber"] = math.degrees(dcamber0 / (2 * dcamber1))
					Options["dcamber0"] = dcamber0
					Options["dcamber1"] = dcamber1
					Options["LS_EXPY"] = tyreData[carName][tyreCompound]["ls_expy"]

			except (OSError, KeyError) as e:
				Options["tireRadius"] = 1
				Options["targetCamber"] = 999
				Options["dcamber0"] = 999
				Options["dcamber1"] = -999
				Options["LS_EXPY"] = 1
				ac.log("CamberExtravaganza ERROR: loadTireData: No custom tyre data found for this car")

			else:
				ac.console("Custom tyre data found for " + carName + " " + tyreCompound)
コード例 #4
0
    def __init__(self, acd: ACD, resolution: str, wheel, window_id: int):
        self.__calc = TirePsi(
            acd.get_ideal_pressure(ac.getCarTyreCompound(0), wheel))

        # Initial size is 85x85
        super(Pressure, self).__init__(70.0 if wheel.is_left() else 382.0,
                                       171.0, 60.0, 60.0)
        self._back.color = Colors.white

        if Pressure.texture_id == 0:
            Pressure.texture_id = ac.newTexture(
                "apps/python/LiveTelemetry/img/pressure.png")

        self.__lb = ac.addLabel(window_id, "")
        ac.setFontAlignment(self.__lb, "center")

        self.resize(resolution)
コード例 #5
0
def loadTireData():
    global Options
    tyreDataPath = os.path.join(os.path.dirname(__file__), "tyres_data")
    tyreData = {}
    for td in os.listdir(tyreDataPath):
        if td.endswith('.json'):
            with open(os.path.join(tyreDataPath, td), 'r') as f:
                try:
                    newData = json.load(f)
                except ValueError as e:
                    ac.log("CamberExtravaganza ERROR: Invalid JSON: " + td)
                else:
                    tyreData.update(newData)

    carName = ac.getCarName(0)
    tyreCompound = ac.getCarTyreCompound(0)
    parseTyreData(carName, tyreCompound, tyreData)
コード例 #6
0
def acUpdate(deltaT):
    # TIMERS
    global timer0, timer1, timer2

    # VARIABLES
    global totalDrivers
    global drivers
    global fastest_lap

    global race_started, replay_started, quali_started

    global qualify_session_time

    global replay_file
    global replay_data

    # Widgets
    global leaderboardWindow, driverWidget, driverComparisonWidget

    # LABELS
    global leaderboard
    global lapCountTimerLabel, leaderboardBaseLabel, leaderboardInfoBackgroundLabel, leaderboardBackgroundLabel
    global flagLabel

    # ============================
    # UPDATE TIMERS
    timer0 += deltaT
    timer1 += deltaT
    timer2 += deltaT
    # ============================

    # ================================================================
    #                            RACES
    # ================================================================
    if info.graphics.session == 2:

        # 10 times per second
        if timer2 > 0.1:
            timer2 = 0

            # =============================
            # SAVE SPLIT TIMES
            for d in drivers:
                if ac.isConnected(d.id) == 0: continue
                current_split = d.get_split_id(ac.getCarState(d.id, acsys.CS.NormalizedSplinePosition))
                if d.current_split != current_split: # save split time at each split of the track
                    d.split_times[current_split-1] = time.time()
                    d.current_split = current_split

        # 3 times per second
        if timer1 > 0.3:
            # CHECK RACE RESTART
            if race_started and info.graphics.completedLaps == 0 and info.graphics.iCurrentTime == 0:
                race_started = False

            # CHECK RACE START
            if not race_started and info.graphics.iCurrentTime > 0:
                # RESET THINGS
                fastest_lap = MAX_LAP_TIME
                LeaderboardRow.FASTEST_LAP_ID = -1
                for row in leaderboard: # clean the fastest lap marker
                    row.mark_fastest_lap()
                # in case we are coming from a qualify
                ac.setFontColor(lapCountTimerLabel, 0.86, 0.86, 0.86, 1)
                ac.setBackgroundTexture(leaderboardBaseLabel, FC.LEADERBOARD_BASE_RACE)
                ac.setVisible(lapCountTimerLabel, 1)

                race_started = True
                quali_started = False
                for d in drivers:
                    d.starting_position = ac.getCarLeaderboardPosition(d.id)
                    d.tyre = ac.getCarTyreCompound(d.id)
                    d.pits = 0
                replay_file = setup_replay_file(drivers, info.graphics.numberOfLaps) # save starting info on the drivers

            # ============================
            # POSITION UPDATE
            for i in range(totalDrivers):
                pos = ac.getCarRealTimeLeaderboardPosition(i)
                connected = ac.isConnected(i)
                if connected == 0: # mark unconnected drivers
                    leaderboard[pos].mark_out()
                    drivers[i].out = True
                else:
                    leaderboard[pos].mark_in()
                    drivers[i].out = False
                leaderboard[pos].update_name(i)

                # OVERTAKE
                if pos != drivers[i].position: # there was an overtake
                    drivers[i].timer = FC.OVERTAKE_POSITION_LABEL_TIMER # set timer
                    if pos < drivers[i].position:
                        leaderboard[pos].mark_green_position()
                    elif pos > drivers[i].position:
                        leaderboard[pos].mark_red_position()
                elif drivers[i].timer <= 0:
                    leaderboard[pos].mark_white_position()
                else:
                    drivers[i].timer -= timer1
                drivers[i].position = pos
                # END OVERTAKE

            # ============================
            # FASTEST LAP BANNER TIMER
            if fastest_lap_banner.timer > 0:
                fastest_lap_banner.timer -= timer1
                fastest_lap_banner.hide()

            # ============================
            # FLAGS
            if info.graphics.flag == 1:
                ac.setBackgroundTexture(flagLabel, FC.BLUE_FLAG)
                ac.setVisible(flagLabel, 1)
            elif info.graphics.flag == 2:
                ac.setBackgroundTexture(flagLabel, FC.YELLOW_FLAG)
                ac.setVisible(flagLabel, 1)
            elif info.graphics.flag == 5:
                ac.setBackgroundTexture(flagLabel, FC.CHECKERED_FLAG)
                ac.setVisible(flagLabel, 1)
            elif info.graphics.flag == 0:
                ac.setVisible(flagLabel, 0)

            timer1 = 0

        # Once per second
        if timer0 > 1:
            timer0 = 0
            ac.setBackgroundOpacity(leaderboardWindow, 0)
            ac.setBackgroundOpacity(driverWidget.window, 0)
            ac.setBackgroundOpacity(driverComparisonWidget.window, 0)
            ac.setBackgroundOpacity(fastest_lap_banner.window, 0)

            # ============================
            # SERVER LAP
            for i in range(totalDrivers):
                drivers[i].current_lap = ac.getCarState(i, acsys.CS.LapCount)
            lc = max((drivers[i].current_lap for i in range(totalDrivers))) + 1
            if lc >= info.graphics.numberOfLaps:
                ac.setVisible(lapCountTimerLabel, 0)
                ac.setBackgroundTexture(leaderboardBaseLabel, FC.LEADERBOARD_FINAL_LAP)
            else:
                ac.setText(lapCountTimerLabel, "%d / %d" % (lc, info.graphics.numberOfLaps))

            # ===========================
            # CALCULATE TIME DIFERENCES
            dPosition = sorted(drivers, key=lambda x: x.position)
            if LeaderboardRow.update_type == INFO_TYPE.GAPS:
                for i in range(1, len(dPosition)):
                    driver_ahead, driver = dPosition[i-1], dPosition[i]
                    timeDiff = driver.split_times[driver.current_split - 1] - driver_ahead.split_times[driver.current_split - 1]
                    if timeDiff < 0: continue # ignore these times, happens on overtakes
                    if driver.position > totalDrivers: continue # might try to update before it is possible
                    driver.timeDiff = timeDiff
                    if timeDiff > 60:
                        leaderboard[driver.position].update_time("+1 MIN")
                    else:
                        leaderboard[driver.position].update_time("+" + time_to_string(timeDiff*1000))
                leaderboard[0].update_time("Interval") # Force it
            elif LeaderboardRow.update_type == INFO_TYPE.POSITIONS:
                for d in dPosition:
                    posDiff = d.starting_position - d.position - 1
                    leaderboard[d.position].update_positions(posDiff)

            # ============================
            # MARK FASTEST LAP
            if lc > FC.FASTEST_LAP_STARTING_LAP:
                for d in drivers:
                    lap = ac.getCarState(d.id, acsys.CS.BestLap)
                    if lap != 0 and lap < fastest_lap:
                        fastest_lap = lap
                        fastest_lap_banner.show(lap, ac.getDriverName(d.id))
                        LeaderboardRow.FASTEST_LAP_ID = d.id
                        if replay_file:
                            write_fastest_lap(replay_file, info.graphics.completedLaps, info.graphics.iCurrentTime, d, fastest_lap)
                for row in leaderboard:
                    row.mark_fastest_lap()

            # ============================
            # PITS MARKER
            for row in leaderboard:
                if ac.isCarInPitline(row.driverId) == 1:
                    row.mark_enter_pits()
                    drivers[row.driverId].tyre = ac.getCarTyreCompound(row.driverId) # maybe will change tyre
                else:
                    row.mark_left_pits()
                if time.time() - drivers[row.driverId].pit_time > 20 and ac.isCarInPit(row.driverId):
                    drivers[row.driverId].pits += 1
                    drivers[row.driverId].pit_time = time.time()

            # ============================
            # CHANGE CAR FOCUS AND DRIVER WIDGET
            if race_started:
                id = ac.getFocusedCar()
                if drivers[id].position <= totalDrivers: # in case it wasnt updated yet
                    driverWidget.show(id, drivers[id].position, drivers[id].starting_position, drivers[id].tyre, drivers[id].pits)
                    if drivers[id].position == 0:
                        driverComparisonWidget.hide()
                    else:
                        for d in drivers: # find driver ahead
                            if d.position == drivers[id].position - 1:
                                driverComparisonWidget.show(d.id, d.position, id, drivers[id].position, drivers[id].timeDiff*1000)
                                break
            else:
                driverWidget.hide()
                driverComparisonWidget.hide()

            # ========================================================
            # SAVE DRIVER STATUS IN A FILE TO LOAD ON REPLAY
            if replay_file:
                write_driver_info(replay_file, info.graphics.completedLaps, info.graphics.iCurrentTime, drivers)
            # ========================================================


    # ================================================================
    #                        QUALIFY / PRACTICE
    # ================================================================
    elif info.graphics.session == 1 or (info.graphics.session == 0 and info.graphics.status != 1):

        # 3 times per second
        if timer1 > 0.3:
            # =============================================
            # QUALIFY RESTART
            if quali_started and qualify_session_time - info.graphics.sessionTimeLeft < 1:
                quali_started = False

            # =============================================
            # QUALIFY START
            if not quali_started:
                ac.setBackgroundTexture(leaderboardBaseLabel, FC.LEADERBOARD_BASE_QUALI)
                if (info.graphics.session == 0 and info.graphics.status != 0):
                    ac.setBackgroundTexture(leaderboardBaseLabel, FC.LEADERBOARD_BASE_PRACTICE)
                ac.setFontColor(lapCountTimerLabel, 0.86, 0.86, 0.86, 1)
                qualify_session_time = info.graphics.sessionTimeLeft
                fastest_lap = MAX_LAP_TIME
                LeaderboardRow.FASTEST_LAP_ID = -1
                quali_started = True
                race_started = False

            # =============================================
            # SAVE BEST LAPS FOR EACH DRIVER
            for i in range(totalDrivers):
                lap = ac.getCarState(i, acsys.CS.BestLap)
                if lap != 0:
                    drivers[i].best_lap = lap
                if lap != 0 and lap < fastest_lap:
                    fastest_lap = lap
                    fastest_lap_banner.show(lap, ac.getDriverName(i))

                # MARK IN/OUT DRIVERS
                connected = ac.isConnected(i)
                if connected == 0: # mark unconnected drivers
                    drivers[i].out = True
                else:
                    drivers[i].out = False
                
            # =============================================
            # MANAGE LEADERBOARD

            # Sorting: sort drivers by this order 1. in or out drivers, 2. best lap, 3. driver id
            dPosition = sorted(drivers, key=lambda d: (d.out, d.best_lap, d.id))

            for i in range(totalDrivers):
                if dPosition[i].out:
                    leaderboard[i].mark_out()
                else:
                    leaderboard[i].mark_in()

                leaderboard[i].update_name(dPosition[i].id)
                if dPosition[i].best_lap == MAX_LAP_TIME:
                    leaderboard[i].update_time("NO TIME")
                elif i == 0:
                    leaderboard[i].update_time(time_to_string(dPosition[i].best_lap))
                else:
                    timeDiff = dPosition[i].best_lap - dPosition[0].best_lap
                    if timeDiff > 60000:
                        leaderboard[i].update_time("+1 MIN")
                    else:
                        leaderboard[i].update_time("+" + time_to_string(timeDiff))

                # OVERTAKES
                if i != dPosition[i].position: # there was an overtake on this driver
                    dPosition[i].timer = FC.OVERTAKE_POSITION_LABEL_TIMER
                    if i < dPosition[i].position:
                        leaderboard[i].mark_green_position()
                    elif i > dPosition[i].position:
                        leaderboard[i].mark_red_position()
                elif dPosition[i].timer <= 0:
                    leaderboard[i].mark_white_position()
                else:
                    dPosition[i].timer -= timer1
                dPosition[i].position = i
                # END OVERTAKE

            # ============================
            # FASTEST LAP BANNER TIMER
            if fastest_lap_banner.timer > 0:
                fastest_lap_banner.timer -= timer1
                fastest_lap_banner.hide()

            timer1 = 0

        # Once per second
        if timer0 > 1:
            timer0 = 0
            ac.setBackgroundOpacity(leaderboardWindow, 0)
            ac.setBackgroundOpacity(driverWidget.window, 0)
            ac.setBackgroundOpacity(driverComparisonWidget.window, 0)
            ac.setBackgroundOpacity(fastest_lap_banner.window, 0)

            if quali_started:
                if info.graphics.sessionTimeLeft < 0:
                    ac.setText(lapCountTimerLabel, "0:00")
                else:
                    timeText = time_to_string(info.graphics.sessionTimeLeft)[:-4]
                    ac.setText(lapCountTimerLabel, "0:00"[:-len(timeText)] + timeText)
                if info.graphics.sessionTimeLeft < qualify_session_time / 5:
                    ac.setFontColor(lapCountTimerLabel, 1,0,0,1)

            driverWidget.hide()
            driverComparisonWidget.hide()


    # ================================================================
    #                            REPLAYS
    # ================================================================
    elif info.graphics.status == 1:

        # three times per second
        if timer1 > 0.3:
            if not replay_started:
                if info.graphics.iCurrentTime > 0:
                    replay_data = load_replay_file(drivers)
                    replay_started = True

            # ============================
            # FASTEST LAP BANNER TIMER
            if fastest_lap_banner.timer > 0:
                fastest_lap_banner.timer -= timer1
                fastest_lap_banner.hide()

            # ============================
            # GET DATA FOR THIS UPDATE
            if replay_data:
                new_positions = lookup_data(info.graphics.completedLaps, info.graphics.iCurrentTime, replay_data, drivers)

                # ============================
                # POSITION UPDATE
                for i in range(totalDrivers):
                    pos = new_positions[i]
                    if drivers[i].out: # mark unconnected drivers
                        leaderboard[pos].mark_out()
                    else:
                        leaderboard[pos].mark_in()
                    leaderboard[pos].update_name(i)

                    # OVERTAKE
                    if pos != drivers[i].position: # there was an overtake
                        drivers[i].timer = FC.OVERTAKE_POSITION_LABEL_TIMER # set timer
                        if pos < drivers[i].position:
                            leaderboard[pos].mark_green_position()
                        elif pos > drivers[i].position:
                            leaderboard[pos].mark_red_position()
                    elif drivers[i].timer <= 0:
                        leaderboard[pos].mark_white_position()
                    else:
                        drivers[i].timer -= timer1
                    drivers[i].position = pos
                    # END OVERTAKE

            timer1 = 0

        # Once per second
        if timer0 > 1:
            timer0 = 0
            ac.setBackgroundOpacity(leaderboardWindow, 0)
            ac.setBackgroundOpacity(driverWidget.window, 0)
            ac.setBackgroundOpacity(driverComparisonWidget.window, 0)
            ac.setBackgroundOpacity(fastest_lap_banner.window, 0)

            # ============================
            # GET FASTEST LAP UPDATE
            if replay_data:
                fl_data = lookup_fastest_lap(info.graphics.completedLaps, info.graphics.iCurrentTime, replay_data)
                if fl_data:
                    display_time = FC.FASTEST_LAP_DISPLAY_TIME - (info.graphics.iCurrentTime - fl_data[0]) / 1000
                    fastest_lap_banner.show(fl_data[2], ac.getDriverName(fl_data[1]), timer=display_time) # display only for the time left

            # ============================
            # SERVER LAP
            if replay_data:
                lc = max((drivers[i].current_lap for i in range(totalDrivers))) + 1
                if lc >= replay_data['nLaps']:
                    ac.setVisible(lapCountTimerLabel, 0)
                    ac.setBackgroundTexture(leaderboardBaseLabel, FC.LEADERBOARD_FINAL_LAP)
                else:
                    ac.setText(lapCountTimerLabel, "%d / %d" % (lc, replay_data['nLaps']))
                    ac.setVisible(lapCountTimerLabel, 1)
                    ac.setBackgroundTexture(leaderboardBaseLabel, FC.LEADERBOARD_BASE_RACE)

            # ============================
            # PITS MARKER
            for row in leaderboard:
                if ac.isCarInPitline(row.driverId) == 1:
                    row.mark_enter_pits()
                else:
                    row.mark_left_pits()

            # ============================
            # DRIVER WIDGET UPDATE
            if replay_started:
                id = ac.getFocusedCar()
                if drivers[id].position <= totalDrivers: # in case it wasnt updated yet
                    driverWidget.show(id, drivers[id].position, drivers[id].starting_position, drivers[id].tyre, drivers[id].pits)
                    if drivers[id].position == 0:
                        driverComparisonWidget.hide()
                    else:
                        for d in drivers: # find driver ahead
                            if d.position == drivers[id].position - 1:
                                driverComparisonWidget.show(d.id, d.position, id, drivers[id].position, drivers[id].timeDiff*1000)
                                break
            else:
                driverWidget.hide()
                driverComparisonWidget.hide()

            # ============================
            # UPDATE TIMES
            if replay_data:
                for row in leaderboard:
                    if LeaderboardRow.update_type == INFO_TYPE.GAPS:
                        row.update_time("+" + time_to_string(drivers[row.driverId].timeDiff*1000))
                        if row.row == 0:
                            row.update_time("Interval") # Force it
                    elif LeaderboardRow.update_type == INFO_TYPE.POSITIONS:
                        posDiff = drivers[row.driverId].starting_position - drivers[row.driverId].position - 1
                        row.update_positions(posDiff)
コード例 #7
0
def onFormRender(deltaT):
	global doRender, CamberIndicators, Options, Labels, redrawText

	if not doRender:
		return

	ac.glColor4f(0.9, 0.9, 0.9, 0.9)
	#~ ac.setText(Labels["targetCamber"], "{0:.1f}°".format(Options["targetCamber"]))

	# Suspension travel to find body position relative to tires
	radius = Options["tireRadius"]
	pixelsPerMeter = Options["tireHeight"] / radius
	w,x,y,z = ac.getCarState(0, acsys.CS.SuspensionTravel)
	dyFL = w * pixelsPerMeter
	dyFR = x * pixelsPerMeter
	dyRL = y * pixelsPerMeter
	dyRR = z * pixelsPerMeter

	# Draw front "car body"
	xFR = CamberIndicators["FR"].xPosition
	xFL = CamberIndicators["FL"].xPosition + Options["tireHeight"]
	y = CamberIndicators["FR"].yPosition - Options["tireHeight"] / 2
	yFL = y + dyFL
	yFR = y + dyFR
	h = Options["tireHeight"] / 4
	ac.glColor4f(0.9, 0.9, 0.9, 0.9)
	ac.glBegin(acsys.GL.Lines)
	ac.glVertex2f(xFL, yFL + h)
	ac.glVertex2f(xFR, yFR + h)
	ac.glVertex2f(xFR, yFR + h)
	ac.glVertex2f(xFR, yFR - h)
	ac.glVertex2f(xFR, yFR - h)
	ac.glVertex2f(xFL, yFL - h)
	ac.glVertex2f(xFL, yFL - h)
	ac.glVertex2f(xFL, yFL + h)
	ac.glEnd()

	# Draw rear "car body"
	xRR = CamberIndicators["RR"].xPosition
	xRL = CamberIndicators["RL"].xPosition + Options["tireHeight"]
	y = CamberIndicators["RR"].yPosition - Options["tireHeight"] / 2
	yRL = y + dyRL
	yRR = y + dyRR
	h = Options["tireHeight"] / 4
	ac.glColor4f(0.9, 0.9, 0.9, 0.9)
	ac.glBegin(acsys.GL.Lines)
	ac.glVertex2f(xRL, yRL + h)
	ac.glVertex2f(xRR, yRR + h)
	ac.glVertex2f(xRR, yRR + h)
	ac.glVertex2f(xRR, yRR - h)
	ac.glVertex2f(xRR, yRR - h)
	ac.glVertex2f(xRL, yRL - h)
	ac.glVertex2f(xRL, yRL - h)
	ac.glVertex2f(xRL, yRL + h)
	ac.glEnd()

	# Draw flappy gauges
	h *= 0.75
	CamberIndicators["FL"].drawTire(xFL, yFL, h, flip=True)
	CamberIndicators["FR"].drawTire(xFR, yFR, h)
	CamberIndicators["RL"].drawTire(xRL, yRL, h, flip=True)
	CamberIndicators["RR"].drawTire(xRR, yRR, h)

	# Draw history graphs
	if Options["drawGraphs"]:
		CamberIndicators["FL"].drawGraph(flip=True)
		CamberIndicators["FR"].drawGraph()
		CamberIndicators["RL"].drawGraph(flip=True)
		CamberIndicators["RR"].drawGraph()

	flC, frC, rlC, rrC = ac.getCarState(0, acsys.CS.CamberRad)
	CamberIndicators["FL"].setValue(flC, deltaT, Options["optimalCamberF"])
	CamberIndicators["FR"].setValue(frC, deltaT, Options["optimalCamberF"])
	CamberIndicators["RL"].setValue(rlC, deltaT, Options["optimalCamberR"])
	CamberIndicators["RR"].setValue(rrC, deltaT, Options["optimalCamberR"])

	# Check if tyre compound changed
	tyreCompound = ac.getCarTyreCompound(0)
	if tyreCompound != Options["tyreCompound"]:
		loadTireData()
		Options["tyreCompound"] = tyreCompound

	# Weight Front and Rear by lateral weight transfer
	filter = 0.97
	flL, frL, rlL, rrL = ac.getCarState(0, acsys.CS.Load)

	outer = max(0.001, flL, frL)
	inner = min(flL, frL)
	camberSplit = abs(flC - frC)
	# DY_LS_FL = DY_REF * pow(TIRE_LOAD_FL / FZ0, LS_EXPY)
	ls_outer = pow(outer, Options["LS_EXPY"])
	ls_inner = pow(inner, Options["LS_EXPY"])
	weightXfer = ls_outer / (ls_inner + ls_outer)
	# (2*(1-w)*D1*rad(c)-(1-2*w)*D0)/(2*D1)
	oldTargetCamber = Options["optimalCamberF"]
	Options["optimalCamberF"] = math.degrees((2 * (1 - weightXfer) * Options["dcamber1"] * math.radians(camberSplit) - (1 - 2 * weightXfer) * Options["dcamber0"]) / (2 * Options["dcamber1"]))
	Options["optimalCamberF"] = filter * oldTargetCamber + (1 - filter) * Options["optimalCamberF"]

	outer = max(0.001, rlL, rrL)
	inner = min(rlL, rrL)
	camberSplit = abs(rlC - rrC)
	# DY_LS_FL = DY_REF * pow(TIRE_LOAD_FL / FZ0, LS_EXPY)
	ls_outer = pow(outer, Options["LS_EXPY"])
	ls_inner = pow(inner, Options["LS_EXPY"])
	weightXfer = ls_outer / (ls_inner + ls_outer)
	# (2*(1-w)*D1*rad(c)-(1-2*w)*D0)/(2*D1)
	oldTargetCamber = Options["optimalCamberR"]
	Options["optimalCamberR"] = math.degrees((2 * (1 - weightXfer) * Options["dcamber1"] * math.radians(camberSplit) - (1 - 2 * weightXfer) * Options["dcamber0"]) / (2 * Options["dcamber1"]))
	Options["optimalCamberR"] = filter * oldTargetCamber + (1 - filter) * Options["optimalCamberR"]

	ac.setText(Labels["targetCamberF"], "{0:.1f}°".format(Options["optimalCamberF"]))
	ac.setText(Labels["targetCamberR"], "{0:.1f}°".format(Options["optimalCamberR"]))

	if redrawText:
		updateTextInputs()
		redrawText = False