Re #139 (human car choice at race-time) Display engine specs just added in <car>.xml (capacity, cylinders nb and layout, position)

git-svn-id: https://svn.code.sf.net/p/speed-dreams/code/trunk@3278 30fe4595-0a0c-4342-8851-515496e4dcbd

Former-commit-id: 2ee6a600aacc82165c7c698440b9078c4c8736dd
Former-commit-id: 5af4b7dfc1d67806200277948ee5221ac61068e6
This commit is contained in:
pouillot 2011-01-23 13:03:14 +00:00
parent fbe5de5d93
commit 21172524c3
6 changed files with 167 additions and 88 deletions

View file

@ -29,7 +29,6 @@
<attnum name="initial fuel" unit="l" min="1.0" max="100.0" val="80.0"/>
</section>
<!-- Same engine for every one -->
<section name="Engine">
<attnum name="revs maxi" unit="rpm" min="5000" max="8000" val="8000"/>
<attnum name="revs limiter" unit="rpm" min="3000" max="10000" val="7000"/>

View file

@ -459,7 +459,7 @@ typedef struct CarElt
/* sections in xml description files */
/* Sections in XML description files */
#define SECT_SIMU_SETTINGS "Simulation Options"
#define SECT_CAR "Car"
@ -499,7 +499,7 @@ typedef struct CarElt
#define SECT_EXHAUST "Exhaust"
#define SECT_LIGHT "Light"
/* parameters names */
/* Parameter names */
#define PRM_CATEGORY "category"
#define PRM_LEN "body length"
#define PRM_WIDTH "body width"
@ -580,10 +580,14 @@ typedef struct CarElt
#define PRM_ENGBRKCOEFF "brake coefficient"
#define PRM_ENGBRKLINCOEFF "brake linear coefficient"
#define PRM_POWER "power"
#define PRM_TURBO "turbo"
#define PRM_TURBO_RPM "turbo rpm"
#define PRM_TURBO_FACTOR "turbo factor"
#define PRM_TURBO_LAG "turbo lag"
#define PRM_TURBO "turbo"
#define PRM_TURBO_RPM "turbo rpm"
#define PRM_TURBO_FACTOR "turbo factor"
#define PRM_TURBO_LAG "turbo lag"
#define PRM_CAPACITY "capacity"
#define PRM_CYLINDERS "cylinders"
#define PRM_ENGSHAPE "shape"
#define PRM_ENGPOS "position"
#define PRM_RATIO "ratio"
#define PRM_BIAS "bias"
@ -631,6 +635,16 @@ typedef struct CarElt
#define VAL_TRANS_FWD "FWD"
#define VAL_TRANS_4WD "4WD"
#define VAL_ENGSHAPE_V "v"
#define VAL_ENGSHAPE_L "l"
#define VAL_ENGSHAPE_H "h"
#define VAL_ENGSHAPE_W "w"
#define VAL_ENGPOS_FRONT "front"
#define VAL_ENGPOS_FRONTMID "front-mid"
#define VAL_ENGPOS_MID "mid"
#define VAL_ENGPOS_REARMID "rear-mid"
#define VAL_ENGPOS_REAR "rear"
/* graphic */
#define PRM_TACHO_TEX "tachometer texture"
@ -682,6 +696,7 @@ typedef struct CarElt
#define VAL_LIGHT_BRAKE2 "brake2"
#define VAL_LIGHT_REVERSE "reverse"
#define VAL_LIGHT_REAR "rear"
/* Simulation Options */
#define PRM_DAMAGE_TYRES "damage/tyres"
#define PRM_DAMAGE_SUSPENSION "damage/suspension"

View file

