1069 lines
42 KiB
C++
1069 lines
42 KiB
C++
/***************************************************************************
|
|
|
|
file : wheel.cpp
|
|
created : Sun Mar 19 00:09:06 CET 2000
|
|
copyright : (C) 2000 by Eric Espie
|
|
email : torcs@free.fr
|
|
version : $Id: wheel.cpp 4983 2012-10-07 13:53:17Z pouillot $
|
|
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include <tgf.h>
|
|
#include "sim.h"
|
|
|
|
static const char *WheelSect[4] = {SECT_FRNTRGTWHEEL, SECT_FRNTLFTWHEEL, SECT_REARRGTWHEEL, SECT_REARLFTWHEEL};
|
|
static const char *SuspSect[4] = {SECT_FRNTRGTSUSP, SECT_FRNTLFTSUSP, SECT_REARRGTSUSP, SECT_REARLFTSUSP};
|
|
static const char *BrkSect[4] = {SECT_FRNTRGTBRAKE, SECT_FRNTLFTBRAKE, SECT_REARRGTBRAKE, SECT_REARLFTBRAKE};
|
|
|
|
void SimWheelConfig(tCar *car, int index)
|
|
{
|
|
void *hdle = car->params;
|
|
char path[256];
|
|
tCarElt *carElt = car->carElt;
|
|
tWheel *wheel = &(car->wheel[index]);
|
|
tdble rimdiam, tirewidth, tireratio, tireheight, patchLen;
|
|
tdble Ca, RFactor, EFactor;
|
|
tCarSetupItem *setupToe = &(car->carElt->setup.toe[index]);
|
|
tCarSetupItem *setupCamber = &(car->carElt->setup.camber[index]);
|
|
tCarSetupItem *setupPressure = &(car->carElt->setup.tirePressure[index]);
|
|
tCarSetupItem *setupOpLoad = &(car->carElt->setup.tireOpLoad[index]);
|
|
tCarSetupItem *setupCompound = &(car->carElt->setup.tireCompound);
|
|
|
|
/* Note: ride height is already read in SimAxleConfig() */
|
|
|
|
setupToe->desired_value = setupToe->min = setupToe->max = 0.0f;
|
|
GfParmGetNumWithLimits(hdle, WheelSect[index], PRM_TOE, (char*)NULL, &(setupToe->desired_value), &(setupToe->min), &(setupToe->max));
|
|
setupToe->changed = true;
|
|
setupToe->stepsize = (tdble) DEG2RAD(0.1);
|
|
|
|
setupCamber->desired_value = setupCamber->min = setupCamber->max = 0.0f;
|
|
GfParmGetNumWithLimits(hdle, WheelSect[index], PRM_CAMBER, (char*)NULL, &(setupCamber->desired_value), &(setupCamber->min), &(setupCamber->max));
|
|
setupCamber->changed = true;
|
|
setupCamber->stepsize = (tdble) DEG2RAD(0.1);
|
|
|
|
setupPressure->desired_value = setupPressure->min = setupPressure->max = 275600;
|
|
GfParmGetNumWithLimits(hdle, WheelSect[index], PRM_PRESSURE, (char*)NULL, &(setupPressure->desired_value), &(setupPressure->min), &(setupPressure->max));
|
|
setupPressure->changed = true;
|
|
setupPressure->stepsize = 10000;
|
|
GfLogInfo(" # Car pressure tire simu = %.2f\n", setupPressure->desired_value);
|
|
|
|
setupOpLoad->desired_value = setupOpLoad->min = setupOpLoad->max = wheel->weight0 * 1.2f;
|
|
GfParmGetNumWithLimits(hdle, WheelSect[index], PRM_OPLOAD, (char*)NULL, &(setupOpLoad->desired_value), &(setupOpLoad->min), &(setupOpLoad->max));
|
|
setupOpLoad->changed = true;
|
|
setupOpLoad->stepsize = 100;
|
|
|
|
setupCompound->desired_value = setupCompound->min = setupCompound->max = 5;
|
|
GfParmGetNumWithLimits(hdle, SECT_TIRESET, PRM_COMPOUNDS_SET, (char*)NULL, &(setupCompound->desired_value), &(setupCompound->min), &(setupCompound->max));
|
|
setupCompound->changed = true;
|
|
setupCompound->stepsize = 1;
|
|
|
|
// Additional parameters for the tire wear model
|
|
wheel->treadThinkness = GfParmGetNum(hdle, WheelSect[index], PRM_TREADTHICKNESS, (char*)NULL, 0.005f); // default 5 [mm]
|
|
tdble rimmass = GfParmGetNum(hdle, WheelSect[index], PRM_RIMMASS, (char*)NULL, 7.0f); // default 7 [kg]
|
|
wheel->hysteresisFactor = GfParmGetNum(hdle, WheelSect[index], PRM_HYSTERESIS, (char*)NULL, 1.0f); // default 1.0 [-]
|
|
wheel->coolingFactor = GfParmGetNum(hdle, WheelSect[index], PRM_TIRECOOLING, (char*)NULL, 0.0f); // default 0.0 [-] maintain compatibility with older cars
|
|
wheel->latHeatFactor = GfParmGetNum(hdle, WheelSect[index], PRM_LATMUHEATING, (char*)NULL, 0.0f); // default 0.0 [-]
|
|
wheel->longHeatFactor = GfParmGetNum(hdle, WheelSect[index], PRM_LONGMUHEATING, (char*)NULL, 0.0f); // default 0.0 [-]
|
|
wheel->tireSpeedCoolFactor = GfParmGetNum(hdle, WheelSect[index], PRM_TIRESPDCOOLING, (char*)NULL, 0.0f); // default 0.0 [-] but recommend 0.5-1.25 for most cars
|
|
wheel->wearFactor = GfParmGetNum(hdle, WheelSect[index], PRM_WEAR, (char*)NULL, 1.0f);
|
|
|
|
if (car->features & FEAT_COMPOUNDS)
|
|
{
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_SOFT);
|
|
wheel->hysteresisFactorC[1] = GfParmGetNum(hdle, path, PRM_HYSTERESIS, (char*)NULL, wheel->hysteresisFactor);
|
|
wheel->coolingFactorC[1] = GfParmGetNum(hdle, path, PRM_TIRECOOLING, (char*)NULL, wheel->coolingFactor);
|
|
wheel->latHeatFactorC[1] = GfParmGetNum(hdle, path, PRM_LATMUHEATING, (char*)NULL, wheel->latHeatFactor);
|
|
wheel->longHeatFactorC[1] = GfParmGetNum(hdle, path, PRM_LONGMUHEATING, (char*)NULL, wheel->longHeatFactor);
|
|
wheel->tireSpeedCoolFactorC[1] = GfParmGetNum(hdle, path, PRM_TIRESPDCOOLING, (char*)NULL, wheel->tireSpeedCoolFactor);
|
|
wheel->wearFactorC[1] = GfParmGetNum(hdle, path, PRM_WEAR, (char*)NULL, wheel->wearFactor);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_MEDIUM);
|
|
wheel->hysteresisFactorC[2] = GfParmGetNum(hdle, path, PRM_HYSTERESIS, (char*)NULL, wheel->hysteresisFactor);
|
|
wheel->coolingFactorC[2] = GfParmGetNum(hdle, path, PRM_TIRECOOLING, (char*)NULL, wheel->coolingFactor);
|
|
wheel->latHeatFactorC[2] = GfParmGetNum(hdle, path, PRM_LATMUHEATING, (char*)NULL, wheel->latHeatFactor);
|
|
wheel->longHeatFactorC[2] = GfParmGetNum(hdle, path, PRM_LONGMUHEATING, (char*)NULL, wheel->longHeatFactor);
|
|
wheel->tireSpeedCoolFactorC[2] = GfParmGetNum(hdle, path, PRM_TIRESPDCOOLING, (char*)NULL, wheel->tireSpeedCoolFactor);
|
|
wheel->wearFactorC[2] = GfParmGetNum(hdle, path, PRM_WEAR, (char*)NULL, wheel->wearFactor);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_HARD);
|
|
wheel->hysteresisFactorC[3] = GfParmGetNum(hdle, path, PRM_HYSTERESIS, (char*)NULL, wheel->hysteresisFactor);
|
|
wheel->coolingFactorC[3] = GfParmGetNum(hdle, path, PRM_TIRECOOLING, (char*)NULL, wheel->coolingFactor);
|
|
wheel->latHeatFactorC[3] = GfParmGetNum(hdle, path, PRM_LATMUHEATING, (char*)NULL, wheel->latHeatFactor);
|
|
wheel->longHeatFactorC[3] = GfParmGetNum(hdle, path, PRM_LONGMUHEATING, (char*)NULL, wheel->longHeatFactor);
|
|
wheel->tireSpeedCoolFactorC[3] = GfParmGetNum(hdle, path, PRM_TIRESPDCOOLING, (char*)NULL, wheel->tireSpeedCoolFactor);
|
|
wheel->wearFactorC[3] = GfParmGetNum(hdle, path, PRM_WEAR, (char*)NULL, wheel->wearFactor);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_WET);
|
|
wheel->hysteresisFactorC[4] = GfParmGetNum(hdle, path, PRM_HYSTERESIS, (char*)NULL, wheel->hysteresisFactor);
|
|
wheel->coolingFactorC[4] = GfParmGetNum(hdle, path, PRM_TIRECOOLING, (char*)NULL, wheel->coolingFactor);
|
|
wheel->latHeatFactorC[4] = GfParmGetNum(hdle, path, PRM_LATMUHEATING, (char*)NULL, wheel->latHeatFactor);
|
|
wheel->longHeatFactorC[4] = GfParmGetNum(hdle, path, PRM_LONGMUHEATING, (char*)NULL, wheel->longHeatFactor);
|
|
wheel->tireSpeedCoolFactorC[4] = GfParmGetNum(hdle, path, PRM_TIRESPDCOOLING, (char*)NULL, wheel->tireSpeedCoolFactor);
|
|
wheel->wearFactorC[4] = GfParmGetNum(hdle, path, PRM_WEAR, (char*)NULL, wheel->wearFactor);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_EXTREM_WET);
|
|
wheel->hysteresisFactorC[5] = GfParmGetNum(hdle, path, PRM_HYSTERESIS, (char*)NULL, wheel->hysteresisFactor);
|
|
wheel->coolingFactorC[5] = GfParmGetNum(hdle, path, PRM_TIRECOOLING, (char*)NULL, wheel->coolingFactor);
|
|
wheel->latHeatFactorC[5] = GfParmGetNum(hdle, path, PRM_LATMUHEATING, (char*)NULL, wheel->latHeatFactor);
|
|
wheel->longHeatFactorC[5] = GfParmGetNum(hdle, path, PRM_LONGMUHEATING, (char*)NULL, wheel->longHeatFactor);
|
|
wheel->tireSpeedCoolFactorC[5] = GfParmGetNum(hdle, path, PRM_TIRESPDCOOLING, (char*)NULL, wheel->tireSpeedCoolFactor);
|
|
wheel->wearFactorC[5] = GfParmGetNum(hdle, path, PRM_WEAR, (char*)NULL, wheel->wearFactor);
|
|
|
|
if (SimRain < 1)
|
|
{
|
|
wheel->hysteresisFactorC[4] *= 1.5;
|
|
wheel->hysteresisFactorC[5] *= 2.0;
|
|
wheel->wearFactorC[4] *= 3.5;
|
|
wheel->wearFactorC[5] *= 3.5;
|
|
GfLogInfo("# Simu wear factor compound with no rain wet = %.4f - extreme wet = %.4f\n",
|
|
wheel->wearFactorC[4], wheel->wearFactorC[5]);
|
|
}
|
|
}
|
|
|
|
rimdiam = GfParmGetNum(hdle, WheelSect[index], PRM_RIMDIAM, (char*)NULL, 0.33f);
|
|
tirewidth = GfParmGetNum(hdle, WheelSect[index], PRM_TIREWIDTH, (char*)NULL, 0.145f);
|
|
tireheight = GfParmGetNum(hdle, WheelSect[index], PRM_TIREHEIGHT, (char*)NULL, -1.0f);
|
|
tireratio = GfParmGetNum(hdle, WheelSect[index], PRM_TIRERATIO, (char*)NULL, 0.75f);
|
|
wheel->mu = GfParmGetNum(hdle, WheelSect[index], PRM_MU, (char*)NULL, 1.0f);
|
|
|
|
if (car->features & FEAT_COMPOUNDS)
|
|
{
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_SOFT);
|
|
wheel->muC[1] = GfParmGetNum(hdle, path, PRM_MU, (char*)NULL, wheel->mu);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_MEDIUM);
|
|
wheel->muC[2] = GfParmGetNum(hdle, path, PRM_MU, (char*)NULL, wheel->mu);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_HARD);
|
|
wheel->muC[3] = GfParmGetNum(hdle, path, PRM_MU, (char*)NULL, wheel->mu);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_WET);
|
|
wheel->muC[4] = GfParmGetNum(hdle, path, PRM_MU, (char*)NULL, wheel->mu);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_EXTREM_WET);
|
|
wheel->muC[5] = GfParmGetNum(hdle, path, PRM_MU, (char*)NULL, wheel->mu);
|
|
GfLogInfo("# Simu MU compound soft = %.3f - medium = %.3f - hard = %.3f - wet = %.3f - extreme wet = %.3f\n",
|
|
wheel->muC[1], wheel->muC[2], wheel->muC[3], wheel->muC[4], wheel->muC[5]);
|
|
|
|
if(SimRain < 1)
|
|
{
|
|
wheel->muC[4] -= 0.2;
|
|
wheel->muC[5] -= 0.3;
|
|
GfLogInfo("# Simu MU compound with no rain wet = %.3f - extreme wet = %.3f\n",
|
|
wheel->muC[4], wheel->muC[5]);
|
|
}
|
|
}
|
|
|
|
wheel->I = GfParmGetNum(hdle, WheelSect[index], PRM_INERTIA, (char*)NULL, 1.5f);
|
|
//BUG: the next line should go after SimBrakeConfig to have an effect
|
|
wheel->I += wheel->brake.I; // add brake inertia
|
|
wheel->staticPos.y = GfParmGetNum(hdle, WheelSect[index], PRM_YPOS, (char*)NULL, 0.0f);
|
|
Ca = GfParmGetNum(hdle, WheelSect[index], PRM_CA, (char*)NULL, 30.0f);
|
|
RFactor = GfParmGetNum(hdle, WheelSect[index], PRM_RFACTOR, (char*)NULL, 0.8f);
|
|
EFactor = GfParmGetNum(hdle, WheelSect[index], PRM_EFACTOR, (char*)NULL, 0.7f);
|
|
wheel->lfMax = GfParmGetNum(hdle, WheelSect[index], PRM_LOADFMAX, (char*)NULL, 1.6f);
|
|
wheel->lfMin = GfParmGetNum(hdle, WheelSect[index], PRM_LOADFMIN, (char*)NULL, 0.8f);
|
|
wheel->AlignTqFactor = GfParmGetNum(hdle, WheelSect[index], PRM_ALIGNTQFACTOR, (char*)NULL, 0.6f);
|
|
wheel->mass = GfParmGetNum(hdle, WheelSect[index], PRM_MASS, (char*)NULL, 20.0f);
|
|
|
|
wheel->lfMin = MIN(0.9f, wheel->lfMin);
|
|
wheel->lfMax = MAX(1.1f, wheel->lfMax);
|
|
|
|
if (wheel->AlignTqFactor < 0.1f )
|
|
{
|
|
wheel->AlignTqFactor = 0.1f;
|
|
}
|
|
|
|
// Absolute pressure of cold tire.
|
|
wheel->pressure = MIN(setupPressure->max, MAX(setupPressure->min, setupPressure->desired_value));
|
|
wheel->currentPressure = MIN(setupPressure->max, MAX(setupPressure->min, setupPressure->desired_value));
|
|
|
|
if (car->features & FEAT_COMPOUNDS)
|
|
{
|
|
wheel->tireSet = MIN(setupCompound->max, MAX(setupCompound->min, setupCompound->desired_value));
|
|
wheel->mu = wheel->muC[wheel->tireSet];
|
|
wheel->hysteresisFactor = wheel->hysteresisFactorC[wheel->tireSet];
|
|
wheel->coolingFactor = wheel->coolingFactorC[wheel->tireSet];
|
|
wheel->latHeatFactor = wheel->latHeatFactorC[wheel->tireSet];
|
|
wheel->longHeatFactor = wheel->longHeatFactorC[wheel->tireSet];
|
|
wheel->tireSpeedCoolFactor = wheel->tireSpeedCoolFactorC[wheel->tireSet];
|
|
wheel->wearFactor = wheel->wearFactorC[wheel->tireSet];
|
|
}
|
|
|
|
RFactor = MIN(1.0f, RFactor);
|
|
RFactor = MAX(0.1f, RFactor);
|
|
EFactor = MIN(1.0f, EFactor);
|
|
|
|
if (tireheight > 0.0)
|
|
wheel->radius = rimdiam / 2.0f + tireheight;
|
|
else
|
|
wheel->radius = rimdiam / 2.0f + tirewidth * tireratio;
|
|
|
|
patchLen = wheel->weight0 / (tirewidth * wheel->pressure);
|
|
wheel->tireSpringRate = wheel->weight0 / (wheel->radius * (1.0f - cos(asin(patchLen / (2.0f * wheel->radius)))));
|
|
wheel->relPos.x = wheel->staticPos.x = car->axle[index/2].xpos;
|
|
wheel->relPos.y = wheel->staticPos.y;
|
|
/* BUG? susp.spring.x0 is still 0 here, maybe move after SimSuspReConfig in SimWheelReConfig? */
|
|
wheel->relPos.z = wheel->radius - wheel->susp.spring.x0;
|
|
wheel->relPos.ay = wheel->relPos.az = 0.0f;
|
|
wheel->steer = 0.0f;
|
|
|
|
/* temperature and degradation */
|
|
wheel->Tinit = GfParmGetNum(hdle, WheelSect[index], PRM_INITTEMP, (char*)NULL, Tair);
|
|
wheel->treadDepth = 1.0;
|
|
wheel->Topt = GfParmGetNum(hdle, WheelSect[index], PRM_OPTTEMP, (char*)NULL, 350.0f);
|
|
|
|
if (car->features & FEAT_COMPOUNDS)
|
|
{
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_SOFT);
|
|
wheel->TinitC[1] = GfParmGetNum(hdle, path, PRM_INITTEMP, (char*)NULL, wheel->Tinit);
|
|
wheel->ToptC[1] = GfParmGetNum(hdle, path, PRM_OPTTEMP, (char*)NULL, wheel->Topt);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_MEDIUM);
|
|
wheel->TinitC[2] = GfParmGetNum(hdle, path, PRM_INITTEMP, (char*)NULL, wheel->Tinit);
|
|
wheel->ToptC[2] = GfParmGetNum(hdle, path, PRM_OPTTEMP, (char*)NULL, wheel->Topt);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_HARD);
|
|
wheel->TinitC[3] = GfParmGetNum(hdle, path, PRM_INITTEMP, (char*)NULL, wheel->Tinit);
|
|
wheel->ToptC[3] = GfParmGetNum(hdle, path, PRM_OPTTEMP, (char*)NULL, wheel->Topt);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_WET);
|
|
wheel->TinitC[4] = GfParmGetNum(hdle, path, PRM_INITTEMP, (char*)NULL, wheel->Tinit);
|
|
wheel->ToptC[4] = GfParmGetNum(hdle, path, PRM_OPTTEMP, (char*)NULL, wheel->Topt);
|
|
|
|
sprintf(path, "%s/%s/%s", WheelSect[index], SECT_COMPOUNDS, SECT_EXTREM_WET);
|
|
wheel->TinitC[5] = GfParmGetNum(hdle, path, PRM_INITTEMP, (char*)NULL, wheel->Tinit);
|
|
wheel->ToptC[5] = GfParmGetNum(hdle, path, PRM_OPTTEMP, (char*)NULL, wheel->Topt);
|
|
GfLogInfo("# Simu Optimal temperature compound soft = %.3f - medium = %.3f - hard = %.3f - wet = %.3f - extreme wet = %.3f\n",
|
|
wheel->ToptC[1] -273.15, wheel->ToptC[2] - 273.15, wheel->ToptC[3] - 273.15, wheel->ToptC[4] - 273.15, wheel->ToptC[5] - 273.15);
|
|
|
|
wheel->Tinit = wheel->TinitC[wheel->tireSet];
|
|
wheel->Topt = wheel->ToptC[wheel->tireSet];
|
|
}
|
|
|
|
if (car->features & FEAT_TIRETEMPDEG)
|
|
{
|
|
wheel->Ttire = wheel->Tinit;
|
|
|
|
}
|
|
else
|
|
{
|
|
wheel->Ttire = wheel->Topt;
|
|
}
|
|
|
|
//wheel->heatingm -=SimRain;
|
|
const tdble rubberDensity = 930.0f; // Density of Rubber (NR) in [kg/m^3].
|
|
wheel->treadMass = (2.0f*wheel->radius - wheel->treadThinkness)*PI*tirewidth*wheel->treadThinkness*rubberDensity;
|
|
wheel->baseMass = wheel->mass - wheel->treadMass - rimmass;
|
|
|
|
if (wheel->baseMass < 0.0f)
|
|
{
|
|
wheel->baseMass = 3.0f;
|
|
GfError("Wheel mass minus tire tread mass minus rim mass is smaller than 0.0kg, setting it to 3.0 kg");
|
|
}
|
|
|
|
// Surface area for convection model
|
|
tdble innerRadius = rimdiam / 2.0f;
|
|
tdble tireSideArea = PI*(wheel->radius*wheel->radius - innerRadius*innerRadius);
|
|
wheel->tireConvectionSurface = 2.0f*(PI*tirewidth*wheel->radius + tireSideArea);
|
|
|
|
// Mass of gas in the tire m=P*V/(R*T)
|
|
tdble temperature = Tair; // Kelvin
|
|
tdble volume = tireSideArea*tirewidth; // meter*meter*meter
|
|
tdble nitrogenR = 296.8f; // Joule/(kg*Kelvin), N2
|
|
|
|
wheel->tireGasMass = (wheel->pressure * volume) / (nitrogenR * temperature); // kg
|
|
|
|
/* components */
|
|
SimSuspConfig(car, hdle, SuspSect[index], &(wheel->susp), index);
|
|
SimBrakeConfig(hdle, BrkSect[index], &(wheel->brake));
|
|
|
|
carElt->_rimRadius(index) = rimdiam / 2.0f;
|
|
|
|
if (tireheight > 0.0)
|
|
carElt->_tireHeight(index) = tireheight;
|
|
else
|
|
carElt->_tireHeight(index) = tirewidth * tireratio;
|
|
|
|
carElt->_tireWidth(index) = tirewidth;
|
|
carElt->_brakeDiskRadius(index) = wheel->brake.radius;
|
|
carElt->_wheelRadius(index) = wheel->radius;
|
|
|
|
/* initialize carElt values even if tire temperature and wear feature is not enabled */
|
|
carElt->_tyreT_opt(index) = wheel->Topt;
|
|
carElt->_tyreT_in(index) = wheel->Ttire;
|
|
carElt->_tyreT_mid(index) = wheel->Ttire;
|
|
carElt->_tyreT_out(index) = wheel->Ttire;
|
|
carElt->_tyreCondition(index) = 1.0;
|
|
carElt->_tyreTreadDepth(index) = wheel->treadDepth;
|
|
carElt->_tyreCurrentPressure(index) = wheel->pressure;
|
|
carElt->_tyreCompound(index) = wheel->tireSet;
|
|
|
|
wheel->mfC = (tdble)(2.0 - asin(RFactor) * 2.0 / PI);
|
|
wheel->mfB = Ca / wheel->mfC;
|
|
wheel->mfE = EFactor;
|
|
|
|
wheel->lfK = log((1.0f - wheel->lfMin) / (wheel->lfMax - wheel->lfMin));
|
|
|
|
wheel->feedBack.I += wheel->I;
|
|
wheel->feedBack.spinVel = 0.0f;
|
|
wheel->feedBack.Tq = 0.0f;
|
|
wheel->feedBack.brkTq = 0.0f;
|
|
wheel->torques.x = wheel->torques.y = wheel->torques.z = 0.0f;
|
|
|
|
/* calculate optimal slip value */
|
|
tdble s, Bx, low, high;
|
|
int i;
|
|
//wheel->mfC * atan(Bx * (1.0f - wheel->mfE) + wheel->mfE * atan(Bx)) == PI/2
|
|
low = 0.0;
|
|
high = wheel->mfB;
|
|
|
|
if (wheel->mfC * atan(high * (1.0f - wheel->mfE) + wheel->mfE * atan(high)) < PI_2)
|
|
{
|
|
/* tire parameters are unphysical*/
|
|
s = 1.0;
|
|
GfLogWarning("Tire magic curve parameters are unphysical!");
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
Bx = (tdble)(0.5 * (low + high));
|
|
if (wheel->mfC * atan(Bx * (1.0f - wheel->mfE) + wheel->mfE * atan(Bx)) < PI_2)
|
|
{
|
|
low = Bx;
|
|
}
|
|
else
|
|
{
|
|
high = Bx;
|
|
}
|
|
}
|
|
|
|
s = (tdble)(0.5 * (low + high) / wheel->mfB);
|
|
}
|
|
|
|
car->carElt->_wheelSlipOpt(index) = s;
|
|
GfLogInfo("SimuV4 MU = %.3f - Topt = %.2f - Tinit = %.2f - wear = %.2f\n",
|
|
wheel->mu, wheel->Topt - 273.15, wheel->Tinit -273.15, wheel->wearFactor);
|
|
}
|
|
|
|
void SimWheelReConfig(tCar *car, int index)
|
|
{/* called by SimCarReConfig in car.cpp */
|
|
tCarElt *carElt = car->carElt;
|
|
tWheel *wheel = &(car->wheel[index]);
|
|
tdble x0;
|
|
|
|
tCarSetupItem *setupRideHeight = &(car->carElt->setup.rideHeight[index]);
|
|
tCarSetupItem *setupToe = &(car->carElt->setup.toe[index]);
|
|
tCarSetupItem *setupCamber = &(car->carElt->setup.camber[index]);
|
|
tCarSetupItem *setupPressure = &(car->carElt->setup.tirePressure[index]);
|
|
tCarSetupItem *setupOpLoad = &(car->carElt->setup.tireOpLoad[index]);
|
|
tCarSetupItem *setupCompound = &(car->carElt->setup.tireCompound);
|
|
tdble patchLen;
|
|
|
|
if (setupToe->changed)
|
|
{
|
|
wheel->staticPos.az = MIN(setupToe->max, MAX(setupToe->min, setupToe->desired_value));
|
|
setupToe->value = wheel->staticPos.az;
|
|
setupToe->changed = false;
|
|
}
|
|
|
|
if (setupCamber->changed)
|
|
{
|
|
wheel->staticPos.ax = MIN(setupCamber->max, MAX(setupCamber->min, setupCamber->desired_value));
|
|
if (index % 2)
|
|
{
|
|
wheel->relPos.ax = -wheel->staticPos.ax;
|
|
}
|
|
else
|
|
{
|
|
wheel->relPos.ax = wheel->staticPos.ax;
|
|
}
|
|
|
|
wheel->cosax = cos(wheel->relPos.ax);
|
|
wheel->sinax = sin(wheel->relPos.ax);
|
|
setupCamber->value = wheel->staticPos.ax;
|
|
setupCamber -> changed = false;
|
|
}
|
|
|
|
if ( setupPressure->changed ||car->carElt->setup.FRWeightRep.changed )
|
|
{
|
|
wheel->pressure = MIN(setupPressure->max, MAX(setupPressure->min, setupPressure->desired_value));
|
|
patchLen = wheel->weight0 / (carElt->_tireWidth(index) * wheel->pressure);
|
|
wheel->tireSpringRate = wheel->weight0 / (wheel->radius * (1.0f - cos(asin(patchLen / (2.0f * wheel->radius)))));
|
|
setupPressure->value = wheel->pressure;
|
|
setupPressure->changed = false;
|
|
GfLogInfo(" # Car simu setup Pressure simuReWheelReConfig = %.2f\n", wheel->pressure);
|
|
}
|
|
|
|
if (setupOpLoad->changed)
|
|
{
|
|
wheel->opLoad = MIN(setupOpLoad->max, MAX(setupOpLoad->min, setupOpLoad->desired_value));
|
|
setupOpLoad->value = wheel->opLoad;
|
|
setupOpLoad->changed = false;
|
|
}
|
|
|
|
if (car->features & FEAT_COMPOUNDS)
|
|
{
|
|
wheel->tireSet = MIN(setupCompound->max, MAX(setupCompound->min, setupCompound->desired_value));
|
|
GfLogInfo("# Tireset value = %d\n", wheel->tireSet);
|
|
setupCompound->value = wheel->tireSet;
|
|
setupCompound->changed = false;
|
|
wheel->mu = wheel->muC[wheel->tireSet];
|
|
wheel->Tinit = wheel->TinitC[wheel->tireSet];
|
|
wheel->Topt = wheel->ToptC[wheel->tireSet];
|
|
wheel->hysteresisFactor = wheel->hysteresisFactorC[wheel->tireSet];
|
|
wheel->coolingFactor = wheel->coolingFactorC[wheel->tireSet];
|
|
wheel->latHeatFactor = wheel->latHeatFactorC[wheel->tireSet];
|
|
wheel->longHeatFactor = wheel->longHeatFactorC[wheel->tireSet];
|
|
wheel->tireSpeedCoolFactor = wheel->tireSpeedCoolFactorC[wheel->tireSet];
|
|
wheel->wearFactor = wheel->wearFactorC[wheel->tireSet];
|
|
GfLogInfo("# SimuV4 tire compound changed mu = %.3f - hysteresis = %.2f - wear factor = %.7f\n", wheel->mu, wheel->hysteresisFactor,
|
|
wheel->wearFactor);
|
|
}
|
|
|
|
x0 = setupRideHeight->value;
|
|
SimSuspReConfig(car, &(wheel->susp), index, wheel->weight0, x0);
|
|
GfLogInfo("SimuV4 MU = %.3f - Topt = %.2f - Tinit = %.2f - wear = %.2f\n",
|
|
wheel->mu, wheel->Topt - 273.15, wheel->Tinit - 273.15, wheel->wearFactor);
|
|
}
|
|
|
|
void SimWheelUpdateRide(tCar *car, int index)
|
|
{
|
|
tWheel *wheel = &(car->wheel[index]);
|
|
tdble Zroad;
|
|
|
|
// compute suspension travel
|
|
RtTrackGlobal2Local(car->trkPos.seg, wheel->pos.x, wheel->pos.y, &(wheel->trkPos), TR_LPOS_SEGMENT);
|
|
wheel->zRoad = Zroad = RtTrackHeightL(&(wheel->trkPos));
|
|
|
|
// Wheel susp.x is not the wheel movement, look at SimSuspCheckIn, it becomes there scaled with
|
|
// susp->spring.bellcrank, so we invert this here.
|
|
|
|
tdble new_susp_x = (wheel->susp.x - wheel->susp.v * SimDeltaTime) / wheel->susp.spring.bellcrank;
|
|
tdble max_extend = wheel->pos.z - Zroad;
|
|
wheel->rideHeight = max_extend;
|
|
|
|
if (max_extend > new_susp_x + 0.01)
|
|
{
|
|
wheel->susp.state = SIM_WH_INAIR;
|
|
}
|
|
else
|
|
{
|
|
wheel->susp.state = 0;
|
|
}
|
|
|
|
if (max_extend < new_susp_x)
|
|
{
|
|
new_susp_x = max_extend;
|
|
}
|
|
|
|
tdble prex = wheel->susp.x;
|
|
tdble prev = wheel->susp.v;
|
|
wheel->susp.x = new_susp_x;
|
|
|
|
// verify the suspension travel, beware, wheel->susp.x will be scaled by SimSuspCheckIn
|
|
SimSuspCheckIn(&(wheel->susp));
|
|
wheel->susp.v = (prex - wheel->susp.x) / SimDeltaTime;
|
|
wheel->susp.a = (prev - wheel->susp.v) / SimDeltaTime;
|
|
|
|
// update wheel brake
|
|
SimBrakeUpdate(car, wheel, &(wheel->brake));
|
|
|
|
// Option TCL ...
|
|
if (car->features & FEAT_TCLINSIMU)
|
|
{
|
|
if (index == 3)
|
|
{ // After using the values for the last wheel
|
|
tEngine *engine = &(car->engine);
|
|
engine->TCL = 1.0; // Reset the TCL accel command
|
|
}
|
|
}
|
|
// ... Option TCL
|
|
}
|
|
|
|
void SimWheelUpdateForce(tCar *car, int index)
|
|
{
|
|
tWheel *wheel = &(car->wheel[index]);
|
|
tdble axleFz = wheel->axleFz;
|
|
tdble vt, v, v2, wrl; // wheel related velocity
|
|
tdble Fn, Ft;
|
|
tdble waz;
|
|
tdble CosA, SinA;
|
|
tdble s, sa, sx, sy; // slip vector
|
|
tdble stmp, F, Bx;
|
|
tdble mu;
|
|
tdble MaxTorqueSlipAngle;
|
|
tdble tireCond = 1.0;
|
|
tdble reaction_force = 0.0f;
|
|
wheel->state = 0;
|
|
|
|
// VERTICAL STUFF CONSIDERING SMALL PITCH AND ROLL ANGLES
|
|
// update suspension force
|
|
SimSuspUpdate(&(wheel->susp));
|
|
// check suspension state
|
|
wheel->state |= wheel->susp.state;
|
|
|
|
if ( ((wheel->state & SIM_SUSP_EXT) == 0) && ((wheel->state & SIM_WH_INAIR) == 0) )
|
|
{
|
|
wheel->forces.z = axleFz + wheel->susp.force + wheel->axleFz3rd;
|
|
|
|
if (car->features & FEAT_FIXEDWHEELFORCE)
|
|
{
|
|
wheel->susp.v -= wheel->susp.spring.bellcrank * SimDeltaTime * wheel->forces.z / wheel->mass;
|
|
}
|
|
else
|
|
{
|
|
wheel->susp.v -= wheel->susp.spring.bellcrank * SimDeltaTime * wheel->susp.force / wheel->mass;
|
|
}
|
|
|
|
if (wheel->forces.z < 0.0f)
|
|
{
|
|
wheel->forces.z = 0.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wheel->state & SIM_SUSP_EXT)
|
|
{
|
|
/* calculate the force needed to reach susp->spring.xMax
|
|
* it becomes 0 from the 2. time step being extended
|
|
* works even if both SIM_SUSP_EXT and SIM_WH_INAIR is set */
|
|
wheel->forces.z = -wheel->susp.a * wheel->mass / wheel->susp.spring.bellcrank;
|
|
wheel->susp.v = 0.0f;
|
|
}
|
|
else
|
|
{ //SIM_WH_INAIR is set, but SIM_SUSP_EXT is not
|
|
wheel->forces.z = axleFz + wheel->susp.force + wheel->axleFz3rd;
|
|
|
|
if (car->features & FEAT_FIXEDWHEELFORCE)
|
|
{
|
|
wheel->susp.v -= wheel->susp.spring.bellcrank * SimDeltaTime * wheel->forces.z / wheel->mass;
|
|
}
|
|
else
|
|
{
|
|
wheel->susp.v -= wheel->susp.spring.bellcrank * SimDeltaTime * wheel->susp.force / wheel->mass;
|
|
}
|
|
|
|
wheel->forces.z = 0.0f; /* zero for zero grip and prevent getting into the air */
|
|
}
|
|
}
|
|
|
|
reaction_force = wheel->forces.z;
|
|
|
|
// update wheel coord, center relative to GC
|
|
wheel->relPos.z = - wheel->susp.x / wheel->susp.spring.bellcrank + wheel->radius;
|
|
|
|
// HORIZONTAL FORCES
|
|
waz = wheel->steer + wheel->staticPos.az;
|
|
CosA = cos(waz);
|
|
SinA = sin(waz);
|
|
|
|
// tangent velocity.
|
|
vt = wheel->bodyVel.x * CosA + wheel->bodyVel.y * SinA;
|
|
v2 = wheel->bodyVel.x * wheel->bodyVel.x + wheel->bodyVel.y * wheel->bodyVel.y;
|
|
v = sqrt(v2);
|
|
|
|
// slip angle ? from [0 = means the tire is going straight ahead (no slip)]
|
|
// to [3-6 = there is slip]
|
|
if (v < 0.000001f)
|
|
{
|
|
sa = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
sa = atan2(wheel->bodyVel.y, wheel->bodyVel.x) - waz;
|
|
}
|
|
|
|
FLOAT_NORM_PI_PI(sa);
|
|
|
|
// slip ratio = the spin velocity divided by its actual world velocity. A slip ratio of -1 means full braking lock; a ratio of 0 means the tire is spinning at the exact same rate as the road is disappearing below it. A slip ratio of 1 means it's spinning.
|
|
wrl = wheel->spinVel * wheel->radius;
|
|
|
|
if ((wheel->state & SIM_SUSP_EXT) != 0)
|
|
{
|
|
sx = sy = 0.0f;
|
|
}
|
|
else if (v < 0.000001f)
|
|
{
|
|
if (car->features & FEAT_SLOWGRIP)
|
|
{
|
|
sx = -wrl;
|
|
}
|
|
else
|
|
{
|
|
sx = wrl;
|
|
}
|
|
|
|
sy = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
if (car->features & FEAT_SLOWGRIP)
|
|
{
|
|
sx = (vt - wrl) / MAX(fabs(vt), 1.0f); //avoid divergence
|
|
sy = sin(sa);
|
|
}
|
|
else
|
|
{
|
|
sx = (vt - wrl) / fabs(vt);
|
|
sy = sin(sa);
|
|
}
|
|
}
|
|
|
|
Ft = 0.0f;
|
|
Fn = 0.0f;
|
|
s = sqrt(sx*sx+sy*sy);
|
|
|
|
{
|
|
// calculate _skid and _reaction for sound.
|
|
if (v < 2.0f)
|
|
{
|
|
car->carElt->_skid[index] = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
car->carElt->_skid[index] = MIN(1.0f, (s*reaction_force*0.0002f));
|
|
}
|
|
|
|
car->carElt->_reaction[index] = reaction_force;
|
|
}
|
|
|
|
tdble casterCamber = sin(wheel->staticPos.ay) * wheel->steer;
|
|
tdble camberDelta;
|
|
|
|
if (index % 2)
|
|
{
|
|
wheel->relPos.ax = -wheel->staticPos.ax - casterCamber;
|
|
camberDelta = -casterCamber;
|
|
}
|
|
else
|
|
{
|
|
wheel->relPos.ax = wheel->staticPos.ax - casterCamber;
|
|
camberDelta = casterCamber;
|
|
}
|
|
|
|
stmp = MIN(s, 150.0f);
|
|
|
|
// MAGIC FORMULA
|
|
Bx = wheel->mfB * stmp;
|
|
F = sin(wheel->mfC * atan(Bx * (1.0f - wheel->mfE) + wheel->mfE * atan(Bx))) * (1.0f + stmp * simSkidFactor[car->carElt->_skillLevel]);
|
|
|
|
// load sensitivity
|
|
mu = wheel->mu * (wheel->lfMin + (wheel->lfMax - wheel->lfMin) * exp(wheel->lfK * wheel->forces.z / wheel->opLoad));
|
|
|
|
//temperature and degradation
|
|
if (car->features & FEAT_TIRETEMPDEG && car->carElt->_skillLevel > 3)
|
|
{
|
|
tireCond = wheel->currentGripFactor;
|
|
mu *= tireCond;
|
|
}
|
|
else
|
|
{
|
|
tireCond = 1;
|
|
mu *= tireCond;
|
|
}
|
|
|
|
F *= wheel->forces.z * mu * wheel->trkPos.seg->surface->kFriction * (1.0f + 0.05f * sin((-wheel->staticPos.ax + camberDelta) * 18.0f)); /* coeff */
|
|
|
|
/* aligning torque for force feedback */
|
|
if ( (s > 0.000001f) && (v>1.5f) )
|
|
{
|
|
//maximal torque at wheel slip angle = TorqueRatio * wheel slip angle at maximal side force
|
|
MaxTorqueSlipAngle = wheel->AlignTqFactor * asin(car->carElt->_wheelSlipOpt(index));
|
|
wheel->torqueAlign = 0.025 * wheel->forces.z * mu * wheel->trkPos.seg->surface->kFriction * sin(2.0f * atan(sa / MaxTorqueSlipAngle));
|
|
}
|
|
else
|
|
wheel->torqueAlign = 0.0f;
|
|
|
|
// For debugging weather simultation on some tracks
|
|
#ifdef SD_DEBUG
|
|
//GfLogDebug("Simu v2.1 kFriction : %f ", wheel->trkPos.seg->surface->kFriction);
|
|
#endif
|
|
|
|
wheel->rollRes = wheel->forces.z * wheel->trkPos.seg->surface->kRollRes;
|
|
car->carElt->priv.wheel[index].rollRes = wheel->rollRes;
|
|
|
|
if (s > 0.000001f)
|
|
{
|
|
// wheel axis based
|
|
Ft -= F * sx / s;
|
|
Fn -= F * sy / s;
|
|
}
|
|
else
|
|
{
|
|
Ft -=F;
|
|
}
|
|
|
|
if ( !(car->features & FEAT_SLOWGRIP) )
|
|
{
|
|
FLOAT_RELAXATION2(Fn, wheel->preFn, 50.0f);
|
|
FLOAT_RELAXATION2(Ft, wheel->preFt, 50.0f);
|
|
}
|
|
|
|
wheel->relPos.az = waz;
|
|
|
|
wheel->forces.x = Ft * CosA - Fn * SinA;
|
|
wheel->forces.y = Ft * SinA + Fn * CosA;
|
|
wheel->spinTq = Ft * wheel->radius;
|
|
wheel->sa = sa;
|
|
wheel->sx = sx;
|
|
wheel->tireSlip = stmp;
|
|
|
|
wheel->feedBack.spinVel = wheel->spinVel;
|
|
wheel->feedBack.Tq = wheel->spinTq;
|
|
wheel->feedBack.brkTq = wheel->brake.Tq;
|
|
|
|
car->carElt->_wheelFx(index) = wheel->forces.x;
|
|
car->carElt->_wheelFy(index) = wheel->forces.y;
|
|
car->carElt->_wheelFz(index) = wheel->forces.z;
|
|
car->carElt->_wheelSlipNorm(index) = stmp;
|
|
car->carElt->_wheelSlipSide(index) = sy*v;
|
|
car->carElt->_wheelSlipAccel(index) = sx*v;
|
|
car->carElt->_reaction[index] = reaction_force;
|
|
car->carElt->_tyreEffMu(index) = mu;
|
|
|
|
//tdble Work = 0.0;
|
|
|
|
/* update tire temperature and degradation */
|
|
if (car->features & FEAT_TIRETEMPDEG)
|
|
{
|
|
SimWheelUpdateTire(car, index);
|
|
}
|
|
|
|
// Option TCL ...
|
|
if (car->features & FEAT_TCLINSIMU)
|
|
{
|
|
//tdble TCL_SlipScale = 1.00f; // Make it be a parameter later
|
|
//tdble TCL_AccelScale = 0.9f; // Make it be a parameter later
|
|
|
|
tEngine *engine = &(car->engine); // Get engine
|
|
|
|
if (sx < -car->TCL_SlipScale) // Slip is over our limit
|
|
{ // Store the TCL_Brake command for this wheel
|
|
wheel->brake.TCL = -sx;
|
|
// Store the minimum TCL_Accel command for the engine
|
|
engine->TCL = (tdble) MIN(car->TCL_AccelScale * wheel->brake.TCL,engine->TCL);
|
|
// fprintf(stderr,"sx: %.1f TCL: %.3f %%\n",sx,wheel->brake.TCL);
|
|
};
|
|
}
|
|
// ... Option TCL
|
|
|
|
// Option ABS ...
|
|
if (car->features & FEAT_ABSINSIMU)
|
|
{
|
|
//tdble ABS_SlipScale = 0.1f; // Make it be a parameter later
|
|
//tdble ABS_BrakeScale = 1.0f; // Make it be a parameter later
|
|
|
|
// If slip is over the limit, reduce brake command for this wheel
|
|
if (sx > car->ABS_SlipScale)
|
|
wheel->brake.ABS = (tdble) MAX(0.0,MIN(1.0,1 - car->ABS_BrakeScale * sx));
|
|
else
|
|
wheel->brake.ABS = 1.0f;
|
|
}
|
|
// ... Option ABS
|
|
}
|
|
|
|
void SimWheelUpdateRotation(tCar *car)
|
|
{
|
|
int i;
|
|
tWheel *wheel;
|
|
tdble deltan;
|
|
tdble cosaz, sinaz;
|
|
|
|
tdble maxslip = 0.0;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
wheel = &(car->wheel[i]);
|
|
/*calculate gyroscopic forces*/
|
|
cosaz = cos(wheel->relPos.az);
|
|
sinaz = sin(wheel->relPos.az);
|
|
|
|
if( (i == 0) || (i == 1) )
|
|
{
|
|
wheel->torques.y = wheel->torques.x * sinaz;
|
|
wheel->torques.x = wheel->torques.x * cosaz;
|
|
}
|
|
else
|
|
{
|
|
wheel->torques.x = wheel->torques.y =0.0;
|
|
}
|
|
|
|
deltan = -(wheel->in.spinVel - wheel->prespinVel) * wheel->I / SimDeltaTime;
|
|
wheel->torques.x -= deltan * wheel->cosax *sinaz;
|
|
wheel->torques.y += deltan * wheel->cosax *cosaz;
|
|
wheel->torques.z = deltan * wheel->sinax;
|
|
/*update rotation*/
|
|
wheel->spinVel = wheel->in.spinVel;
|
|
|
|
if ( (car->features & FEAT_SLOWGRIP) && (wheel->brake.Tq <= 1.0) && (car->ctrl->accelCmd * car->transmission.clutch.transferValue < 0.05) )
|
|
{
|
|
/* prevent wheelspin value oscillating around wheel tangential velocity */
|
|
tdble waz = wheel->steer + wheel->staticPos.az;
|
|
tdble vt = wheel->bodyVel.x * cos(waz) + wheel->bodyVel.y * sin(waz);
|
|
tdble wrl = wheel->spinVel * wheel->radius;
|
|
tdble oldwrl = wheel->prespinVel * wheel->radius;
|
|
if( (vt-wrl)*(vt-oldwrl) < 0.0 ) {
|
|
wheel->spinVel = vt / wheel->radius;
|
|
}
|
|
|
|
wheel->prespinVel = wheel->spinVel;
|
|
}
|
|
else
|
|
{
|
|
FLOAT_RELAXATION2(wheel->spinVel, wheel->prespinVel, 50.0f);
|
|
}
|
|
|
|
wheel->relPos.ay += wheel->spinVel * SimDeltaTime;
|
|
FLOAT_NORM_PI_PI(wheel->relPos.ay);
|
|
car->carElt->_wheelSpinVel(i) = wheel->spinVel;
|
|
|
|
// Option TCL ...
|
|
if (car->features & FEAT_TCLINSIMU)
|
|
{
|
|
if (maxslip < wheel->brake.TCL)
|
|
maxslip = wheel->brake.TCL;
|
|
}
|
|
// ... Option TCL
|
|
}
|
|
|
|
// Option TCL ...
|
|
if (maxslip > 0.0)
|
|
{
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
wheel = &(car->wheel[i]);
|
|
if (wheel->brake.TCL != maxslip)
|
|
wheel->brake.TCL = 0.0;
|
|
}
|
|
}
|
|
// ... Option TCL
|
|
}
|
|
|
|
void SimUpdateFreeWheels(tCar *car, int axlenb)
|
|
{
|
|
int i;
|
|
tWheel *wheel;
|
|
tdble BrTq; // brake torque
|
|
tdble ndot; // rotation acceleration
|
|
tdble I;
|
|
|
|
for (i = axlenb * 2; i < axlenb * 2 + 2; i++)
|
|
{
|
|
wheel = &(car->wheel[i]);
|
|
|
|
I = wheel->I + car->axle[axlenb].I / 2.0f;
|
|
|
|
ndot = SimDeltaTime * wheel->spinTq / I;
|
|
wheel->spinVel -= ndot;
|
|
|
|
BrTq = (tdble)(- SIGN(wheel->spinVel) * wheel->brake.Tq);
|
|
ndot = SimDeltaTime * BrTq / I;
|
|
|
|
if (fabs(ndot) > fabs(wheel->spinVel))
|
|
{
|
|
ndot = -wheel->spinVel;
|
|
}
|
|
|
|
wheel->spinVel += ndot;
|
|
wheel->in.spinVel = wheel->spinVel;
|
|
}
|
|
}
|
|
|
|
void SimWheelUpdateTire(tCar *car, int index)
|
|
{
|
|
if (car->carElt->info.skillLevel <= 3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
tWheel *wheel = &(car->wheel[index]);
|
|
|
|
tdble normalForce = wheel->forces.z;
|
|
tdble slip = wheel->tireSlip;
|
|
tdble skidSlip = wheel->tireSlip;
|
|
tdble lateralForce = fabs(wheel->forces.y);
|
|
tdble longForce = fabs(wheel->forces.x);
|
|
tdble absForce2 = fabs(normalForce * 0.5);
|
|
tdble slipRatio = wheel->spinVel * wheel->radius;
|
|
tdble latMod = 0;
|
|
tdble longMod = 0;
|
|
tdble minOptTemp = wheel->Topt - 20;
|
|
|
|
tdble wheelSpeed = fabs(wheel->spinVel * wheel->radius);
|
|
tdble deltaTemperature = wheel->Ttire - Tair;
|
|
|
|
// Normalize slip. Not realistic, but prevents extreme spiking when high wheelspin occurs
|
|
// when trying to recover from bumpy surfaces such as gravel traps.
|
|
if (slip >= 1)
|
|
{
|
|
slip = 1;
|
|
}
|
|
else
|
|
{
|
|
slip = slip;
|
|
}
|
|
|
|
// Calculate factor for energy which is turned into heat, according papers this seems to be pretty constant
|
|
// for a specific construction and constant slip (empiric value with model validation, called hysteresis).
|
|
// A value of 0.1 is available in papers, so for 10% slip I head for 0.1, where 0.05 come from rolling and
|
|
// the other 0.05 from slip. Additionaly the hysteresis goes down with wear.
|
|
tdble elasticity = (wheel->pressure - SimAirPressure)/(wheel->currentPressure - SimAirPressure);
|
|
tdble hysteresis = (0.05f * (sqrt(1.0f - wheel->currentWear)) * elasticity + 0.5f * slip) * wheel->hysteresisFactor;
|
|
|
|
// Calculate energy input for the tire
|
|
tdble energyGain = normalForce * wheelSpeed * SimDeltaTime * hysteresis;
|
|
|
|
// Normalize lateral and longitudinal forces if they peak too far past the operating threshold.
|
|
// This is done to (slightly) even out tire heating so huge differences in tire pressure are not necessary
|
|
// for cars with a lot of weight on one axle or the other.
|
|
if (lateralForce >= wheel->opLoad * 2)
|
|
{
|
|
lateralForce = wheel->opLoad * 2;
|
|
}
|
|
else
|
|
{
|
|
lateralForce = lateralForce;
|
|
}
|
|
|
|
if (longForce >= wheel->opLoad * 2)
|
|
{
|
|
longForce = wheel->opLoad * 2;
|
|
}
|
|
else
|
|
{
|
|
longForce = lateralForce;
|
|
}
|
|
|
|
// Modifiers for energy input from lateral and longitudinal forces.
|
|
latMod = ((lateralForce * absForce2) * wheel->latHeatFactor) * SimDeltaTime * 0.0004;
|
|
longMod = ((longForce * absForce2) * wheel->latHeatFactor) * SimDeltaTime * 0.0004;
|
|
|
|
tdble energyMod = (latMod + longMod);
|
|
|
|
tdble lockMod = 0;
|
|
|
|
//if (slipRatio < -0.9 && sqrt(car->airSpeed2) > 0)
|
|
if (slipRatio <= 1 && slipRatio > 0)
|
|
{
|
|
lockMod = (slipRatio) * (longForce) * fabs(wheelSpeed) * SimDeltaTime;
|
|
}
|
|
else if (slipRatio > 1)
|
|
{
|
|
lockMod = (1) * (longForce) * fabs(wheelSpeed) * 0.020 * SimDeltaTime;
|
|
}
|
|
|
|
// Calculate energy loss of the tire (convection, convection model approximation from papers,
|
|
// 5.9f + airspeed*3.7f [W/(meter*meter*Kelvin)]). Because the model is linear it is reasonable to apply
|
|
// it to the whole tire at once (no subdivision in elements).
|
|
tdble energyLoss = (5.9f + wheelSpeed * 3.7f) * deltaTemperature * wheel->tireConvectionSurface * SimDeltaTime * (1 + (wheel->tireSpeedCoolFactor * 1.5));
|
|
|
|
tdble deltaEnergy = (lockMod + energyMod + energyGain) - energyLoss;
|
|
|
|
// Calculate heat capacity. Basically the heat capacity of the gas in the tire accounts for a "normal" TORCS tire
|
|
// around 2 percent of the whole tire heat capacity, so one could neglect this. I put it into the model because it:
|
|
// - is more than 1 percent
|
|
// - you could think of some tire build where this ratio is significantly higher (e.g. 4 percent)
|
|
//
|
|
// Because the tire is a sufficiently rigid volume we assume for the isochoric process (constant volume, variable
|
|
// pressure and temperature).
|
|
//
|
|
// Material properties: The heat capacity of nitorgen N2 is "almost" constant over our temperature ranges:
|
|
// 29.1 (at 25°C) vs 29.3 (at 100°C) [J/(mol*Kelvin)]. So this is assumed constant, error less than 1 percent.
|
|
// But this does not apply for Rubber (NR):
|
|
// 1.982 (at 20°C) [J/(g*Kelvin)] vs 2.121 (at 100°C), so this is more than 6 percent.
|
|
tdble tireCelsius = wheel->Ttire - 273.15f;
|
|
tdble cpRubber = 2009.0f - 1.962f * tireCelsius + 3.077f * tireCelsius * tireCelsius / 100.0f;
|
|
|
|
// Calculate the actual rubber mass. This is some base mass (constant) plus the mass of the tread (dynamic,
|
|
// wears down).
|
|
tdble actualRubberMass = wheel->treadMass*(1.0f - wheel->currentWear) + wheel->baseMass;
|
|
|
|
// Calculate actual heat capacity
|
|
const tdble cvNitrogen = 1041.0f - 296.8f; // cv = cp - R, [J/(kg*Kelvin)]
|
|
tdble heatCapacity = cpRubber * actualRubberMass + cvNitrogen * wheel->tireGasMass;
|
|
|
|
// Energy transfer into the tire
|
|
wheel->Ttire += deltaEnergy / heatCapacity;
|
|
|
|
// Base cooling from lack of force
|
|
// static cooling, slowly cool tire down with low loads
|
|
wheel->Ttire -= ((wheel->coolingFactor) * (fabs(wheel->Ttire - Tair) * SimDeltaTime));
|
|
|
|
// Effect of temperature on live tire pressure
|
|
wheel->currentPressure = wheel->Ttire / Tair * wheel->pressure;
|
|
|
|
// Wear
|
|
double deltaWear = (wheel->currentPressure - SimAirPressure) * skidSlip * wheelSpeed * SimDeltaTime * normalForce
|
|
* wheel->wearFactor * 0.00000000000009;
|
|
|
|
wheel->currentWear += deltaWear;
|
|
if (wheel->currentWear > 1.0f)
|
|
wheel->currentWear = 1.0f;
|
|
|
|
// Graining
|
|
// Note that we use the TRACK temp and not the initial tire temp
|
|
// if the initial tire temp is higher (tire warmers, for example)
|
|
tdble grainTemperature = (wheel->Topt - Tair) * 3.0f / 4.0f + Tair;
|
|
tdble deltaGraining = (grainTemperature - wheel->Ttire) * deltaWear;
|
|
if (deltaGraining > 0.0f)
|
|
{
|
|
deltaGraining *= wheel->currentWear;
|
|
}
|
|
|
|
wheel->currentGraining += deltaGraining;
|
|
if (wheel->currentGraining > 1.0f)
|
|
{
|
|
wheel->currentGraining = 1.0f;
|
|
}
|
|
else if(wheel->currentGraining < 0.0f)
|
|
{
|
|
wheel->currentGraining = 0.0f;
|
|
}
|
|
|
|
// Temperature window.
|
|
tdble di;
|
|
|
|
// Ratio modifier for when temp is under minimal optimal
|
|
// operating temp (f.e. below 70 C for a 90 deg optimal)
|
|
tdble diRatio;
|
|
diRatio = (wheel->Ttire - Tair) / (Tair - minOptTemp) * 0.125;
|
|
|
|
// Racing tires typically have a roughly 10-20C window
|
|
// where they achieve most of their optimal grip.
|
|
// When within this "ideal window", grip changes are less extreme.
|
|
if (wheel->Ttire < (wheel->Topt - 20))
|
|
{
|
|
di = ((wheel->Ttire - minOptTemp) / (minOptTemp - Tair)) + diRatio;
|
|
}
|
|
else if (wheel->Ttire <= wheel->Topt)
|
|
{
|
|
di = (wheel->Ttire - wheel->Topt) / (wheel->Topt - minOptTemp) * 0.125;
|
|
}
|
|
else
|
|
{
|
|
di = (wheel->Ttire - wheel->Topt) / (wheel->Topt - Tair);
|
|
}
|
|
|
|
wheel->currentGripFactor = ((1.0f-(MIN((di*di), 1.0f)))/4.0f + 3.0f/4.0f) * (1.0f - wheel->currentGraining / 10.0f);
|
|
car->carElt->_tyreCondition(index) = wheel->currentGripFactor;
|
|
car->carElt->_tyreT_in(index) = wheel->Ttire;
|
|
car->carElt->_tyreT_mid(index) = wheel->Ttire;
|
|
car->carElt->_tyreT_out(index) = wheel->Ttire;
|
|
car->carElt->_tyreTreadDepth(index) = 1.0 - wheel->currentWear;
|
|
car->carElt->_tyreCurrentPressure(index) = wheel->currentPressure;
|
|
car->carElt->_tyreCompound(index) = wheel->tireSet;
|
|
GfLogDebug("SimuV4 wheel tyre updated Grip = %.2f - Temperature = %.3f - Graining = %.5f - Wear = %.5f - Optimal = %3.2f\n",
|
|
wheel->currentGripFactor, wheel->Ttire, wheel->currentGraining, wheel->currentWear, car->carElt->_tyreT_opt(index));
|
|
}
|