@ -2,8 +2,9 @@
file : carselect.cpp
created : December 2009
copyright : (C) 2009 Brian Gavin
copyright : (C) 2009 Brian Gavin, 2010 Jean-Philippe Meuret
web : speed-dreams.sourceforge.net
version : $Id$
***************************************************************************/
@ -17,12 +18,13 @@
***************************************************************************/
/* Car selection / view menu */
/* Car selection / preview menu */
#include <sys/stat.h>
#include <algorithm>
#include <string>
#include <sstream>
#include <iomanip>
#include <tgfclient.h>
#include <cars.h>
@ -209,7 +211,12 @@ void RmCarSelectMenu::resetCarModelComboBox(const std::string& strCatName,
void RmCarSelectMenu::resetCarDataSheet(const std::string& strSelCarId)
{
static const char* pszDriveWheels[] = { "Rear", "Front", "4" };
static const char* pszDriveWheels[GfCar::eNDriveTrains+1] =
{ "Rear", "Front", "4", "?" };
static const char* pszEnginePosition[GfCar::eNEnginePositions+1] =
{ "Front", "Front-mid", "Mid", "Rear-mid", "Rear", "?" };
static const char* pszEngineShape[GfCar::eNEngineShapes+1] =
{ "V", "L", "H", "W", "?" };
// Retrieve selected car.
const GfCar* pSelCar = GfCars::self()->getCar(strSelCarId);
@ -233,25 +240,39 @@ void RmCarSelectMenu::resetCarDataSheet(const std::string& strSelCarId)
ossSpecValue.str().c_str());
ossSpecValue.str("");
ossSpecValue << (long)(pSelCar->getMaxPower() / 75 / G) << " bhp ("
<< (long)(pSelCar->getMaxPowerSpeed() * 30.0 / PI) << " rpm)";
ossSpecValue << std::fixed << std::setprecision(0)
<< pSelCar->getMaxPower() / 75 / G << " bhp ("
<< pSelCar->getMaxPowerSpeed() * 30.0 / PI << " rpm)";
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("MaxPowerLabel"),
ossSpecValue.str().c_str());
ossSpecValue.str("");
ossSpecValue << (long)pSelCar->getMaxTorque() << " N.m ("
<< (long)(pSelCar->getMaxTorqueSpeed() * 30.0 / PI) << " rpm)";
ossSpecValue << pSelCar->getMaxTorque() << " N.m ("
<< pSelCar->getMaxTorqueSpeed() * 30.0 / PI << " rpm)";
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("MaxTorqueLabel"),
ossSpecValue.str().c_str());
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("Engine1Label"),
"? cyl., ???? cm3");
ossSpecValue.str("");
ossSpecValue << (pSelCar->isTurboCharged() ? "Turbo-charged" : "Naturally-aspirated");
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("Engine2Label"),
if (pSelCar->getEnginePosition() != GfCar::eNEnginePositions)
ossSpecValue << pszEnginePosition[pSelCar->getEnginePosition()] << ' ';
if (pSelCar->getEngineShape() != GfCar::eNEngineShapes)
ossSpecValue << pszEngineShape[pSelCar->getEngineShape()];
if (pSelCar->getCylinders() > 0)
{
ossSpecValue << pSelCar->getCylinders() << " ";
if (pSelCar->getEngineShape() == GfCar::eNEngineShapes)
ossSpecValue << "cyl. ";
}
if (pSelCar->getEngineCapacity() > 0)
ossSpecValue << std::setprecision(1) << pSelCar->getEngineCapacity() * 1000 << " l ";
if (pSelCar->isTurboCharged())
ossSpecValue << "turbo";
if (ossSpecValue.str().empty())
ossSpecValue << "missing information";
GfuiLabelSetText(GetMenuHandle(), GetDynamicControlId("EngineLabel"),
ossSpecValue.str().c_str());
GfuiProgressbarSetValue(GetMenuHandle(), GetDynamicControlId("TopSpeedProgress"),
pSelCar->getTopSpeed() * 3.6f);
GfuiProgressbarSetValue(GetMenuHandle(), GetDynamicControlId("PowerMassRatioProgress"),
@ -263,7 +284,8 @@ void RmCarSelectMenu::resetCarDataSheet(const std::string& strSelCarId)
GfuiProgressbarSetValue(GetMenuHandle(), GetDynamicControlId("CorneringProgress"),
pSelCar->getInvertedZAxisInertia());
GfLogDebug("%s : ts=%f, mpr=%f, lsg=%f, hsg=%f, izi=%f\n", strSelCarId.c_str(),
GfLogDebug("%s : topSp=%.1f, powMass=%.2f, lowSpGrip=%.1f, highSpGrip=%.1f, 1/ZInertia=%.5f\n",
strSelCarId.c_str(),
pSelCar->getTopSpeed()*3.6f, pSelCar->getMaxPower() / 75 / G / pSelCar->getMass(),
pSelCar->getLowSpeedGrip(), pSelCar->getHighSpeedGrip(),
pSelCar->getInvertedZAxisInertia());
@ -346,8 +368,7 @@ bool RmCarSelectMenu::Initialize()
CreateLabelControl("MaxPowerLabel");
CreateLabelControl("MaxTorqueLabel");
CreateLabelControl("MassLabel");
CreateLabelControl("Engine1Label");
CreateLabelControl("Engine2Label");
CreateLabelControl("EngineLabel");
CreateProgressbarControl("TopSpeedProgress");
CreateProgressbarControl("PowerMassRatioProgress");

View file

@ -4,6 +4,7 @@
created : July 2010
copyright : (C) 2010 Jean-Philippe Meuret
web : speed-dreams.sourceforge.net
version : $Id$
***************************************************************************/

View file

@ -252,7 +252,8 @@ GfCar::GfCar(const std::string& strId, const std::string& strCatId,
void GfCar::load(void* hparmCar)
{
static const char *pszGearName[MAX_GEARS] = {"r", "n", "1", "2", "3", "4", "5", "6", "7", "8"};
static const char *pszGearName[MAX_GEARS] =
{ "r", "n", "1", "2", "3", "4", "5", "6", "7", "8" };
// Name.
_strName = GfParmGetName(hparmCar);
@ -261,18 +262,20 @@ void GfCar::load(void* hparmCar)
_strDescFile = GfParmGetFileName(hparmCar);
// Mass and front/rear repartition.
_fMass = GfParmGetNum(hparmCar, SECT_CAR, PRM_MASS, (char*)NULL, 1500);
_fFrontRearMassRatio = GfParmGetNum(hparmCar, SECT_CAR, PRM_FRWEIGHTREP, (char*)NULL, .5);
_fMass = GfParmGetNum(hparmCar, SECT_CAR, PRM_MASS, 0, 1500);
_fFrontRearMassRatio = GfParmGetNum(hparmCar, SECT_CAR, PRM_FRWEIGHTREP, 0, .5);
// Drive train.
const std::string strDriveTrain =
GfParmGetStr(hparmCar, SECT_DRIVETRAIN, PRM_TYPE, VAL_TRANS_RWD);
GfParmGetStr(hparmCar, SECT_DRIVETRAIN, PRM_TYPE, "");
if (strDriveTrain == VAL_TRANS_RWD)
_eDriveTrain = eRWD;
else if (strDriveTrain == VAL_TRANS_FWD)
_eDriveTrain = eFWD;
else if (strDriveTrain == VAL_TRANS_4WD)
_eDriveTrain = e4WD;
else
_eDriveTrain = eNDriveTrains;
// Number of gears.
std::ostringstream ossSpecPath;
@ -280,7 +283,7 @@ void GfCar::load(void* hparmCar)
{
ossSpecPath.str("");
ossSpecPath << SECT_GEARBOX << '/' << ARR_GEARS << '/' << pszGearName[nGearInd];
if (GfParmGetNum(hparmCar, ossSpecPath.str().c_str(), PRM_RATIO, (char*)NULL, 0.0f))
if (GfParmGetNum(hparmCar, ossSpecPath.str().c_str(), PRM_RATIO, 0, 0.0f))
{
_nGears = nGearInd - 1;
break;
@ -321,38 +324,75 @@ void GfCar::load(void* hparmCar)
}
}
// "Mechanical = Low speed" grip (~mu*g, but with front/rear mass repartition).
// Engine shape.
const std::string strEngShape =
GfParmGetStr(hparmCar, SECT_ENGINE, PRM_ENGSHAPE, "");
if (strEngShape == VAL_ENGSHAPE_V)
_eEngineShape = eV;
else if (strEngShape == VAL_ENGSHAPE_H)
_eEngineShape = eH;
else if (strEngShape == VAL_ENGSHAPE_L)
_eEngineShape = eL;
else if (strEngShape == VAL_ENGSHAPE_W)
_eEngineShape = eW;
else
_eEngineShape = eNEngineShapes;
// Engine position.
const std::string strEngPos =
GfParmGetStr(hparmCar, SECT_ENGINE, PRM_ENGPOS, "");
if (strEngPos == VAL_ENGPOS_REAR)
_eEnginePosition = eRear;
else if (strEngPos == VAL_ENGPOS_REARMID)
_eEnginePosition = eRearMid;
else if (strEngPos == VAL_ENGPOS_MID)
_eEnginePosition = eMid;
else if (strEngPos == VAL_ENGPOS_FRONTMID)
_eEnginePosition = eFrontMid;
else if (strEngPos == VAL_ENGPOS_FRONT)
_eEnginePosition = eFront;
else
_eEnginePosition = eNEnginePositions;
// Engine capacity.
_fEngineCapacity =
GfParmGetNum(hparmCar, SECT_ENGINE, PRM_CAPACITY, 0, 0);
// Engine number of cylinders.
_nCylinders = (unsigned)GfParmGetNum(hparmCar, SECT_ENGINE, PRM_CYLINDERS, 0, 0);
// "Mechanical = Low speed" grip (~mu*g, but with front/rear mass repartition).
const tdble fMuFront =
(GfParmGetNum(hparmCar, SECT_FRNTRGTWHEEL, PRM_MU, (char*)NULL, 1.0)
+ GfParmGetNum(hparmCar, SECT_FRNTLFTWHEEL, PRM_MU, (char*)NULL, 1.0)) / 2.0f;
(GfParmGetNum(hparmCar, SECT_FRNTRGTWHEEL, PRM_MU, 0, 1.0)
+ GfParmGetNum(hparmCar, SECT_FRNTLFTWHEEL, PRM_MU, 0, 1.0)) / 2.0f;
const tdble fMuRear =
(GfParmGetNum(hparmCar, SECT_REARRGTWHEEL, PRM_MU, (char*)NULL, 1.0)
+ GfParmGetNum(hparmCar, SECT_REARLFTWHEEL, PRM_MU, (char*)NULL, 1.0)) / 2.0f;
(GfParmGetNum(hparmCar, SECT_REARRGTWHEEL, PRM_MU, 0, 1.0)
+ GfParmGetNum(hparmCar, SECT_REARLFTWHEEL, PRM_MU, 0, 1.0)) / 2.0f;
_fLowSpeedGrip =
(_fFrontRearMassRatio * fMuFront + (1.0f - _fFrontRearMassRatio) * fMuRear) * G;
// "Aerodynamic = High speed" grip (same + with aero down-force).
const tdble fRefCarSpeed2 = 40000 / 12.96f; //200 km/h square in m/s
const tdble fFrontWingArea =
GfParmGetNum(hparmCar, SECT_FRNTWING, PRM_WINGAREA, (char*)NULL, 0.0);
GfParmGetNum(hparmCar, SECT_FRNTWING, PRM_WINGAREA, 0, 0.0);
const tdble fRearWingArea =
GfParmGetNum(hparmCar, SECT_REARWING, PRM_WINGAREA, (char*)NULL, 0.0);
GfParmGetNum(hparmCar, SECT_REARWING, PRM_WINGAREA, 0, 0.0);
const tdble fFrontWingAngle =
GfParmGetNum(hparmCar, SECT_FRNTWING, PRM_WINGANGLE, (char*)NULL, 0.0);
GfParmGetNum(hparmCar, SECT_FRNTWING, PRM_WINGANGLE, 0, 0.0);
const tdble fRearWingAngle =
GfParmGetNum(hparmCar, SECT_REARWING, PRM_WINGANGLE, (char*)NULL, 0.0);
GfParmGetNum(hparmCar, SECT_REARWING, PRM_WINGANGLE, 0, 0.0);
const tdble fFrontClift =
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_FCL, (char*)NULL, 0.0);
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_FCL, 0, 0.0);
const tdble fRearClift =
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_RCL, (char*)NULL, 0.0);
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_RCL, 0, 0.0);
const tdble fFrontWingXpos =
GfParmGetNum(hparmCar, SECT_FRNTWING, PRM_XPOS, (char*)NULL, 0);
GfParmGetNum(hparmCar, SECT_FRNTWING, PRM_XPOS, 0, 0);
const tdble fRearWingXpos =
GfParmGetNum(hparmCar, SECT_REARWING, PRM_XPOS, (char*)NULL, 0);
GfParmGetNum(hparmCar, SECT_REARWING, PRM_XPOS, 0, 0);
const tdble fFrontAxleXpos =
GfParmGetNum(hparmCar, SECT_FRNTAXLE, PRM_XPOS, (char*)NULL, 0.0f);
GfParmGetNum(hparmCar, SECT_FRNTAXLE, PRM_XPOS, 0, 0.0f);
const tdble fRearAxleXpos =
GfParmGetNum(hparmCar, SECT_REARAXLE, PRM_XPOS, (char*)NULL, 0.0f);
GfParmGetNum(hparmCar, SECT_REARAXLE, PRM_XPOS, 0, 0.0f);
const tdble fGCXpos = _fFrontRearMassRatio * fFrontAxleXpos + (1.0 - _fFrontRearMassRatio) * fRearAxleXpos;
const tdble fTotalFrontClift = 2 * fFrontClift + 4.92 * fFrontWingArea * sin(fFrontWingAngle);
const tdble fTotalRearClift = 2 * fRearClift + 4.92 * fRearWingArea * sin(fRearWingAngle);
@ -367,32 +407,33 @@ void GfCar::load(void* hparmCar)
+ ((1.0 - _fFrontRearMassRatio) * _fMass * G + fRearAeroLoad) * fMuRear) / _fMass;
// Cornering: axle distance divided by the inertia around the Z axis.
const tdble fMassRepCoef = GfParmGetNum(hparmCar, SECT_CAR, PRM_CENTR, (char*)NULL, 1.0);
const tdble fCarLength = GfParmGetNum(hparmCar, SECT_CAR, PRM_LEN, (char*)NULL, 4.7f);
const tdble fCarWidth = GfParmGetNum(hparmCar, SECT_CAR, PRM_WIDTH, (char*)NULL, 1.9f);
const tdble fMassRepCoef = GfParmGetNum(hparmCar, SECT_CAR, PRM_CENTR, 0, 1.0);
const tdble fCarLength = GfParmGetNum(hparmCar, SECT_CAR, PRM_LEN, 0, 4.7f);
const tdble fCarWidth = GfParmGetNum(hparmCar, SECT_CAR, PRM_WIDTH, 0, 1.9f);
_fInvertedZAxisInertia = // Stolen from Simu V2.1, car.cpp, SimCarConfig()
(fFrontAxleXpos - fRearAxleXpos) * 12.0f / (_fMass * fMassRepCoef * fMassRepCoef)
/ (fCarWidth * fCarWidth + fCarLength * fCarLength);
// Theoretical top speed on a flat road, assuming the gears are tuned accordingly.
const tdble fFrontArea =
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_FRNTAREA, (char*)NULL, 2.0f);
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_FRNTAREA, 0, 2.0f);
const tdble fCx =
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_CX, (char*)NULL, 0.35f);
GfParmGetNum(hparmCar, SECT_AERODYNAMICS, PRM_CX, 0, 0.35f);
const tdble muRollRes = 0.015f; // Average track.
ossSpecPath.str("");
ossSpecPath << SECT_GEARBOX << '/' << ARR_GEARS << '/' << pszGearName[_nGears];
const tdble fTopGearEff = GfParmGetNum(hparmCar, ossSpecPath.str().c_str(), PRM_EFFICIENCY, (char*)NULL, 1.0f);
const tdble fTopGearEff =
GfParmGetNum(hparmCar, ossSpecPath.str().c_str(), PRM_EFFICIENCY, 0, 1.0f);
// TODO: RWD, FWD, 4WD differential efficiency
const tdble fDiffEff = 0.95f; // now use an average number
const tdble fDiffEff = 0.95f; // For now, use an average number
const tdble eff = fTopGearEff * fDiffEff; // gear * differential efficiencies
const tdble Cd =
0.645f * fCx * fFrontArea
+ 1.23f * (fFrontWingArea * sin(fFrontWingAngle) + fRearWingArea * sin(fRearWingAngle));
const double pp =
muRollRes * _fMass * G / (Cd + muRollRes * (tdble)(fTotalFrontClift + fTotalRearClift));
const double q = eff * _fMaxPower / (Cd + muRollRes * (tdble)(fTotalFrontClift + fTotalRearClift));
// Work in progress.
const double q =
eff * _fMaxPower / (Cd + muRollRes * (tdble)(fTotalFrontClift + fTotalRearClift));
_fTopSpeed =
(tdble)pow(q/2+sqrt(q*q/4+pp*pp*pp/27), 1.0/3)
- (tdble)pow(-q/2+sqrt(q*q/4+pp*pp*pp/27), 1.0/3);
@ -438,6 +479,26 @@ bool GfCar::isTurboCharged() const
return _bTurboCharged;
}
unsigned GfCar::getCylinders() const
{
return _nCylinders;
}
tdble GfCar::getEngineCapacity() const
{
return _fEngineCapacity;
}
GfCar::EEngineShape GfCar::getEngineShape() const
{
return _eEngineShape;
}
GfCar::EEnginePosition GfCar::getEnginePosition() const
{
return _eEnginePosition;
}
tdble GfCar::getMaxPower() const
{
return _fMaxPower;
@ -487,28 +548,3 @@ tdble GfCar::getInvertedZAxisInertia() const
{
return _fInvertedZAxisInertia;
}
// void GfCar::setId(const std::string& strId)
// {
// _strId = strId;
// }
// void GfCar::setName(const std::string& strName)
// {
// _strName = strName;
// }
// void GfCar::setCategoryId(const std::string& strCatId)
// {
// _strCatId = strCatId;
// }
// void GfCar::setCategoryName(const std::string& strCatName)
// {
// _strCatName = strCatName;
// }
// void GfCar::setDescriptorFileName(const std::string& strDescFile)
// {
// _strDescFile = strDescFile;
// }

View file

@ -46,10 +46,23 @@ public:
const std::string& getCategoryName() const;
const std::string& getDescriptorFileName() const;
enum EDriveTrain { eRWD, eFWD, e4WD };
enum EDriveTrain { eRWD, eFWD, e4WD, eNDriveTrains };
EDriveTrain getDriveTrain() const;
unsigned getGearsCount() const;
bool isTurboCharged() const;
unsigned getCylinders() const;
tdble getEngineCapacity() const;
enum EEngineShape { eV, eL, eH, eW, eNEngineShapes };
EEngineShape getEngineShape() const;
enum EEnginePosition { eFront, eFrontMid, eMid, eRearMid, eRear, eNEnginePositions };
EEnginePosition getEnginePosition() const;
tdble getMaxPower() const;
tdble getMaxPowerSpeed() const;
tdble getMaxTorque() const;
@ -62,12 +75,6 @@ public:
tdble getHighSpeedGrip() const;
tdble getInvertedZAxisInertia() const;
// void setId(const std::string& strId);
// void setName(const std::string& strName);
// void setCategoryId(const std::string& strCatId);
// void setCategoryName(const std::string& strCatName);
// void setDescriptorFileName(const std::string& strDescFile);
void load(void* hparmCar);
protected:
@ -81,10 +88,10 @@ protected:
EDriveTrain _eDriveTrain;
unsigned _nGears; // Number of gears.
bool _bTurboCharged; // TODO: Move to an enum (Turbo, Compressor, ...)
// unsigned _nCylinders; // TODO
// enum { eV, eW, eFlat, eStraight, eTurbine } EEngineShape;
// EEngineShape _eEngineShape; // TODO
// tdble fEngineCapacity; // TODO
tdble _fEngineCapacity; // m3
unsigned _nCylinders;
EEngineShape _eEngineShape;
EEnginePosition _eEnginePosition;
tdble _fMaxPower, _fMaxPowerSpeed; // Engine max power (SI) and associated engine speed.
tdble _fMaxTorque, _fMaxTorqueSpeed; // Engine max torque (Nm) and associated engine speed.
tdble _fMass; // Total mass (kg).