LUADOC - Farming Simulator 22

Script v1_7_1_0

Engine v1_7_1_0

Foundation Reference

VehicleMotor

Description
Class for vehicle motors
Functions

applyTargetGear

Description
Apply target gear
Definition
applyTargetGear()
Code
1695function VehicleMotor:applyTargetGear()
1696 local gearRatioMultiplier = self:getGearRatioMultiplier()
1697 self.gear = self.targetGear
1698
1699 if self.gearShiftMode ~= VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then
1700 if self.currentGears[self.gear] ~= nil then
1701 self.minGearRatio = self.currentGears[self.gear].ratio * gearRatioMultiplier
1702 self.maxGearRatio = self.minGearRatio
1703 else
1704 self.minGearRatio = 0
1705 self.maxGearRatio = 0
1706 end
1707
1708 self.startDebug = 0
1709 end
1710
1711 self.gearChangeTime = self.gearChangeTimeOrig
1712
1713 local directionMultiplier = self.directionChangeUseGear and self.currentDirection or 1
1714 SpecializationUtil.raiseEvent(self.vehicle, "onGearChanged", self.gear * directionMultiplier, self.targetGear * directionMultiplier, 0)
1715end

calculatePhysicalMaximumBackwardSpeed

Description
Returns physical maximum backward speed
Definition
calculatePhysicalMaximumBackwardSpeed()
Return Values
floatphysicalMaxBackwardSpeedphysical maximum backward speed
Code
902function VehicleMotor:calculatePhysicalMaximumBackwardSpeed()
903 return VehicleMotor.calculatePhysicalMaximumSpeed(self.minBackwardGearRatio, self.backwardGears or self.forwardGears, self.maxRpm)
904end

calculatePhysicalMaximumForwardSpeed

Description
Returns physical maximum forward speed
Definition
calculatePhysicalMaximumForwardSpeed()
Return Values
floatphysicalMaxForwardSpeedphysical maximum forward speed
Code
895function VehicleMotor:calculatePhysicalMaximumForwardSpeed()
896 return VehicleMotor.calculatePhysicalMaximumSpeed(self.minForwardGearRatio, self.forwardGears, self.maxRpm)
897end

calculatePhysicalMaximumSpeed

Description
Returns physical maximum speed
Definition
calculatePhysicalMaximumSpeed(float minGearRatio, table gears, Integer maxRpm)
Arguments
floatminGearRatiomin gear ratio
tablegearsgears
IntegermaxRpmmax rpm
Return Values
floatphysicalMaxSpeedphysical maximum speed
Code
912function VehicleMotor.calculatePhysicalMaximumSpeed(minGearRatio, gears, maxRpm)
913 local minRatio
914 if minGearRatio ~= nil then
915 minRatio = minGearRatio
916 elseif gears ~= nil then
917 minRatio = math.huge
918 for _, gear in pairs(gears) do
919 minRatio = math.min(minRatio, gear.ratio)
920 end
921 else
922 printCallstack()
923 return 0
924 end
925
926 return maxRpm * math.pi / (30 * minRatio)
927end

changeDirection

Description
Definition
changeDirection()
Code
2144function VehicleMotor:changeDirection(direction, force)
2145 local targetDirection
2146 if direction == nil then
2147 targetDirection = -self.currentDirection
2148 else
2149 targetDirection = direction
2150 end
2151
2152 if self.backwardGears == nil and self.forwardGears == nil then
2153 self.currentDirection = targetDirection
2154 SpecializationUtil.raiseEvent(self.vehicle, "onGearDirectionChanged", self.currentDirection)
2155 return
2156 end
2157
2158 local changeAllowed = (self.directionChangeUseGroup and not self.gearGroupChangedIsLocked)
2159 or (self.directionChangeUseGear and not self.gearChangedIsLocked)
2160 or (not self.directionChangeUseGear and not self.directionChangeUseGroup)
2161 if changeAllowed then
2162 if targetDirection ~= self.currentDirection or force then
2163 self.currentDirection = targetDirection
2164
2165 if self.directionChangeTime > 0 then
2166 self.directionChangeTimer = self.directionChangeTime
2167 self.gear = 0
2168 self.minGearRatio = 0
2169 self.maxGearRatio = 0
2170 end
2171
2172 local oldGearGroupIndex = self.activeGearGroupIndex
2173 if self.currentDirection < 0 then
2174 if self.directionChangeUseGear then
2175 self.directionLastGear = self.targetGear
2176 if not self:getUseAutomaticGearShifting() or not self.lastManualShifterActive then
2177 self.targetGear = self.directionChangeGearIndex
2178 end
2179
2180 self.currentGears = self.backwardGears or self.forwardGears
2181 elseif self.directionChangeUseGroup then
2182 self.directionLastGroup = self.activeGearGroupIndex
2183 self.activeGearGroupIndex = self.directionChangeGroupIndex
2184 end
2185 else
2186 if self.directionChangeUseGear then
2187 if not self:getUseAutomaticGearShifting() or not self.lastManualShifterActive then
2188 if self.directionLastGear > 0 then
2189 self.targetGear = not self:getUseAutomaticGearShifting() and self.directionLastGear or self.defaultForwardGear
2190 else
2191 self.targetGear = self.defaultForwardGear
2192 end
2193 end
2194
2195 self.currentGears = self.forwardGears
2196 elseif self.directionChangeUseGroup then
2197 if self.directionLastGroup > 0 then
2198 self.activeGearGroupIndex = self.directionLastGroup
2199 else
2200 self.activeGearGroupIndex = self.defaultGearGroup
2201 end
2202 end
2203 end
2204
2205 SpecializationUtil.raiseEvent(self.vehicle, "onGearDirectionChanged", self.currentDirection)
2206
2207 local directionMultiplier = self.directionChangeUseGear and self.currentDirection or 1
2208 SpecializationUtil.raiseEvent(self.vehicle, "onGearChanged", self.gear * directionMultiplier, self.targetGear * directionMultiplier, self.directionChangeTime)
2209
2210 if self.activeGearGroupIndex ~= oldGearGroupIndex then
2211 SpecializationUtil.raiseEvent(self.vehicle, "onGearGroupChanged", self.activeGearGroupIndex, self.directionChangeTime)
2212 end
2213
2214 if self.directionChangeTime == 0 then
2215 self:applyTargetGear()
2216 end
2217 end
2218 end
2219end

delete

Description
Definition
delete()
Code
322function VehicleMotor:delete()
323 g_messageCenter:unsubscribeAll(self)
324end

findGearChangeTargetGearPrediction

Description
Definition
findGearChangeTargetGearPrediction()
Code
1436function VehicleMotor:findGearChangeTargetGearPrediction(curGear, gears, gearSign, gearChangeTimer, acceleratorPedal, dt)
1437 local newGear = curGear
1438 local gearRatioMultiplier = self:getGearRatioMultiplier()
1439
1440 local minAllowedRpm, maxAllowedRpm = self.minRpm, self.maxRpm
1441 --print(string.format("rpmRange [%.2f %.2f]", minAllowedRpm, maxAllowedRpm))
1442 local gearRatio = math.abs(gears[curGear].ratio * gearRatioMultiplier)
1443
1444 local differentialRotSpeed = math.max(self.differentialRotSpeed*gearSign, 0.0001)
1445 local differentialRpm = differentialRotSpeed * 30 / math.pi
1446 local clutchRpm = differentialRpm * gearRatio
1447 --log("differentialRpm", differentialRpm, "gearRatio", gearRatio, "clutchRpm", clutchRpm, "gearSign", gearSign, "self.differentialRotSpeed", self.differentialRotSpeed)
1448
1449
1450 -- 1. Predict the velocity of the vehicle after the gear change
1451 local diffSpeedAfterChange
1452 if math.abs(acceleratorPedal) < 0.0001 then
1453 -- Assume that we will continue decelerating with 80% of the current deceleration
1454 local brakeAcc = math.min(self.differentialRotAccelerationSmoothed*gearSign*0.8, 0)
1455 diffSpeedAfterChange = math.max(differentialRotSpeed + brakeAcc * self.gearChangeTime*0.001, 0)
1456 --print(string.format("brake expectedAcc: %.3f realAcc %.3f %.3f max: %.2f gr: %.2f speed: %.2f", brakeAcc, self.vehicle.lastSpeedAcceleration*1000*1000, self.differentialRotAccelerationSmoothed, maxExpectedAcc, gearRatio, self.vehicle.lastSpeedReal*1000))
1457 else
1458 -- Ignore wheels mass as it is usually negligible and the calculation below is not correct when the differential acceleration is not uniformely distributed
1459 --[[local neededWheelsInertiaTorque = 0
1460 local specWheels = self.vehicle.spec_wheels
1461 for _, wheel in pairs(specWheels.wheels) do
1462 local invRotInterita = 2.0 / (wheel.mass*wheel.radius * wheel.radius)
1463 neededWheelsInertiaTorque = neededWheelsInertiaTorque + invRotInterita * self.differentialRotAcceleration * wheel.radius
1464 end
1465 neededWheelsInertiaTorque = neededWheelsInertiaTorque / (gearRatio*gearRatio)]]
1466
1467 local lastMotorRotSpeed = self.motorRotSpeed - self.motorRotAcceleration * (g_physicsDtLastValidNonInterpolated*0.001)
1468 local lastDampedMotorRotSpeed = lastMotorRotSpeed / (1.0 + self.dampingRateFullThrottle/self.rotInertia*g_physicsDtLastValidNonInterpolated*0.001)
1469
1470
1471 local neededInertiaTorque = (self.motorRotSpeed - lastDampedMotorRotSpeed)/(g_physicsDtLastValidNonInterpolated*0.001) * self.rotInertia
1472
1473 local lastMotorTorque = (self.motorAppliedTorque - self.motorExternalTorque - neededInertiaTorque)
1474
1475 --print(string.format("load: %.3f expected torque: %.3f neededPtoTorque %.3f neededInertiaTorque %.4f", self.motorAppliedTorque, self.motorAvailableTorque, self.motorExternalTorque, neededInertiaTorque))
1476
1477 local totalMass = self.vehicle:getTotalMass()
1478 local expectedAcc = lastMotorTorque * gearRatio / totalMass -- differential rad/s^2
1479
1480 -- The the difference in acceleration is due to gravity and thus will pull back the vehicle when changing gears and some other reasons (non-accounted mass (e.g. trees), collisions, wheel damping, wheel mass, ...)
1481 -- Use a fixed factor of 90% to separate the effect of the gravity
1482 local uncalculatedAccFactor = 0.9
1483 local gravityAcc = math.max(expectedAcc*uncalculatedAccFactor - math.max(self.differentialRotAccelerationSmoothed*gearSign, 0), 0)
1484
1485 --print(string.format("expectedAcc: %.3f realAcc: %.3f %.3f gravityAcc: %.3f gr: %.2f mass %.1f speed: %.3f dt %.2fms", expectedAcc, self.vehicle.lastSpeedAcceleration*1000*1000, self.differentialRotAcceleration, gravityAcc, gearRatio, totalMass, self.vehicle.lastSpeedReal*1000, g_physicsDtLastValidNonInterpolated))
1486
1487 diffSpeedAfterChange = math.max(differentialRotSpeed - gravityAcc * self.gearChangeTime*0.001, 0)
1488
1489 --log("differentialRotSpeed", differentialRotSpeed, "gravityAcc", gravityAcc, "expectedAcc", expectedAcc, "self.differentialRotAccelerationSmoothed", self.differentialRotAccelerationSmoothed, "gearRatio", gearRatio, "lastMotorTorque", lastMotorTorque, "neededInertiaTorque", neededInertiaTorque, "lastDampedMotorRotSpeed", lastDampedMotorRotSpeed, "lastMotorRotSpeed", lastMotorRotSpeed)
1490 end
1491
1492
1493 -- 2. Find the gear that gives the maximum power in the valid rpm range after the gear change
1494 -- If none is valid, store the gear that will get closest to the valid rpm range
1495
1496 -- TODO allow some clutch slippage to extend the possible rpm range (e.g. when accelerating and switching from gear 1 to gear 2)
1497
1498 local maxPower = 0
1499 local maxPowerGear = 0
1500 for gear=1, #gears do
1501 local rpm
1502 if gear == curGear then
1503 rpm = clutchRpm
1504 else
1505 rpm = diffSpeedAfterChange * math.abs(gears[gear].ratio * gearRatioMultiplier) * 30 / math.pi
1506 end
1507
1508 -- if we could start in this gear we allow changes, no matter of rpm and power
1509 local startInGearFactor = self:getStartInGearFactor(gears[gear].ratio * gearRatioMultiplier)
1510 local minRpmFactor = 1
1511 if startInGearFactor < self.startGearThreshold then
1512 minRpmFactor = 0
1513 end
1514
1515 -- current gear is always allowed since clutchRpm could be slightly highe ror lower then the limits due to float 32
1516 if (rpm <= maxAllowedRpm and rpm >= minAllowedRpm * minRpmFactor) or gear == curGear then
1517 local power = self:getTorqueCurveValue(rpm) * rpm
1518 --print(string.format(" power %.2f @ %.d %d", power, gear, rpm))
1519 if power >= maxPower then
1520 maxPower = power
1521 maxPowerGear = gear
1522 end
1523 end
1524 end
1525
1526 --local curPower = self:getTorqueCurveValue(clutchRpm) * clutchRpm
1527 --print(string.format("power %.2f @ %d rpms: %.2f %.2f diffSpeedAfterChange: %.10f drpm: %.2f", curPower, curGear, clutchRpm, diffSpeedAfterChange * gearRatio * 30 / math.pi, diffSpeedAfterChange, self.differentialRotAccelerationSmoothed * gearRatio * 30 / math.pi))
1528
1529 local neededPowerPct = 0.8
1530
1531 -- 3. Find the gear with the best tradeoff (lots of power with low rpm)
1532 -- Or use the the gear will get closest to the valid rpm range if none of the gears are good
1533 if maxPowerGear ~= 0 then
1534 local bestTradeoff = 0
1535
1536 for gear=#gears,1,-1 do
1537 local validGear = false
1538 local nextRpm
1539 if gear == curGear then
1540 nextRpm = clutchRpm
1541 else
1542 nextRpm = diffSpeedAfterChange * math.abs(gears[gear].ratio * gearRatioMultiplier) * 30 / math.pi
1543 end
1544
1545 -- if we could start in this gear we allow changes, no matter of rpm and power
1546 local startInGearFactor = self:getStartInGearFactor(gears[gear].ratio * gearRatioMultiplier)
1547 local minRpmFactor = 1
1548 local neededPowerPctGear = neededPowerPct
1549 if startInGearFactor < self.startGearThreshold then
1550 neededPowerPctGear = 0
1551 minRpmFactor = 0
1552 end
1553
1554 if nextRpm <= maxAllowedRpm and nextRpm >= minAllowedRpm * minRpmFactor or gear == curGear then
1555 local nextPower = self:getTorqueCurveValue(nextRpm) * nextRpm
1556
1557 -- Choose the gear if it gets close enough to the max power
1558 if nextPower >= maxPower*neededPowerPctGear or gear == curGear then
1559 local powerFactor = (nextPower - maxPower*neededPowerPctGear) / (maxPower*(1-neededPowerPctGear)) -- 0 when at 80% of maxPower, 1 when at maxPower
1560 local curSpeedRpm = differentialRpm * math.abs(gears[gear].ratio * gearRatioMultiplier)
1561 local rpmFactor = MathUtil.clamp((maxAllowedRpm - curSpeedRpm) / math.max(maxAllowedRpm-minAllowedRpm, 0.001), 0, 2)
1562 if rpmFactor > 1 then
1563 rpmFactor = 1 - (rpmFactor - 1) * 4
1564 end
1565
1566 local gearChangeFactor
1567 if gear == curGear then
1568 gearChangeFactor = 1
1569 else
1570 gearChangeFactor = math.min(-gearChangeTimer / 2000, 0.9) -- the longer we wait, the less penality we add for gear changes
1571 end
1572
1573 local rpmPreferenceFactor = 0
1574 -- when shifting down the lower gear should have a higher rpm, otherwise we penalize it with -1
1575 if gear < curGear then
1576 rpmPreferenceFactor = MathUtil.clamp((nextRpm - clutchRpm) / 250, -1, 0)
1577 end
1578
1579 -- when starting with a preselected/higher gear we force to use it as long as the factor is still valid
1580 if gear < self.bestGearSelected then
1581 local factor = self:getStartInGearFactor(gearRatio)
1582 if factor < self.startGearThreshold then
1583 gearChangeFactor = gearChangeFactor - 3
1584 end
1585 end
1586
1587 -- prefer middle rpm range instead of upper and lower 20% of range
1588 rpmPreferenceFactor = rpmPreferenceFactor - (1-math.min(math.sin(rpmFactor * math.pi)*5, 2)) * 0.7
1589
1590 -- if multiple gears are able to stay in the prefered rpm range, we always prefer the gear we are in until it's getting out of the range
1591 -- this prevents to much shifting when we have a lot of gears with small ratio steps
1592 -- only apply if rpmPreferenceFactor is postive so we do not negative influence the current gear
1593 if gear == curGear and rpmPreferenceFactor > 0 then
1594 rpmPreferenceFactor = rpmPreferenceFactor * 1.5
1595 end
1596
1597 if math.abs(acceleratorPedal) < 0.0001 then
1598 rpmFactor = 1-rpmFactor -- choose a high rpm when decelerating
1599 else
1600 rpmFactor = rpmFactor * 2
1601 end
1602
1603 -- when just rolling allow downshifting to use motor brake when below 25% of rpm range
1604 -- so we avoid hitting always the highest rpm on the lower gear (would be better for motor break, but sounds stupid)
1605 if math.abs(acceleratorPedal) < 0.0001 then
1606 if (clutchRpm - minRpmFactor) / (maxAllowedRpm - minRpmFactor) > 0.25 then
1607 if gear < curGear then
1608 powerFactor = 0
1609 rpmFactor = 0
1610 elseif gear == curGear then
1611 powerFactor = 1
1612 rpmFactor = 1
1613 end
1614 end
1615 end
1616
1617 -- if we could start in the gear we don't care about the power and rpm preference
1618 -- only apply to higher gears, so we won't accidentally rate lower gears higher than current gear if current gear is in higher rpms
1619 if gear > curGear then
1620 if startInGearFactor < self.startGearThreshold then
1621 powerFactor = 1
1622 rpmPreferenceFactor = 1
1623 end
1624 end
1625
1626 local tradeoff = powerFactor + rpmFactor + gearChangeFactor + rpmPreferenceFactor
1627
1628 if tradeoff >= bestTradeoff then
1629 bestTradeoff = tradeoff
1630 newGear = gear
1631 -- print(string.format("better tradeoff %.2f with %d power: %.2f vs %.2f @ %d rpm %.2f/%.2f vs %.2f factors: %.2f %.2f %.2f %.2f", tradeoff, gear, nextPower, maxPower, maxPowerGear, nextRpm, curSpeedRpm, clutchRpm, powerFactor, rpmFactor, gearChangeFactor, rpmPreferenceFactor))
1632 --else
1633 -- print(string.format("worse tradeoff %.2f with %d power: %.2f vs %.2f @ %d rpm %.2f/%.2f vs %.2f factors: %.2f %.2f %.2f %.2f", tradeoff, gear, nextPower, maxPower, maxPowerGear, nextRpm, curSpeedRpm, clutchRpm, powerFactor, rpmFactor, gearChangeFactor, rpmPreferenceFactor))
1634 end
1635
1636 if VehicleDebug.state == VehicleDebug.DEBUG_TRANSMISSION then
1637 gears[gear].lastTradeoff = tradeoff
1638 gears[gear].lastDiffSpeedAfterChange = gear == curGear and diffSpeedAfterChange or nil
1639 gears[gear].lastPowerFactor = powerFactor
1640 gears[gear].lastRpmFactor = rpmFactor
1641 gears[gear].lastGearChangeFactor = gearChangeFactor
1642 gears[gear].lastRpmPreferenceFactor = rpmPreferenceFactor
1643 gears[gear].lastNextPower = nextPower
1644 gears[gear].nextPowerValid = true
1645 gears[gear].lastNextRpm = nextRpm
1646 gears[gear].nextRpmValid = true
1647 gears[gear].lastMaxPower = maxPower
1648 gears[gear].lastHasPower = true
1649 end
1650
1651 validGear = true
1652 else
1653 if VehicleDebug.state == VehicleDebug.DEBUG_TRANSMISSION then
1654 gears[gear].lastNextPower = nextPower
1655 end
1656 end
1657 end
1658
1659 if not validGear then
1660 if VehicleDebug.state == VehicleDebug.DEBUG_TRANSMISSION then
1661 gears[gear].lastTradeoff = 0
1662 gears[gear].lastPowerFactor = 0
1663 gears[gear].lastRpmFactor = 0
1664 gears[gear].lastGearChangeFactor = 0
1665 gears[gear].lastRpmPreferenceFactor = 0
1666 gears[gear].lastDiffSpeedAfterChange = gear == curGear and diffSpeedAfterChange or nil
1667 gears[gear].lastNextRpm = nextRpm
1668 gears[gear].nextRpmValid = nextRpm <= maxAllowedRpm and nextRpm >= minAllowedRpm * minRpmFactor
1669 gears[gear].nextPowerValid = false
1670 gears[gear].lastMaxPower = maxPower
1671 gears[gear].lastHasPower = false
1672 end
1673 end
1674 end
1675 else
1676 local minDiffGear = 0
1677 local minDiff = math.huge
1678 for gear=1,#gears do
1679 local rpm = diffSpeedAfterChange * math.abs(gears[gear].ratio * gearRatioMultiplier) * 30 / math.pi
1680 local diff = math.max(rpm - maxAllowedRpm, minAllowedRpm - rpm)
1681 if diff < minDiff then
1682 --print(string.format("better min diff gear: %d diff: %.2f rpm: %.2f" , gear, diff, rpm))
1683 minDiff = diff
1684 minDiffGear = gear
1685 end
1686 end
1687 newGear = minDiffGear
1688 end
1689
1690 return newGear
1691end

getAccelerationLimit

Description
Definition
getAccelerationLimit()
Code
2404function VehicleMotor:getAccelerationLimit()
2405 return self.accelerationLimit
2406end

getBestGear

Description
Returns best gear
Definition
getBestGear(float acceleration, float wheelSpeedRpm, float accSafeMotorRpm, float requiredMotorPower, float requiredMotorRpm)
Arguments
floataccelerationacceleration
floatwheelSpeedRpmwheel speed rpm
floataccSafeMotorRpmacc save motor rpm
floatrequiredMotorPowerrequired wheel torque
floatrequiredMotorRpmrequired motor rpm
Return Values
floatbestGearbest gear
floatgearRatiogear ratio
Code
1404function VehicleMotor:getBestGear(acceleration, wheelSpeedRpm, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm)
1405 if math.abs(acceleration) < 0.001 then
1406 acceleration = 1
1407 if wheelSpeedRpm < 0 then
1408 acceleration = -1
1409 end
1410 end
1411 if acceleration > 0 then
1412 if self.minForwardGearRatio ~= nil then
1413 wheelSpeedRpm = math.max(wheelSpeedRpm, 0)
1414 local bestGearRatio = self:getBestGearRatio(wheelSpeedRpm, self.minForwardGearRatio, self.maxForwardGearRatio, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm)
1415 return 1, bestGearRatio
1416 else
1417 return 1, self.forwardGears[1].ratio
1418 end
1419 else
1420 if self.minBackwardGearRatio ~= nil then
1421 wheelSpeedRpm = math.max(-wheelSpeedRpm, 0)
1422 local bestGearRatio = self:getBestGearRatio(wheelSpeedRpm, self.minBackwardGearRatio, self.maxBackwardGearRatio, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm)
1423 return -1, -bestGearRatio
1424 else
1425 if self.backwardGears ~= nil then
1426 return -1, -self.backwardGears[1].ratio
1427 else
1428 return 1, self.forwardGears[1].ratio
1429 end
1430 end
1431 end
1432end

getBestGearRatio

Description
Returns best gear ratio
Definition
getBestGearRatio(float wheelSpeedRpm, float minRatio, float maxRatio, float accSafeMotorRpm, float requiredMotorPower, float requiredMotorRpm)
Arguments
floatwheelSpeedRpmwheel speed rpm
floatminRatiomin ratio
floatmaxRatiomax ratio
floataccSafeMotorRpmacc save motor rpm
floatrequiredMotorPowerthe required motor power [kW] (can be bigger than what the motor can actually achieve)
floatrequiredMotorRpmfixed motor rpm to be used (if not 0)
Return Values
floatbestGearRatiobest gear ratio
Code
1359function VehicleMotor:getBestGearRatio(wheelSpeedRpm, minRatio, maxRatio, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm)
1360
1361 if requiredMotorRpm ~= 0 then
1362 local gearRatio = math.max(requiredMotorRpm-accSafeMotorRpm, requiredMotorRpm*0.8) / math.max(wheelSpeedRpm, 0.001)
1363 gearRatio = MathUtil.clamp(gearRatio, minRatio, maxRatio)
1364 return gearRatio
1365 end
1366
1367 -- Use a minimum wheel rpm to avoid that gearRatio is ignored
1368 wheelSpeedRpm = math.max(wheelSpeedRpm, 0.0001)
1369
1370 local bestMotorPower = 0
1371 local bestGearRatio = minRatio
1372 --local bestRPM = 0
1373 -- TODO make this more efficient
1374 for gearRatio = minRatio, maxRatio, 0.5 do
1375 local motorRpm = wheelSpeedRpm * gearRatio
1376 if motorRpm > self.maxRpm - accSafeMotorRpm then
1377 break
1378 end
1379 local motorPower = self:getTorqueCurveValue(math.max(motorRpm, self.minRpm)) * motorRpm *math.pi/30
1380 if motorPower > bestMotorPower then
1381 bestMotorPower = motorPower
1382 bestGearRatio = gearRatio
1383 --bestRPM = motorRpm
1384 end
1385
1386 if motorPower >= requiredMotorPower then
1387 break
1388 end
1389 end
1390 --print(string.format("Selected best gear: %f, %.2fkW rpm %.2f wheel %.2f", bestGearRatio, bestMotorPower, bestRPM, wheelSpeedRpm,))
1391
1392 return bestGearRatio
1393end

getBestStartGear

Description
Returns the highest gear possible to start
Definition
getBestStartGear(table gears)
Arguments
tablegearsgears
Return Values
integerbestGearbest gear to start
Code
1255function VehicleMotor:getBestStartGear(gears)
1256 local directionMultiplier = self.directionChangeUseGroup and 1 or self.currentDirection
1257
1258 local minFactor = math.huge
1259 local minFactorGear, minFactorGroup = 1, 1
1260
1261 local maxFactor = 0
1262 local maxFactorGear, maxFactorGroup = 1, 1 -- use min gear in min group as default return value
1263 if self.gearGroups ~= nil then
1264 if self:getUseAutomaticGroupShifting() then
1265 for j=1, #self.gearGroups do
1266 local groupRatio = self.gearGroups[j].ratio * directionMultiplier
1267 if MathUtil.sign(groupRatio) == self.currentDirection or not self.directionChangeUseGroup then
1268 for i=1, #gears do
1269 local factor = self:getStartInGearFactor(gears[i].ratio * groupRatio)
1270 if factor < self.startGearThreshold then
1271 if factor > maxFactor then
1272 maxFactor = factor
1273 maxFactorGear = i
1274 maxFactorGroup = j
1275 end
1276 end
1277
1278 if factor < minFactor then
1279 minFactor = factor
1280 minFactorGear = i
1281 minFactorGroup = j
1282 end
1283 end
1284 end
1285 end
1286 end
1287 else
1288 local gearRatioMultiplier = self:getGearRatioMultiplier()
1289 for i=1, #gears do
1290 local factor = self:getStartInGearFactor(gears[i].ratio * gearRatioMultiplier)
1291 if factor < self.startGearThreshold then
1292 if factor > maxFactor then
1293 maxFactor = factor
1294 maxFactorGear = i
1295 end
1296 end
1297
1298 if factor < minFactor then
1299 minFactor = factor
1300 minFactorGear = i
1301 end
1302 end
1303 end
1304
1305 -- return the gear with the lowest factor if we don't find any gear below self.startGearThreshold
1306 if maxFactor == 0 then
1307 return minFactorGear, minFactorGroup
1308 end
1309
1310 return maxFactorGear, maxFactorGroup
1311end

getBrakeForce

Description
Returns brake force
Definition
getBrakeForce()
Return Values
floatbrakeForcebrake force
Code
476function VehicleMotor:getBrakeForce()
477 return self.brakeForce
478end

getCanMotorRun

Description
Definition
getCanMotorRun()
Code
2342function VehicleMotor:getCanMotorRun()
2343 if self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then
2344 if not self.vehicle:getIsMotorStarted() then
2345 if self.backwardGears or self.forwardGears then
2346 if self.manualClutchValue == 0 and self.maxGearRatio ~= 0 then
2347 local factor = (self:getClutchRotSpeed() * 30 / math.pi + 50) / self:getNonClampedMotorRpm()
2348 if factor < 0.2 then
2349 return false, VehicleMotor.REASON_CLUTCH_NOT_ENGAGED
2350 end
2351 end
2352 end
2353 end
2354 end
2355
2356 return true
2357end

getClutchPedal

Description
Returns clutch pedal state
Definition
getClutchPedal()
Return Values
floatstatestate [0-1]
Code
551function VehicleMotor:getClutchPedal()
552 if not self.vehicle.isServer or self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then
553 return self.manualClutchValue
554 end
555
556 return 1 - math.max(math.min((self:getClutchRotSpeed() * 30 / math.pi + 50) / self:getNonClampedMotorRpm(), 1), 0) -- have 50 rpm tolerance
557end

getClutchRotSpeed

Description
Returns clutch rpm
Definition
getClutchRotSpeed()
Return Values
floatclutchRpmclutch rpm
Code
834function VehicleMotor:getClutchRotSpeed()
835 return self.differentialRotSpeed * self.gearRatio
836end

getCurMaxRpm

Description
Returns current max rpm
Definition
getCurMaxRpm()
Return Values
IntegermaxRpmcurrent max rpm
Code
2363function VehicleMotor:getCurMaxRpm()
2364 local maxRpm = self.maxRpm
2365
2366 local gearRatio = self:getGearRatio()
2367 if gearRatio ~= 0 then
2368 --local speedLimit = self.speedLimit * 0.277778
2369 local speedLimit = math.min(self.speedLimit, math.max(self.speedLimitAcc, self.vehicle.lastSpeedReal*3600)) * 0.277778
2370 if gearRatio > 0 then
2371 speedLimit = math.min(speedLimit, self.maxForwardSpeed)
2372 else
2373 speedLimit = math.min(speedLimit, self.maxBackwardSpeed)
2374 end
2375
2376 maxRpm = math.min(maxRpm, speedLimit * 30 / math.pi * math.abs(gearRatio))
2377 end
2378
2379 maxRpm = math.min(maxRpm, self.rpmLimit)
2380 return maxRpm
2381end

getDampingRateFullThrottle

Description
Returns the damping rate of the motor if the acceleration pedal is 1
Definition
getDampingRateFullThrottle()
Return Values
floatdampingRatedamping rate [t m^2 s^-1]
Code
420function VehicleMotor:getDampingRateFullThrottle()
421 return self.dampingRateFullThrottle
422end

getDampingRateZeroThrottleClutchDisengaged

Description
Returns the damping rate of the motor if the acceleration pedal is 0 and the clutch is disengaged
Definition
getDampingRateZeroThrottleClutchDisengaged()
Return Values
floatdampingRatedamping rate [t m^2 s^-1]
Code
434function VehicleMotor:getDampingRateZeroThrottleClutchDisengaged()
435 return self.dampingRateZeroThrottleClutchDisengaged
436end

getDampingRateZeroThrottleClutchEngaged

Description
Returns the damping rate of the motor if the acceleration pedal is 0 and the clutch is engaged
Definition
getDampingRateZeroThrottleClutchEngaged()
Return Values
floatdampingRatedamping rate [t m^2 s^-1]
Code
427function VehicleMotor:getDampingRateZeroThrottleClutchEngaged()
428 return self.dampingRateZeroThrottleClutchEngaged
429end

getDrivingDirection

Description
Returns the current driving direction or preselected direction
Definition
getDrivingDirection()
Code
645function VehicleMotor:getDrivingDirection()
646 if self.directionChangeMode == VehicleMotor.DIRECTION_CHANGE_MODE_MANUAL or self.gearShiftMode ~= VehicleMotor.SHIFT_MODE_AUTOMATIC then
647 return self.currentDirection
648 else
649 if self.vehicle:getLastSpeed() > 0.95 then
650 return self.vehicle.movingDirection
651 end
652 end
653
654 return 0
655end

getEqualizedMotorRpm

Description
Returns equalized motor rpm
Definition
getEqualizedMotorRpm()
Return Values
floatequalizedMotorRpmequalized motor rpm
Code
790function VehicleMotor:getEqualizedMotorRpm()
791 return self.equalizedMotorRpm
792end

getGearGroupToDisplay

Description
Returns the current gear group
Definition
getGearGroupToDisplay()
Return Values
stringgroupgroup
booleangroupsAvailablegroupsAvailable
Code
661function VehicleMotor:getGearGroupToDisplay()
662 local gearGroupName, available = "N", false
663 if self.backwardGears or self.forwardGears then
664 if self.gearGroups ~= nil then
665 if self.activeGearGroupIndex > 0 then
666 local gearGroup = self.gearGroups[self.activeGearGroupIndex]
667 if gearGroup ~= nil then
668 gearGroupName = gearGroup.name
669 end
670 end
671
672 available = true
673 end
674 end
675
676 return gearGroupName, available
677end

getGearRatio

Description
Definition
getGearRatio()
Code
2305function VehicleMotor:getGearRatio()
2306 return self.gearRatio
2307end

getGearRatioMultiplier

Description
Returns ratio from current selected gear group or 1 if non is defined
Definition
getGearRatioMultiplier()
Return Values
floatratioratio
Code
2312function VehicleMotor:getGearRatioMultiplier()
2313 local multiplier = self.directionChangeUseGroup and 1 or self.currentDirection
2314 if self.gearGroups ~= nil then
2315 if self.activeGearGroupIndex == 0 then
2316 return 0
2317 end
2318
2319 local group = self.gearGroups[self.activeGearGroupIndex]
2320 if group ~= nil then
2321 return group.ratio * multiplier
2322 end
2323 end
2324
2325 return multiplier
2326end

getGearToDisplay

Description
Returns the current gear
Definition
getGearToDisplay()
Return Values
stringgeargear
booleangearsAvailablegearsAvailable
Code
581function VehicleMotor:getGearToDisplay()
582 local gearName, available = "N", false
583 local prevGearName, nextGearName
584 local prevPrevGearName, nextNextGearName
585 local isAutomatic = false
586 local isGearChanging = false
587
588 if self.backwardGears or self.forwardGears then
589 if self.targetGear > 0 then
590 local gear = self.currentGears[self.targetGear]
591 if gear ~= nil then
592 local displayDirection = self.currentDirection
593 local gearNameDirection = self.currentGears == self.forwardGears and self.currentDirection or 1
594
595 gearName = gearNameDirection == 1 and gear.name or gear.reverseName
596
597 local prevGear = self.currentGears[self.targetGear + 1 * -displayDirection]
598 if prevGear ~= nil then
599 prevGearName = gearNameDirection == 1 and prevGear.name or prevGear.reverseName
600
601 prevGear = self.currentGears[self.targetGear + 2 * -displayDirection]
602 if prevGear ~= nil then
603 prevPrevGearName = gearNameDirection == 1 and prevGear.name or prevGear.reverseName
604 end
605 end
606
607 local nextGear = self.currentGears[self.targetGear + 1 * displayDirection]
608 if nextGear ~= nil then
609 nextGearName = gearNameDirection == 1 and nextGear.name or nextGear.reverseName
610
611 nextGear = self.currentGears[self.targetGear + 2 * displayDirection]
612 if nextGear ~= nil then
613 nextNextGearName = gearNameDirection == 1 and nextGear.name or nextGear.reverseName
614 end
615 end
616
617 if self.gear ~= self.targetGear then
618 isGearChanging = true
619 end
620 end
621 end
622
623 available = true
624 else
625 local direction = self:getDrivingDirection()
626 if direction > 0 then
627 gearName = "D"
628 prevGearName = "N"
629 elseif direction < 0 then
630 gearName = "R"
631 nextGearName = "N"
632 else
633 nextGearName = "D"
634 prevGearName = "R"
635 end
636
637 isAutomatic = true
638 end
639
640 return gearName, available, isAutomatic, prevGearName, nextGearName, prevPrevGearName, nextNextGearName, isGearChanging
641end

getIsGearChangeAllowed

Description
Returns is shifting is allowed due to clutch pedal state
Definition
getIsGearChangeAllowed()
Return Values
booleanallowedallowed
Code
2231function VehicleMotor:getIsGearChangeAllowed()
2232 if self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then
2233 if self.gearType ~= VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT then
2234 return self.manualClutchValue > 0.5
2235 end
2236 end
2237
2238 return true
2239end

getIsGearGroupChangeAllowed

Description
Returns is shifting is allowed due to clutch pedal state
Definition
getIsGearGroupChangeAllowed()
Return Values
booleanallowedallowed
Code
2244function VehicleMotor:getIsGearGroupChangeAllowed()
2245 if self.gearGroups == nil then
2246 return false
2247 end
2248
2249 if self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then
2250 if self.groupType ~= VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT then
2251 return self.manualClutchValue > 0.5
2252 end
2253 end
2254
2255 return true
2256end

getIsInNeutral

Description
Definition
getIsInNeutral()
Code
2330function VehicleMotor:getIsInNeutral()
2331 if self.backwardGears or self.forwardGears then
2332 if self.gear == 0 and self.targetGear == 0 then
2333 return true
2334 end
2335 end
2336
2337 return false
2338end

getLastModulatedMotorRpm

Description
Returns last motor rpm modulated
Definition
getLastModulatedMotorRpm()
Return Values
floatlastModulatedMotorRpmlast modulated motor rpm
Code
516function VehicleMotor:getLastModulatedMotorRpm()
517 local modulationIntensity = MathUtil.clamp((self.smoothedLoadPercentage - MODULATION_RPM_MIN_REF_LOAD) / (MODULATION_RPM_MAX_REF_LOAD - MODULATION_RPM_MIN_REF_LOAD), MODULATION_RPM_MIN_INTENSITY, 1)
518 local modulationOffset = self.lastModulationPercentage * (MODULATION_RPM_MAX_OFFSET * modulationIntensity) * self.constantRpmCharge
519
520 -- apply only if clutch is released since with slipping clutch the rpm is already decreased
521 local loadChangeChargeDrop = 0
522 if self:getClutchPedal() < 0.1 and self.minGearRatio > 0 then
523 local rpmRange = self.maxRpm - self.minRpm
524 local dropScale = (self.lastMotorRpm - self.minRpm) / rpmRange * 0.5
525 loadChangeChargeDrop = self.loadPercentageChangeCharge * rpmRange * dropScale
526 else
527 self.loadPercentageChangeCharge = 0
528 end
529
530 return self.lastMotorRpm + modulationOffset - loadChangeChargeDrop
531end

getLastMotorRpm

Description
Returns last motor rpm damped
Definition
getLastMotorRpm()
Return Values
floatlastMotorRpmlast motor rpm
Code
509function VehicleMotor:getLastMotorRpm()
510 return self.lastMotorRpm
511end

getLastRealMotorRpm

Description
Returns last motor rpm real
Definition
getLastRealMotorRpm()
Return Values
floatlastMotorRpmlast motor rpm
Code
536function VehicleMotor:getLastRealMotorRpm()
537 return self.lastRealMotorRpm
538end

getManualClutchPedal

Description
Returns manual clutch pedal state
Definition
getManualClutchPedal()
Return Values
floatstatestate [0-1]
Code
569function VehicleMotor:getManualClutchPedal()
570 if self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then
571 return self.manualClutchValue
572 end
573
574 return 0
575end

getMaxClutchTorque

Description
Returns max clutch torque
Definition
getMaxClutchTorque()
Return Values
floatmaxClutchTorquemax clutch torque
Code
399function VehicleMotor:getMaxClutchTorque()
400 return self.maxClutchTorque
401end

getMaximumBackwardSpeed

Description
Returns maximum backward speed
Definition
getMaximumBackwardSpeed()
Return Values
floatmaxBackwardSpeedmaximum backward speed
Code
888function VehicleMotor:getMaximumBackwardSpeed()
889 return self.maxBackwardSpeed
890end

getMaximumForwardSpeed

Description
Returns maximum forward speed
Definition
getMaximumForwardSpeed()
Return Values
floatmaxForwardSpeedmaximum forward speed
Code
881function VehicleMotor:getMaximumForwardSpeed()
882 return self.maxForwardSpeed
883end

getMaxRpm

Description
Returns max rpm
Definition
getMaxRpm()
Return Values
floatmaxRpmmax rpm
Code
490function VehicleMotor:getMaxRpm()
491 return self.maxRpm
492end

getMinMaxGearRatio

Description
Returns currently selected minimum and maximum gear ratio Gear ratios for driving backwards are negative. Min/max always refers to the absolute value For regular gear box transmission, the minimum and maximum gear ratios are identical
Definition
getMinMaxGearRatio()
Return Values
floatminGearRatiominimum gear ratio
floatmaxGearRatiomaximum gear ratio
Code
2285function VehicleMotor:getMinMaxGearRatio()
2286 local minRatio = self.minGearRatio
2287 local maxRatio = self.maxGearRatio
2288
2289 if self.minGearRatio ~= 0 or self.maxGearRatio ~= 0 then
2290 if self.clutchSlippingTimer == self.clutchSlippingTime then
2291 -- use high max gear ratio for smooth acceleration since we are not in a speed where we can fully engage the gear
2292 maxRatio = math.max(350, self.maxGearRatio) * MathUtil.sign(self.maxGearRatio)
2293 elseif self.clutchSlippingTimer > 0 then
2294 -- after we reached 75% of the min. needed differential speed we slowly fade into the target gear ratio
2295 minRatio = MathUtil.lerp(minRatio, self.clutchSlippingGearRatio, self.clutchSlippingTimer / self.clutchSlippingTime)
2296 maxRatio = MathUtil.lerp(maxRatio, self.clutchSlippingGearRatio, self.clutchSlippingTimer / self.clutchSlippingTime)
2297 end
2298 end
2299
2300 return minRatio, maxRatio
2301end

getMinRpm

Description
Returns min rpm
Definition
getMinRpm()
Return Values
floatminRpmmin rpm
Code
483function VehicleMotor:getMinRpm()
484 return self.minRpm
485end

getMotorAppliedTorque

Description
Returns the last applied torque to the motor
Definition
getMotorAppliedTorque()
Return Values
floatappliedTorquetorque [kN]
Code
769function VehicleMotor:getMotorAppliedTorque()
770 return self.motorAppliedTorque
771end

getMotorAvailableTorque

Description
Returns the last total available motor torque
Definition
getMotorAvailableTorque()
Return Values
floattorqueexternal torque [kN]
Code
783function VehicleMotor:getMotorAvailableTorque()
784 return self.motorAvailableTorque
785end

getMotorExternalTorque

Description
Returns the last applied external torque (torque used by external power consumers like the PTO)
Definition
getMotorExternalTorque()
Return Values
floatexternalTorqueexternal torque [kN]
Code
776function VehicleMotor:getMotorExternalTorque()
777 return self.motorExternalTorque
778end

getMotorRotationAccelerationLimit

Description
Definition
getMotorRotationAccelerationLimit()
Code
2424function VehicleMotor:getMotorRotationAccelerationLimit()
2425 return self.motorRotationAccelerationLimit
2426end

getMotorRotSpeed

Description
Returns non clamped motor rpm
Definition
getMotorRotSpeed()
Return Values
floatnonClampedMotorRpmnon clamped motor rpm
Code
826function VehicleMotor:getMotorRotSpeed()
827 return self.motorRotSpeed
828end

getNonClampedMotorRpm

Description
Returns non clamped motor rpm
Definition
getNonClampedMotorRpm()
Return Values
floatnonClampedMotorRpmnon clamped motor rpm
Code
819function VehicleMotor:getNonClampedMotorRpm()
820 return self.motorRotSpeed * 30 / math.pi
821end

getPeakTorque

Description
Returns max torque
Definition
getPeakTorque()
Return Values
floatmaxMotorTorquemax motor torque
Code
469function VehicleMotor:getPeakTorque()
470 return self.peakMotorTorque
471end

getPtoMotorRpmRatio

Description
Returns pto motor rpm ratio
Definition
getPtoMotorRpmRatio()
Return Values
floatptoMotorRpmRatiopto motor rpm ratio
Code
805function VehicleMotor:getPtoMotorRpmRatio()
806 return self.ptoMotorRpmRatio
807end

getRequiredMotorRpmRange

Description
Returns the currently required motor rpm range (e.g. defined by the activated pto)
Definition
getRequiredMotorRpmRange()
Return Values
floatminRequiredRpmmin required rpm
floatminRequiredRpmmax required rpm
Code
498function VehicleMotor:getRequiredMotorRpmRange()
499 local motorPtoRpm = math.min(PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio, self.maxRpm)
500 if motorPtoRpm ~= 0 then
501 return motorPtoRpm, self.maxRpm
502 end
503 return self.minRpm, self.maxRpm
504end

getRequiredRpmAtSpeedLimit

Description
Returns the rpm while driving with this gear ratio
Definition
getRequiredRpmAtSpeedLimit(float ratio)
Arguments
floatratiogear ratio to check
Return Values
floatrpmrpm
Code
1317function VehicleMotor:getRequiredRpmAtSpeedLimit(ratio)
1318 -- use vehicle speed limit (normally from working tool) and cruise control speed, not motor speed limit to avoid issues with ai since the ai is permanently controlling the motor speed limit on headlands
1319 -- this leads to permanent gear shifts and wrong starting gears
1320 local speedLimit = math.min(self.vehicle:getSpeedLimit(true), math.max(self.speedLimitAcc, self.vehicle.lastSpeedReal*3600))
1321 if self.vehicle:getCruiseControlState() == Drivable.CRUISECONTROL_STATE_ACTIVE then
1322 speedLimit = math.min(speedLimit, self.vehicle:getCruiseControlSpeed())
1323 end
1324
1325 speedLimit = ratio > 0 and math.min(speedLimit, self.maxForwardSpeed * 3.6) or math.min(speedLimit, self.maxBackwardSpeed * 3.6)
1326 return speedLimit / 3.6 * 30 / math.pi * math.abs(ratio)
1327end

getRotInertia

Description
Returns rotation inertia
Definition
getRotInertia()
Return Values
floatrotInertiarotation inertia
Code
406function VehicleMotor:getRotInertia()
407 return self.rotInertia
408end

getSmoothedClutchPedal

Description
Returns smoothed clutch pedal state
Definition
getSmoothedClutchPedal()
Return Values
floatstatestate [0-1]
Code
562function VehicleMotor:getSmoothedClutchPedal()
563 return self.lastSmoothedClutchPedal
564end

getSmoothLoadPercentage

Description
Returns the last smoothed load percentage
Definition
getSmoothLoadPercentage()
Return Values
floatloadload [0-1]
Code
543function VehicleMotor:getSmoothLoadPercentage()
544 local modulationIntensity = MathUtil.clamp((self.smoothedLoadPercentage - MODULATION_LOAD_MIN_REF_LOAD) / (MODULATION_LOAD_MAX_REF_LOAD - MODULATION_LOAD_MIN_REF_LOAD), MODULATION_LOAD_MIN_INTENSITY, 1)
545 return self.smoothedLoadPercentage - self.lastModulationPercentage * (MODULATION_LOAD_MAX_OFFSET * modulationIntensity)
546end

getSpeedLimit

Description
Definition
getSpeedLimit()
Code
2392function VehicleMotor:getSpeedLimit()
2393 return self.speedLimit
2394end

getStartInGearFactor

Description
Returns factor which defines if the vehicle can start with the given gear ratio
Definition
getStartInGearFactor(float ratio)
Arguments
floatratiogear ratio to check
Return Values
floatstartFactorstart factor
Code
1333function VehicleMotor:getStartInGearFactor(ratio)
1334 -- if we cannot run the gear with at least 25% rpm with the current speed limit we skip it
1335 if self:getRequiredRpmAtSpeedLimit(ratio) < self.minRpm + (self.maxRpm - self.minRpm) * 0.25 then
1336 return math.huge
1337 end
1338
1339 local slope = self.startGearValues.slope
1340 if ratio < 0 then
1341 slope = -slope
1342 end
1343
1344 local slopePowerFactor = ((self.startGearValues.availablePower / 100 - 1) / 2) ^ 2 * 2 + 1
1345 local slopeFactor = 1 + math.max(slope, 0) / (slopePowerFactor * 0.06981) -- 4 degrees
1346 return self.startGearValues.massFactor * slopeFactor / (math.abs(ratio) / 300)
1347end

getTorque

Description
Returns torque of the motor at the current rpm with the given accelerator pedal
Definition
getTorque(float acceleration)
Arguments
floataccelerationacceleration
Return Values
floattorquetorque
Code
849function VehicleMotor:getTorque(acceleration)
850 -- Note: the torque curve is undefined outside the min/max rpm range. Clamping makes the curve flat at the outside range
851 local torque = self:getTorqueCurveValue(MathUtil.clamp(self.motorRotSpeed * 30/math.pi, self.minRpm, self.maxRpm))
852 torque = torque * math.abs(acceleration)
853 return torque
854end

getTorqueAndSpeedValues

Description
Definition
getTorqueAndSpeedValues()
Code
867function VehicleMotor:getTorqueAndSpeedValues()
868 local rotationSpeeds = {}
869 local torques = {}
870 for _,v in ipairs(self:getTorqueCurve().keyframes) do
871 table.insert(rotationSpeeds, v.time*math.pi/30)
872 table.insert(torques, self:getTorqueCurveValue(v.time))
873 end
874
875 return torques, rotationSpeeds
876end

getTorqueCurve

Description
Returns torque curve
Definition
getTorqueCurve()
Return Values
tabletorqueCurvetorque curve
Code
841function VehicleMotor:getTorqueCurve()
842 return self.torqueCurve
843end

getTorqueCurveValue

Description
Returns torque of the motor at the given rpm
Definition
getTorqueCurveValue(float rpm)
Arguments
floatrpmrpm
Return Values
floattorquetorque
Code
860function VehicleMotor:getTorqueCurveValue(rpm)
861 local damage = 1 - (self.vehicle:getVehicleDamage() * VehicleMotor.DAMAGE_TORQUE_REDUCTION)
862 return self:getTorqueCurve():get(rpm) * damage
863end

getUseAutomaticGearShifting

Description
Definition
getUseAutomaticGearShifting()
Code
2442function VehicleMotor:getUseAutomaticGearShifting()
2443 if self.gearShiftMode == VehicleMotor.SHIFT_MODE_AUTOMATIC then
2444 return true
2445 end
2446
2447 if not self.manualShiftGears then
2448 return true
2449 end
2450
2451 return false
2452end

getUseAutomaticGroupShifting

Description
Definition
getUseAutomaticGroupShifting()
Code
2456function VehicleMotor:getUseAutomaticGroupShifting()
2457 if self.gearShiftMode == VehicleMotor.SHIFT_MODE_AUTOMATIC then
2458 return true
2459 end
2460
2461 if not self.manualShiftGroups then
2462 return true
2463 end
2464
2465 return false
2466end

new

Description
Creating new motor
Definition
new(Integer minRpm, Integer maxRpm, float maxForwardSpeed, float maxBackwardSpeed, table torqueCurve, float brakeForce, float forwardGears, float backwardGears, float minForwardGearRatio, float maxForwardGearRatio, float minBackwardGearRatio, float maxBackwardGearRatio, Integer ptoMotorRpmRatio)
Arguments
IntegerminRpmmin rpm
IntegermaxRpmmax rpm
floatmaxForwardSpeedmax forward speed
floatmaxBackwardSpeedmax backward speed
tabletorqueCurvetorque curve (AnimCurve)
floatbrakeForcebrake force
floatforwardGearslist of gear ratios to use when driving forwards (in decreasing order)
floatbackwardGearslist of gear ratios to use when driving backwards (in decreasing order)
floatminForwardGearRatiomin forward gear ratio
floatmaxForwardGearRatiomax forward gear ratio
floatminBackwardGearRatiomin backward gear ratio
floatmaxBackwardGearRatiomax backward gear ratio
IntegerptoMotorRpmRatiopto motor rpm ratio
Return Values
tablemotorInstancemotor instance
Code
64function VehicleMotor.new(vehicle, minRpm, maxRpm, maxForwardSpeed, maxBackwardSpeed, torqueCurve, brakeForce, forwardGears, backwardGears, minForwardGearRatio, maxForwardGearRatio, minBackwardGearRatio, maxBackwardGearRatio, ptoMotorRpmRatio, minSpeed)
65
66 local self = {}
67 setmetatable(self, VehicleMotor_mt)
68
69 self.vehicle = vehicle
70 self.minRpm = minRpm
71 self.maxRpm = maxRpm
72 self.minSpeed = minSpeed
73 self.maxForwardSpeed = maxForwardSpeed -- speed in m/s
74 self.maxBackwardSpeed = maxBackwardSpeed
75
76 self.maxClutchTorque = 5 -- amount of torque that can be transferred from motor to clutch/wheels [t m s^-2]
77
78 self.torqueCurve = torqueCurve
79 self.brakeForce = brakeForce
80
81 self.lastAcceleratorPedal = 0
82 self.idleGearChangeTimer = 0 -- if this timer reaches 0 the automatic gear change while not moving is allowed
83 self.doSecondBestGearSelection = 0
84
85 self.gear = 0
86 self.bestGearSelected = 0
87 self.minGearRatio = 0
88 self.maxGearRatio = 0
89 self.allowGearChangeTimer = 0
90 self.allowGearChangeDirection = 0
91
92 self.forwardGears = forwardGears
93 self.backwardGears = backwardGears
94 self.currentGears = self.forwardGears
95 self.minForwardGearRatio = minForwardGearRatio
96 self.maxForwardGearRatio = maxForwardGearRatio
97 self.minBackwardGearRatio = minBackwardGearRatio
98 self.maxBackwardGearRatio = maxBackwardGearRatio
99
100 self.maxClutchSpeedDifference = 0
101 self.defaultForwardGear = 1
102 if self.forwardGears ~= nil then
103 for i=1, #self.forwardGears do
104 self.maxClutchSpeedDifference = math.max(self.maxClutchSpeedDifference, self.minRpm / self.forwardGears[i].ratio * math.pi / 30)
105 if self.forwardGears[i].default then
106 self.defaultForwardGear = i
107 end
108 end
109 end
110
111 self.defaultBackwardGear = 1
112 if self.backwardGears ~= nil then
113 for i=1, #self.backwardGears do
114 self.maxClutchSpeedDifference = math.max(self.maxClutchSpeedDifference, self.minRpm / self.backwardGears[i].ratio * math.pi / 30)
115 if self.backwardGears[i].default then
116 self.defaultBackwardGear = i
117 end
118 end
119 end
120
121 self.gearType = VehicleMotor.TRANSMISSION_TYPE.DEFAULT
122 self.groupType = VehicleMotor.TRANSMISSION_TYPE.DEFAULT
123
124 self.manualTargetGear = nil
125 self.targetGear = 0
126 self.previousGear = 0
127 self.gearChangeTimer = -1
128 self.gearChangeTime = 250
129 self.gearChangeTimeOrig = self.gearChangeTime
130 self.autoGearChangeTimer = -1
131 self.autoGearChangeTime = 1000
132 self.manualClutchValue = 0
133 self.stallTimer = 0
134 self.lastGearChangeTime = 0
135 self.gearChangeTimeAutoReductionTime = 500
136 self.gearChangeTimeAutoReductionTimer = 0
137
138 self.lastManualShifterActive = false
139
140 self.clutchSlippingTime = 1000
141 self.clutchSlippingTimer = 0
142 self.clutchSlippingGearRatio = 0
143
144 self.groupChangeTime = 500
145 self.groupChangeTimer = 0
146 self.gearGroupUpShiftTime = 3000
147 self.gearGroupUpShiftTimer = 0
148
149 self.currentDirection = 1 -- current used gear direction
150 self.directionChangeTimer = 0
151 self.directionChangeTime = 500
152 self.directionChangeUseGear = false -- use a backward gear for direction change buttons
153 self.directionChangeGearIndex = 1 -- backward gear to activate if direction changes
154 self.directionLastGear = -1 -- last forward gear, activated if direction is changed again
155 self.directionChangeUseGroup = false -- use a group for direction change buttons
156 self.directionChangeGroupIndex = 1 -- group to activate if direction changes
157 self.directionLastGroup = -1 -- last selected group, acitvated if direction changes again
158 self.directionChangeUseInverse = true -- if true the forward gears are just inverted for driving backwards
159
160 self.gearChangedIsLocked = false
161 self.gearGroupChangedIsLocked = false
162
163 self.startGearValues = {
164 slope = 0,
165 mass = 0,
166 lastMass = 0,
167 maxForce = 0,
168 massDirectionDifferenceXZ = 0,
169 massDirectionDifferenceY = 0,
170 massDirectionFactor = 0,
171 availablePower = 0,
172 massFactor = 0
173 }
174
175 self.startGearThreshold = VehicleMotor.GEAR_START_THRESHOLD
176
177 self.lastSmoothedClutchPedal = 0
178
179 self.lastRealMotorRpm = 0
180 self.lastMotorRpm = 0
181
182 self.lastModulationPercentage = 0
183 self.lastModulationTimer = 0
184
185 self.rawLoadPercentage = 0
186 self.rawLoadPercentageBuffer = 0
187 self.rawLoadPercentageBufferIndex = 0
188 self.smoothedLoadPercentage = 0
189 self.loadPercentageChangeCharge = 0
190
191 self.accelerationLimitLoadScale = 1
192 self.accelerationLimitLoadScaleTimer = 0
193 self.accelerationLimitLoadScaleDelay = 2000 -- after running this time at max acceleration we decrease the motor load slowly again
194
195 self.constantRpmCharge = 0
196 self.constantAccelerationCharge = 0
197
198 self.lastTurboScale = 0
199 self.blowOffValveState = 0
200
201 self.overSpeedTimer = 0
202
203 self.rpmLimit = math.huge
204 self.speedLimit = math.huge -- Speed limit in km/h
205 self.speedLimitAcc = math.huge
206
207 self.accelerationLimit = 2 -- m s^-2
208
209 self.motorRotationAccelerationLimit = (maxRpm - minRpm)*math.pi/30 / 2 -- rad s^-2 default accelerate from min rpm to max rpm in 2 sec
210
211 self.equalizedMotorRpm = 0
212
213 self.requiredMotorPower = 0
214
215 if self.maxForwardSpeed == nil then
216 self.maxForwardSpeed = self:calculatePhysicalMaximumForwardSpeed()
217 end
218 if self.maxBackwardSpeed == nil then
219 self.maxBackwardSpeed = self:calculatePhysicalMaximumBackwardSpeed()
220 end
221
222 -- saving the original values to be able to inverse them
223 self.maxForwardSpeedOrigin = self.maxForwardSpeed
224 self.maxBackwardSpeedOrigin = self.maxBackwardSpeed
225 self.minForwardGearRatioOrigin = self.minForwardGearRatio
226 self.maxForwardGearRatioOrigin = self.maxForwardGearRatio
227 self.minBackwardGearRatioOrigin = self.minBackwardGearRatio
228 self.maxBackwardGearRatioOrigin = self.maxBackwardGearRatio
229
230 self.peakMotorTorque = self.torqueCurve:getMaximum()
231
232 -- Calculate peak power. Assume we have a linear interpolation on the torque values
233 -- For each segment, find the maximum power (D[torque(x, i) * x] == 0) and take the maximum segment
234 -- D[ ((x-x0) / (x1-x0) (y1-y0) + y0) x] == 0
235 -- -> (x1 y0 - x0 y1) / (2 (y0 - y1)) if y0 != y1
236 self.peakMotorPower = 0
237 self.peakMotorPowerRotSpeed = 0
238 local numKeyFrames = #self.torqueCurve.keyframes
239 if numKeyFrames >= 2 then
240 for i=2,numKeyFrames do
241 local v0 = self.torqueCurve.keyframes[i-1]
242 local v1 = self.torqueCurve.keyframes[i]
243 local torque0 = self.torqueCurve:getFromKeyframes(v0, v0, i-1, i-1, 0)
244 local torque1 = self.torqueCurve:getFromKeyframes(v1, v1, i, i, 0)
245 local rpm, torque
246 if math.abs(torque0 - torque1) > 0.0001 then
247 rpm = (v1.time * torque0 - v0.time * torque1) / (2.0 * (torque0 - torque1))
248 rpm = math.min(math.max(rpm, v0.time), v1.time)
249 torque = self.torqueCurve:getFromKeyframes(v0, v1, i-1, i, (v1.time - rpm) / (v1.time - v0.time))
250 else
251 rpm = v0.time
252 torque = torque0
253 end
254 local power = torque * rpm
255 if power > self.peakMotorPower then
256 self.peakMotorPower = power
257 self.peakMotorPowerRotSpeed = rpm
258 end
259 end
260 -- Convert from rpm to rad/s
261 self.peakMotorPower = self.peakMotorPower * math.pi/30
262 self.peakMotorPowerRotSpeed = self.peakMotorPowerRotSpeed * math.pi/30
263 else
264 local v = self.torqueCurve.keyframes[1]
265 local rotSpeed = v.time*math.pi/30
266 local torque = self.torqueCurve:getFromKeyframes(v, v, 1, 1, 0)
267 self.peakMotorPower = rotSpeed*torque
268 self.peakMotorPowerRotSpeed = rotSpeed
269 end
270
271 self.ptoMotorRpmRatio = ptoMotorRpmRatio
272
273 self.rotInertia = self.peakMotorTorque / 600 -- Rotational inertia of the motor, mostly defined by the flywheel [t m^2]
274 self.dampingRateFullThrottle = VehicleMotor.DEFAULT_DAMPING_RATE_FULL_THROTTLE -- Damping rate of the motor if the acceleration pedal is 1 [t m^2 s^-1]
275 self.dampingRateZeroThrottleClutchEngaged = VehicleMotor.DEFAULT_DAMPING_RATE_ZERO_THROTTLE_CLUTCH_EN -- Damping rate of the motor if the acceleration pedal is 0 and the clutch is engaged [t m^2 s^-1]
276 self.dampingRateZeroThrottleClutchDisengaged = VehicleMotor.DEFAULT_DAMPING_RATE_ZERO_THROTTLE_CLUTCH_DIS -- Damping rate of the motor if the acceleration pedal is 0 and the clutch is disengaged [t m^2 s^-1]
277
278 -- Motor properties as read from the physics engine
279 self.gearRatio = 0
280 self.motorRotSpeed = 0 -- motor rotation speed [rad/s]
281 self.motorRotSpeedClutchEngaged = 0 -- additional rotation speed when clutch is engaged
282 self.motorRotAcceleration = 0 -- motor rotation acceleration [rad/s^2]
283 self.motorRotAccelerationSmoothed = 0 -- motor rotation acceleration smoothed [rad/s^2]
284
285 self.motorAvailableTorque, self.lastMotorAvailableTorque = 0, 0 -- torque that was available to the physics simulation [kN == t m/s^2]
286 self.motorAppliedTorque, self.lastMotorAppliedTorque = 0, 0 -- torque that was applied (<= available), can be smaller when acceleration/speed is limited [kN == t m/s^2]
287 self.motorExternalTorque, self.lastMotorExternalTorque = 0, 0 -- torque that was removed from the motor and was not applied to the wheels (e.g. PTO) [kN == t m/s^2]
288
289 -- externalTorqueVirtualMultiplicator: is used to have virtually more motor load due to external torque (pto) but still reduce the motor by the same external torque
290 -- like this we can increase the motor load without cutting the motor to hard so we cannot accelerate anymore
291 self.externalTorqueVirtualMultiplicator = 1
292
293 self.differentialRotSpeed = 0 -- rotation speed of the main differential [rad/s]
294 self.differentialRotAcceleration = 0 -- rotation accleration of the main differential [rad/s^2]
295 self.differentialRotAccelerationSmoothed = 0 -- smoothed rotation accleration of the main differential [rad/s^2]
296
297 self.differentialRotAccelerationIndex = 1
298 self.differentialRotAccelerationSamples = {}
299 for _=1, 10 do
300 table.insert(self.differentialRotAccelerationSamples, 0)
301 end
302
303 self.lastDifference = 0
304
305 self.directionChangeMode = g_gameSettings:getValue(GameSettings.SETTING.DIRECTION_CHANGE_MODE)
306 self.gearShiftMode = g_gameSettings:getValue(GameSettings.SETTING.GEAR_SHIFT_MODE)
307
308 return self
309end

onManualClutchChanged

Description
Called when manual clutch value changes
Definition
onManualClutchChanged(float value)
Arguments
floatvaluevalue
Code
2224function VehicleMotor:onManualClutchChanged(clutchValue)
2225 self.manualClutchValue = clutchValue
2226end

postLoad

Description
Post load motor
Definition
postLoad(table savegame)
Arguments
tablesavegamesavegame information
Code
314function VehicleMotor:postLoad(savegame)
315 if self.gearGroups ~= nil then
316 SpecializationUtil.raiseEvent(self.vehicle, "onGearGroupChanged", self.activeGearGroupIndex, 0)
317 end
318end

readGearDataFromStream

Description
Reads current gear data from stream
Definition
readGearDataFromStream()
Code
681function VehicleMotor:readGearDataFromStream(streamId)
682 self.currentDirection = streamReadUIntN(streamId, 2) - 1
683
684 if streamReadBool(streamId) then
685 local gear = streamReadUIntN(streamId, 6)
686 local changingGear = streamReadBool(streamId)
687
688 if streamReadBool(streamId) then
689 self.currentGears = self.forwardGears
690 else
691 self.currentGears = self.backwardGears
692 end
693
694 local activeGearGroupIndex
695 if self.gearGroups ~= nil then
696 activeGearGroupIndex = streamReadUIntN(streamId, 5)
697 end
698
699 if gear ~= self.gear then
700 if changingGear and self.gear ~= 0 then
701 self.lastGearChangeTime = g_time
702 end
703
704 self.gear = changingGear and 0 or gear
705 self.targetGear = gear
706
707 local directionMultiplier = self.directionChangeUseGear and self.currentDirection or 1
708 SpecializationUtil.raiseEvent(self.vehicle, "onGearChanged", self.gear * directionMultiplier, self.targetGear * directionMultiplier, 0)
709 end
710
711 if activeGearGroupIndex ~= self.activeGearGroupIndex then
712 self.activeGearGroupIndex = activeGearGroupIndex
713 SpecializationUtil.raiseEvent(self.vehicle, "onGearGroupChanged", self.activeGearGroupIndex, self.groupType == VehicleMotor.TRANSMISSION_TYPE.DEFAULT and self.groupChangeTime or 0)
714 end
715 end
716end

selectGear

Description
Definition
selectGear()
Code
2022function VehicleMotor:selectGear(gearIndex, activation)
2023 if activation then
2024 if self.gear ~= gearIndex then
2025 if self:getIsGearChangeAllowed() then
2026 if self.currentGears[gearIndex] ~= nil then
2027 self:setGear(gearIndex, true)
2028 self.lastManualShifterActive = true
2029 end
2030 else
2031 SpecializationUtil.raiseEvent(self.vehicle, "onClutchCreaking", false, false, gearIndex)
2032 end
2033 end
2034 else
2035 -- go into neutral state if any gear input action is released
2036 self:setGear(0, false)
2037 self.lastManualShifterActive = true
2038 end
2039end

selectGroup

Description
Definition
selectGroup()
Code
2092function VehicleMotor:selectGroup(groupIndex, activation)
2093 if activation then
2094 if self:getIsGearGroupChangeAllowed() then
2095 if self.gearGroups ~= nil then
2096 if self.gearGroups[groupIndex] ~= nil then
2097 self:setGearGroup(groupIndex, true)
2098 end
2099 end
2100 else
2101 if self.activeGearGroupIndex ~= groupIndex then
2102 SpecializationUtil.raiseEvent(self.vehicle, "onClutchCreaking", false, true, nil, groupIndex)
2103 end
2104 end
2105 else
2106 -- go into neutral state if any group input action is released
2107 self:setGearGroup(0, false)
2108 end
2109end

setAccelerationLimit

Description
Definition
setAccelerationLimit()
Code
2398function VehicleMotor:setAccelerationLimit(accelerationLimit)
2399 self.accelerationLimit = accelerationLimit
2400end

setAutoGearChangeTime

Description
Sets the time that needs to pass since the last gear change until an automatic gear change is allowed
Definition
setAutoGearChangeTime(float autoGearChangeTime)
Arguments
floatautoGearChangeTimeautomatic gear change time [ms]
Code
461function VehicleMotor:setAutoGearChangeTime(autoGearChangeTime)
462 self.autoGearChangeTime = autoGearChangeTime
463 self.autoGearChangeTimer = math.min(self.autoGearChangeTimer, autoGearChangeTime)
464end

setDampingRateScale

Description
Scales all damping rate values with this factor
Definition
setDampingRateScale(float dampingRateScale)
Arguments
floatdampingRateScalescale of damping rate [0-1]
Code
441function VehicleMotor:setDampingRateScale(dampingRateScale)
442 self.dampingRateFullThrottle = VehicleMotor.DEFAULT_DAMPING_RATE_FULL_THROTTLE * dampingRateScale
443 self.dampingRateZeroThrottleClutchEngaged = VehicleMotor.DEFAULT_DAMPING_RATE_ZERO_THROTTLE_CLUTCH_EN * dampingRateScale
444 self.dampingRateZeroThrottleClutchDisengaged = VehicleMotor.DEFAULT_DAMPING_RATE_ZERO_THROTTLE_CLUTCH_DIS * dampingRateScale
445end

setDirectionChange

Description
Set power shift stages
Definition
setDirectionChange(table gearGroups)
Arguments
tablegearGroupsgearGroups
Code
361function VehicleMotor:setDirectionChange(directionChangeUseGear, directionChangeGearIndex, directionChangeUseGroup, directionChangeGroupIndex, directionChangeTime)
362 self.directionChangeUseGear = directionChangeUseGear
363 self.directionChangeGearIndex = directionChangeGearIndex
364 self.directionChangeUseGroup = directionChangeUseGroup
365 self.directionChangeGroupIndex = directionChangeGroupIndex
366 self.directionChangeTime = directionChangeTime
367 self.directionChangeUseInverse = not directionChangeUseGear and not directionChangeUseGroup
368end

setDirectionChangeMode

Description
Definition
setDirectionChangeMode()
Code
2430function VehicleMotor:setDirectionChangeMode(directionChangeMode)
2431 self.directionChangeMode = directionChangeMode
2432end

setEqualizedMotorRpm

Description
Sets equalized motor rpm
Definition
setEqualizedMotorRpm(float equalizedMotorRpm)
Arguments
floatequalizedMotorRpmequalized motor rpm
Code
797function VehicleMotor:setEqualizedMotorRpm(rpm)
798 self.equalizedMotorRpm = rpm
799 self:setLastRpm(rpm)
800end

setExternalTorqueVirtualMultiplicator

Description
Sets the virtual external torque multiplicator
Definition
setExternalTorqueVirtualMultiplicator()
Return Values
floatexternalTorqueVirtualMultiplicatorvirtual external torque multiplicator
Code
812function VehicleMotor:setExternalTorqueVirtualMultiplicator(externalTorqueVirtualMultiplicator)
813 self.externalTorqueVirtualMultiplicator = externalTorqueVirtualMultiplicator or 1
814end

setGear

Description
Definition
setGear()
Code
2043function VehicleMotor:setGear(gearIndex, isLocked)
2044 if gearIndex ~= self.targetGear then
2045 if self.gearChangeTime == 0 and self.targetGear > gearIndex then
2046 self.loadPercentageChangeCharge = 1
2047 end
2048
2049 if self.gearShiftMode ~= VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then
2050 self.targetGear = gearIndex
2051 self.previousGear = self.gear
2052 self.gear = 0
2053 self.minGearRatio = 0
2054 self.maxGearRatio = 0
2055 self.autoGearChangeTimer = self.autoGearChangeTime
2056 self.gearChangeTimer = self.gearChangeTime
2057 else
2058 self.targetGear = gearIndex
2059 self.previousGear = self.gear
2060 self.gear = gearIndex
2061 end
2062
2063 local directionMultiplier = self.directionChangeUseGear and self.currentDirection or 1
2064 SpecializationUtil.raiseEvent(self.vehicle, "onGearChanged", self.gear * directionMultiplier, self.targetGear * directionMultiplier, self.gearChangeTime)
2065 end
2066end

setGearChangeTime

Description
Sets the time it takes change gears
Definition
setGearChangeTime(float gearChangeTime)
Arguments
floatgearChangeTimegear change time [ms]
Code
450function VehicleMotor:setGearChangeTime(gearChangeTime)
451 self.gearChangeTime = gearChangeTime
452 self.gearChangeTimeOrig = gearChangeTime
453 self.gearChangeTimer = math.min(self.gearChangeTimer, gearChangeTime)
454
455 self.gearType = gearChangeTime == 0 and VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT or VehicleMotor.TRANSMISSION_TYPE.DEFAULT
456end

setGearGroup

Description
Definition
setGearGroup()
Code
2113function VehicleMotor:setGearGroup(groupIndex, isLocked)
2114 local lastActiveGearGroupIndex = self.activeGearGroupIndex
2115 self.activeGearGroupIndex = groupIndex
2116 self.gearGroupChangedIsLocked = isLocked
2117
2118 if self.activeGearGroupIndex ~= lastActiveGearGroupIndex then
2119 if self.groupType == VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT and self.activeGearGroupIndex > lastActiveGearGroupIndex then
2120 self.loadPercentageChangeCharge = 1
2121 end
2122
2123 if self.directionChangeUseGroup then
2124 self.currentDirection = self.activeGearGroupIndex == self.directionChangeGroupIndex and -1 or 1
2125 end
2126
2127 if self.gearShiftMode ~= VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then
2128 if self.groupType == VehicleMotor.TRANSMISSION_TYPE.DEFAULT then
2129 self.groupChangeTimer = self.groupChangeTime
2130 self.gear = 0
2131 self.minGearRatio = 0
2132 self.maxGearRatio = 0
2133 elseif self.groupType == VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT then
2134 self:applyTargetGear()
2135 end
2136 end
2137
2138 SpecializationUtil.raiseEvent(self.vehicle, "onGearGroupChanged", self.activeGearGroupIndex, self.groupType == VehicleMotor.TRANSMISSION_TYPE.DEFAULT and self.groupChangeTime or 0)
2139 end
2140end

setGearGroups

Description
Set power shift stages
Definition
setGearGroups(table gearGroups)
Arguments
tablegearGroupsgearGroups
Code
329function VehicleMotor:setGearGroups(gearGroups, groupType, groupChangeTime)
330 self.gearGroups = gearGroups
331 self.groupType = VehicleMotor.TRANSMISSION_TYPE[groupType:upper()] or VehicleMotor.TRANSMISSION_TYPE.DEFAULT
332 self.groupChangeTime = groupChangeTime
333
334 if gearGroups ~= nil then
335 self.numGearGroups = #gearGroups
336 self.defaultGearGroup = 1
337
338 -- use first forward gear group
339 for i=1, self.numGearGroups do
340 if self.gearGroups[i].ratio > 0 then
341 self.defaultGearGroup = i
342 break
343 end
344 end
345
346 -- use group with default attribute set
347 for i=1, self.numGearGroups do
348 if self.gearGroups[i].isDefault then
349 self.defaultGearGroup = i
350 break
351 end
352 end
353
354 self.activeGearGroupIndex = self.defaultGearGroup
355 end
356end

setGearShiftMode

Description
Definition
setGearShiftMode()
Code
2436function VehicleMotor:setGearShiftMode(gearShiftMode)
2437 self.gearShiftMode = gearShiftMode
2438end

setLastRpm

Description
Sets last motor rpm
Definition
setLastRpm(float lastRpm)
Arguments
floatlastRpmnew last motor rpm
Code
738function VehicleMotor:setLastRpm(lastRpm)
739 local oldMotorRpm = self.lastMotorRpm
740
741 self.lastRealMotorRpm = lastRpm
742
743 local interpolationSpeed = 0.05
744
745 -- fast rpm drop for power shift transmissions to have a clear audible drop
746 if self.gearType == VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT and (g_time - self.lastGearChangeTime) < 200 then
747 interpolationSpeed = 0.2
748 end
749
750 self.lastMotorRpm = self.lastMotorRpm * (1 - interpolationSpeed) + self.lastRealMotorRpm * interpolationSpeed
751
752 -- calculate turbo speed scale depending on rpm and motor load
753 local rpmPercentage = (self.lastMotorRpm - math.max(self.lastPtoRpm or self.minRpm, self.minRpm)) / (self.maxRpm - self.minRpm)
754 local targetTurboRpm = rpmPercentage * self:getSmoothLoadPercentage()
755 self.lastTurboScale = self.lastTurboScale * 0.95 + targetTurboRpm * 0.05
756
757 if self.lastAcceleratorPedal == 0 or (self.minGearRatio == 0 and self.autoGearChangeTime > 0) then
758 self.blowOffValveState = self.lastTurboScale
759 else
760 self.blowOffValveState = 0
761 end
762
763 self.constantRpmCharge = 1 - math.min(math.abs(self.lastMotorRpm - oldMotorRpm) * 0.15, 1)
764end

setLowBrakeForce

Description
Set low brake force
Definition
setLowBrakeForce(float lowBrakeForceScale, float lowBrakeForceSpeedLimit)
Arguments
floatlowBrakeForceScalelow brake force scale
floatlowBrakeForceSpeedLimitlow brake force speed limit
Code
391function VehicleMotor:setLowBrakeForce(lowBrakeForceScale, lowBrakeForceSpeedLimit)
392 self.lowBrakeForceScale = lowBrakeForceScale
393 self.lowBrakeForceSpeedLimit = lowBrakeForceSpeedLimit
394end

setManualShift

Description
Sets the manual shift settings
Definition
setManualShift(boolean gears, boolean groups)
Arguments
booleangearsgears can be shifted manually
booleangroupsgroups can be shifted manually
Code
374function VehicleMotor:setManualShift(manualShiftGears, manualShiftGroups)
375 self.manualShiftGears = manualShiftGears
376 self.manualShiftGroups = manualShiftGroups
377end

setMotorRotationAccelerationLimit

Description
Definition
setMotorRotationAccelerationLimit()
Code
2418function VehicleMotor:setMotorRotationAccelerationLimit(limit)
2419 self.motorRotationAccelerationLimit = limit
2420end

setRotInertia

Description
Sets rotation inertia
Definition
setRotInertia(float rotInertia)
Arguments
floatrotInertiarotation inertia
Code
413function VehicleMotor:setRotInertia(rotInertia)
414 self.rotInertia = rotInertia
415end

setRpmLimit

Description
Sets rpm limit
Definition
setRpmLimit(float limit)
Arguments
floatlimitnew limit
Code
2411function VehicleMotor:setRpmLimit(rpmLimit)
2412 self.rpmLimit = rpmLimit
2413end

setSpeedLimit

Description
Sets speed limit
Definition
setSpeedLimit(float limit)
Arguments
floatlimitnew limit
Code
2386function VehicleMotor:setSpeedLimit(limit)
2387 self.speedLimit = math.max(limit, self.minSpeed)
2388end

setStartGearThreshold

Description
Sets custom start gear threshold
Definition
setStartGearThreshold(float lowBrakeForceScale, float lowBrakeForceSpeedLimit)
Arguments
floatlowBrakeForceScalelow brake force scale
floatlowBrakeForceSpeedLimitlow brake force speed limit
Code
383function VehicleMotor:setStartGearThreshold(startGearThreshold)
384 self.startGearThreshold = startGearThreshold
385end

setTransmissionDirection

Description
Switches the gear ratios by the given direction
Definition
setTransmissionDirection(integer direction)
Arguments
integerdirectiondirection
Code
2261function VehicleMotor:setTransmissionDirection(direction)
2262 if direction > 0 then
2263 self.maxForwardSpeed = self.maxForwardSpeedOrigin
2264 self.maxBackwardSpeed = self.maxBackwardSpeedOrigin
2265 self.minForwardGearRatio = self.minForwardGearRatioOrigin
2266 self.maxForwardGearRatio = self.maxForwardGearRatioOrigin
2267 self.minBackwardGearRatio = self.minBackwardGearRatioOrigin
2268 self.maxBackwardGearRatio = self.maxBackwardGearRatioOrigin
2269 else
2270 self.maxForwardSpeed = self.maxBackwardSpeedOrigin
2271 self.maxBackwardSpeed = self.maxForwardSpeedOrigin
2272 self.minForwardGearRatio = self.minBackwardGearRatioOrigin
2273 self.maxForwardGearRatio = self.maxBackwardGearRatioOrigin
2274 self.minBackwardGearRatio = self.minForwardGearRatioOrigin
2275 self.maxBackwardGearRatio = self.maxForwardGearRatioOrigin
2276 end
2277end

shiftGear

Description
Definition
shiftGear()
Code
1978function VehicleMotor:shiftGear(up)
1979 if not self.gearChangedIsLocked then
1980 if self:getIsGearChangeAllowed() then
1981 local newGear
1982 if up then
1983 newGear = self.targetGear + 1 * self.currentDirection
1984 else
1985 newGear = self.targetGear - 1 * self.currentDirection
1986 end
1987
1988 if self.currentDirection > 0 or self.backwardGears == nil then
1989 if newGear > #self.forwardGears then
1990 newGear = #self.forwardGears
1991 end
1992 elseif self.currentDirection < 0 or self.backwardGears ~= nil then
1993 if newGear > #self.backwardGears then
1994 newGear = #self.backwardGears
1995 end
1996 end
1997
1998 if newGear ~= self.targetGear then
1999 if self.currentDirection > 0 then
2000 if newGear < 0 then
2001 self:changeDirection(-1)
2002 newGear = 1
2003 end
2004 else
2005 if newGear < 0 then
2006 self:changeDirection(1)
2007 newGear = 1
2008 end
2009 end
2010
2011 self:setGear(newGear)
2012 self.lastManualShifterActive = false
2013 end
2014 else
2015 SpecializationUtil.raiseEvent(self.vehicle, "onClutchCreaking", true, false)
2016 end
2017 end
2018end

shiftGroup

Description
Definition
shiftGroup()
Code
2071function VehicleMotor:shiftGroup(up)
2072 if not self.gearGroupChangedIsLocked then
2073 if self:getIsGearGroupChangeAllowed() then
2074 if self.gearGroups ~= nil then
2075 local newGearGroupIndex
2076 if up then
2077 newGearGroupIndex = self.activeGearGroupIndex + 1
2078 else
2079 newGearGroupIndex = self.activeGearGroupIndex - 1
2080 end
2081
2082 self:setGearGroup(MathUtil.clamp(newGearGroupIndex, 1, self.numGearGroups))
2083 end
2084 else
2085 SpecializationUtil.raiseEvent(self.vehicle, "onClutchCreaking", true, true)
2086 end
2087 end
2088end

update

Description
Update the state of the motor (sync with physics simulation)
Definition
update(float dt)
Arguments
floatdttime since last call in ms
Code
932function VehicleMotor:update(dt)
933 local vehicle = self.vehicle
934 if next(vehicle.spec_motorized.differentials) ~= nil and vehicle.spec_motorized.motorizedNode ~= nil then
935 local lastMotorRotSpeed = self.motorRotSpeed
936 local lastDiffRotSpeed = self.differentialRotSpeed
937 self.motorRotSpeed, self.differentialRotSpeed, self.gearRatio = getMotorRotationSpeed(vehicle.spec_motorized.motorizedNode)
938
939
940 if self.gearShiftMode ~= VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then
941 -- dynamically adjust the max gear ratio while starting in a gear and have not reached the min. differential speed
942 -- this simulates clutch slipping and allows a smooth acceleration
943 if (self.backwardGears or self.forwardGears) and self.gearRatio ~= 0 and self.maxGearRatio ~= 0 then
944 if self.lastAcceleratorPedal ~= 0 then
945 local minDifferentialSpeed = self.minRpm / math.abs(self.maxGearRatio) * math.pi / 30
946 if math.abs(self.differentialRotSpeed) < minDifferentialSpeed * 0.75 then
947 self.clutchSlippingTimer = self.clutchSlippingTime
948 self.clutchSlippingGearRatio = self.gearRatio
949 else
950 self.clutchSlippingTimer = math.max(self.clutchSlippingTimer - dt, 0)
951 end
952 end
953 end
954 end
955
956 if not self:getUseAutomaticGearShifting() then
957 local accelerationPedal = self.lastAcceleratorPedal * self.currentDirection
958
959 -- calculate additional rpm if clutch is engaged and user is pressing the accelerator pedal
960 local clutchValue = 0
961 if (self.minGearRatio == 0 and self.maxGearRatio == 0) or self.manualClutchValue > 0.1 then
962 clutchValue = 1
963 end
964 local direction = clutchValue * accelerationPedal
965 if direction == 0 then
966 direction = -1
967 end
968
969 local accelerationSpeed = direction > 0 and (self.motorRotationAccelerationLimit * 0.02) or self.dampingRateZeroThrottleClutchEngaged * 30 * math.pi
970 local minRotSpeed = self.minRpm * math.pi / 30
971 local maxRotSpeed = self.maxRpm * math.pi / 30
972 self.motorRotSpeedClutchEngaged = math.min(math.max(self.motorRotSpeedClutchEngaged + direction * accelerationSpeed * dt, minRotSpeed), minRotSpeed + (maxRotSpeed - minRotSpeed) * accelerationPedal)
973 self.motorRotSpeed = math.max(self.motorRotSpeed, self.motorRotSpeedClutchEngaged)
974 end
975
976 if g_physicsDtNonInterpolated > 0.0 and not getIsSleeping(vehicle.rootNode) then
977 self.lastMotorAvailableTorque, self.lastMotorAppliedTorque, self.lastMotorExternalTorque = getMotorTorque(vehicle.spec_motorized.motorizedNode)
978 end
979
980 self.motorAvailableTorque, self.motorAppliedTorque, self.motorExternalTorque = self.lastMotorAvailableTorque, self.lastMotorAppliedTorque, self.lastMotorExternalTorque
981
982 -- apply virtual pto torque factor
983 self.motorAppliedTorque = self.motorAppliedTorque - self.motorExternalTorque
984 self.motorExternalTorque = math.min(self.motorExternalTorque * self.externalTorqueVirtualMultiplicator, self.motorAvailableTorque - self.motorAppliedTorque)
985 self.motorAppliedTorque = self.motorAppliedTorque + self.motorExternalTorque
986
987 local motorRotAcceleration = (self.motorRotSpeed - lastMotorRotSpeed) / (g_physicsDtNonInterpolated*0.001)
988 self.motorRotAcceleration = motorRotAcceleration
989 self.motorRotAccelerationSmoothed = 0.8 * self.motorRotAccelerationSmoothed + 0.2 * motorRotAcceleration
990
991 local diffRotAcc = (self.differentialRotSpeed - lastDiffRotSpeed) / (g_physicsDtNonInterpolated*0.001)
992 self.differentialRotAcceleration = diffRotAcc
993 self.differentialRotAccelerationSmoothed = 0.95 * self.differentialRotAccelerationSmoothed + 0.05 * diffRotAcc
994
995 self.requiredMotorPower = math.huge
996 else
997 local _, gearRatio = self:getMinMaxGearRatio()
998 self.differentialRotSpeed = WheelsUtil.computeDifferentialRotSpeedNonMotor(vehicle)
999 self.motorRotSpeed = math.max(math.abs(self.differentialRotSpeed * gearRatio), 0)
1000 self.gearRatio = gearRatio
1001 end
1002
1003 -- the clamped motor rpm always is higher-equal than the required rpm by the pto
1004 --local ptoRpm = math.min(PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio, self.maxRpm)
1005 -- smoothing for raise/fall of ptoRpm
1006 if self.lastPtoRpm == nil then
1007 self.lastPtoRpm = self.minRpm
1008 end
1009 local ptoRpm = PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio
1010 if ptoRpm > self.lastPtoRpm then
1011 self.lastPtoRpm = math.min(ptoRpm, self.lastPtoRpm + self.maxRpm*dt/2000)
1012 elseif ptoRpm < self.lastPtoRpm then
1013 self.lastPtoRpm = math.max(self.minRpm, self.lastPtoRpm - self.maxRpm*dt/1000)
1014 end
1015
1016 -- client will recieve this value from the server
1017 if self.vehicle.isServer then
1018 local clampedMotorRpm = math.max(self.motorRotSpeed*30/math.pi, math.min(self.lastPtoRpm, self.maxRpm), self.minRpm)
1019 self:setLastRpm(clampedMotorRpm)
1020
1021 self.equalizedMotorRpm = clampedMotorRpm
1022
1023 local rawLoadPercentage = self:getMotorAppliedTorque() / math.max(self:getMotorAvailableTorque(), 0.0001)
1024 self.rawLoadPercentageBuffer = self.rawLoadPercentageBuffer + rawLoadPercentage
1025 self.rawLoadPercentageBufferIndex = self.rawLoadPercentageBufferIndex + 1
1026 if self.rawLoadPercentageBufferIndex >= 2 then
1027 self.rawLoadPercentage = self.rawLoadPercentageBuffer / 2
1028 self.rawLoadPercentageBuffer = 0
1029 self.rawLoadPercentageBufferIndex = 0
1030 end
1031
1032 if self.rawLoadPercentage < 0.01 and self.lastAcceleratorPedal < 0.2
1033 and not ((self.backwardGears or self.forwardGears) and self.gear == 0 and self.targetGear ~= 0) then
1034 -- while rolling but not currently changing gears
1035 self.rawLoadPercentage = -1
1036 else
1037 -- normal driving load is at 0 while motor is at idle load to keep it running and at 1 while it's at max load
1038 local idleLoadPct = 0.05 -- TODO change to real idle percentage
1039 self.rawLoadPercentage = (self.rawLoadPercentage - idleLoadPct) / (1 - idleLoadPct)
1040 end
1041
1042 local accelerationPercentage = math.min((self.vehicle.lastSpeedAcceleration * 1000 * 1000 * self.vehicle.movingDirection) / self.accelerationLimit, 1)
1043 if accelerationPercentage < 0.95 and self.lastAcceleratorPedal > 0.2 then
1044 self.accelerationLimitLoadScale = 1
1045 self.accelerationLimitLoadScaleTimer = self.accelerationLimitLoadScaleDelay
1046 else
1047 if self.accelerationLimitLoadScaleTimer > 0 then
1048 self.accelerationLimitLoadScaleTimer = self.accelerationLimitLoadScaleTimer - dt
1049
1050 local alpha = math.max(self.accelerationLimitLoadScaleTimer / self.accelerationLimitLoadScaleDelay, 0)
1051 self.accelerationLimitLoadScale = math.sin((1 - alpha) * 3.14) * 0.85
1052 end
1053 end
1054
1055 if accelerationPercentage > 0 then
1056 self.rawLoadPercentage = math.max(self.rawLoadPercentage, accelerationPercentage * self.accelerationLimitLoadScale)
1057 end
1058
1059 -- while we are not accelerating the constantAccelerationCharge is at 1, so the max. raw load from the engine is used. If we are accelerating we use only 80% of the load
1060 self.constantAccelerationCharge = 1 - math.min((math.abs(self.vehicle.lastSpeedAcceleration) * 1000 * 1000) / self.accelerationLimit, 1)
1061 if self.rawLoadPercentage > 0 then
1062 self.rawLoadPercentage = self.rawLoadPercentage * MAX_ACCELERATION_LOAD + self.rawLoadPercentage * (1 - MAX_ACCELERATION_LOAD) * self.constantAccelerationCharge
1063 end
1064
1065 if self.backwardGears or self.forwardGears then
1066 if self:getUseAutomaticGearShifting() then
1067 -- if we are in automatic mode and we are stuck in one gear for a while we try to reduce the shifting time
1068 -- like this are are not loosing too much speed while shifting and more gears are an option
1069 -- especially helpfull if we picked the wrong gear in field work
1070 if self.constantRpmCharge > 0.99 then
1071 if self.maxRpm - clampedMotorRpm < 50 then
1072 self.gearChangeTimeAutoReductionTimer = math.min(self.gearChangeTimeAutoReductionTimer + dt, self.gearChangeTimeAutoReductionTime)
1073 self.gearChangeTime = self.gearChangeTimeOrig * (1 - self.gearChangeTimeAutoReductionTimer / self.gearChangeTimeAutoReductionTime)
1074 else
1075 self.gearChangeTimeAutoReductionTimer = 0
1076 self.gearChangeTime = self.gearChangeTimeOrig
1077 end
1078 else
1079 self.gearChangeTimeAutoReductionTimer = 0
1080 self.gearChangeTime = self.gearChangeTimeOrig
1081 end
1082 end
1083 end
1084 end
1085
1086 self:updateSmoothLoadPercentage(dt, self.rawLoadPercentage)
1087
1088 self.idleGearChangeTimer = math.max(self.idleGearChangeTimer - dt, 0)
1089
1090 if self.forwardGears or self.backwardGears then
1091 self:updateStartGearValues(dt)
1092
1093 local clutchPedal = self:getClutchPedal()
1094 self.lastSmoothedClutchPedal = self.lastSmoothedClutchPedal * 0.9 + clutchPedal * 0.1
1095 end
1096
1097 self.lastModulationTimer = self.lastModulationTimer + dt * MODULATION_SPEED
1098 self.lastModulationPercentage = math.sin(self.lastModulationTimer) * math.sin((self.lastModulationTimer + 2) * 0.3) * 0.8 + math.cos(self.lastModulationTimer * 5) * 0.2
1099end

updateGear

Description
Update gear
Definition
updateGear(float acceleratorPedal)
Arguments
floatacceleratorPedalacceleratorPedal
Return Values
floatadjustedAcceleratorPedalthe adjusted accelerator pedal for the current gear situation (e.g. 0 while switching gears)
Code
1721function VehicleMotor:updateGear(acceleratorPedal, brakePedal, dt)
1722 self.lastAcceleratorPedal = acceleratorPedal
1723 local adjAcceleratorPedal = acceleratorPedal
1724 if self.gearChangeTimer >= 0 then
1725 self.gearChangeTimer = self.gearChangeTimer - dt
1726 if self.gearChangeTimer < 0 then
1727 if self.targetGear ~= 0 then
1728 self.allowGearChangeTimer = 3000
1729 self.allowGearChangeDirection = MathUtil.sign(self.targetGear-self.previousGear)
1730
1731 self:applyTargetGear()
1732 end
1733 end
1734 adjAcceleratorPedal = 0
1735 elseif self.groupChangeTimer > 0 or self.directionChangeTimer > 0 then
1736 self.groupChangeTimer = self.groupChangeTimer - dt
1737 self.directionChangeTimer = self.directionChangeTimer - dt
1738 if self.groupChangeTimer < 0 and self.directionChangeTimer < 0 then
1739 self:applyTargetGear()
1740 end
1741 else
1742 local gearSign = 0
1743 if acceleratorPedal > 0 then
1744 if self.minForwardGearRatio ~= nil then
1745 self.minGearRatio = self.minForwardGearRatio
1746 self.maxGearRatio = self.maxForwardGearRatio
1747 else
1748 gearSign = 1
1749 end
1750 elseif acceleratorPedal < 0 then
1751 if self.minBackwardGearRatio ~= nil then
1752 self.minGearRatio = -self.minBackwardGearRatio
1753 self.maxGearRatio = -self.maxBackwardGearRatio
1754 else
1755 gearSign = -1
1756 end
1757 else
1758 if self.maxGearRatio > 0 then
1759 if self.minForwardGearRatio == nil then
1760 gearSign = 1
1761 end
1762 elseif self.maxGearRatio < 0 then
1763 if self.minBackwardGearRatio == nil then
1764 gearSign = -1
1765 end
1766 end
1767 end
1768
1769 local newGear = self.gear
1770 local forceGearChange = false
1771 if self.backwardGears or self.forwardGears then
1772 if self:getUseAutomaticGearShifting() then
1773 self.autoGearChangeTimer = self.autoGearChangeTimer - dt
1774
1775 -- the users action to accelerate will always allow shfting
1776 -- this is just to avoid shifting while vehicle is not moving, but shfting conditions change (attaching tool, lowering/lifting tool etc.)
1777 if self.vehicle:getIsAutomaticShiftingAllowed() or acceleratorPedal ~= 0 then
1778 -- slower than 1,08km/h
1779 if math.abs(self.vehicle.lastSpeed) < 0.0003 then
1780 local directionChanged = false
1781 local trySelectBestGear = false
1782 local allowGearOverwritting = false
1783 if gearSign < 0 and (self.currentDirection == 1 or self.gear == 0) then
1784 self:changeDirection(-1, true)
1785 directionChanged = true
1786 elseif gearSign > 0 and (self.currentDirection == -1 or self.gear == 0) then
1787 self:changeDirection(1, true)
1788 directionChanged = true
1789 elseif self.lastAcceleratorPedal == 0 and self.idleGearChangeTimer <= 0 then
1790 trySelectBestGear = true
1791 self.doSecondBestGearSelection = 3
1792 elseif self.doSecondBestGearSelection > 0 and self.lastAcceleratorPedal ~= 0 then
1793 self.doSecondBestGearSelection = self.doSecondBestGearSelection - 1
1794 if self.doSecondBestGearSelection == 0 then
1795 -- do another try for the best gear directly after acceleration started
1796 -- the selected gear may not be correct due to an active speed limit (when accelerating with cruise control)
1797 trySelectBestGear = true
1798 allowGearOverwritting = true
1799 end
1800 end
1801
1802 if directionChanged then
1803 if self.targetGear ~= self.gear then
1804 newGear = self.targetGear
1805 end
1806
1807 trySelectBestGear = true
1808 end
1809
1810 if trySelectBestGear then
1811 local bestGear, maxFactorGroup = self:getBestStartGear(self.currentGears)
1812 if bestGear ~= self.gear or bestGear ~= self.bestGearSelected then
1813 newGear = bestGear
1814
1815 if bestGear > 1 or allowGearOverwritting then
1816 self.bestGearSelected = bestGear
1817 self.allowGearChangeTimer = 0
1818 end
1819 end
1820
1821 if self:getUseAutomaticGroupShifting() then
1822 if maxFactorGroup ~= nil and maxFactorGroup ~= self.activeGearGroupIndex then
1823 self:setGearGroup(maxFactorGroup)
1824 end
1825 end
1826 end
1827 else
1828 if self.gear ~= 0 then
1829 if self.autoGearChangeTimer <= 0 then
1830 if MathUtil.sign(acceleratorPedal) ~= MathUtil.sign(self.currentDirection) then
1831 acceleratorPedal = 0
1832 end
1833 newGear = self:findGearChangeTargetGearPrediction(self.gear, self.currentGears, self.currentDirection, self.autoGearChangeTimer, acceleratorPedal, dt)
1834
1835 if self:getUseAutomaticGroupShifting() then
1836 if self.gearGroups ~= nil then
1837 -- if we are in the highest gear and the maximum rpm range (50rpm threshold) we shift one group up
1838 if self.activeGearGroupIndex < #self.gearGroups then
1839 if math.abs(math.min(self:getLastRealMotorRpm(), self.maxRpm)-self.maxRpm) < 50 then
1840 if self.gear == #self.currentGears then
1841 -- if in the highest gear we immediatly shift up
1842 local nextRatio = self.gearGroups[self.activeGearGroupIndex + 1].ratio
1843 if MathUtil.sign(self.gearGroups[self.activeGearGroupIndex].ratio) == MathUtil.sign(nextRatio) then
1844 -- only shift up if we got at least 25% of the rpm range with the current set speed limit
1845 -- important with active cruise control or field work
1846 nextRatio = nextRatio * self.currentGears[self.gear].ratio
1847 if self:getRequiredRpmAtSpeedLimit(nextRatio) > self.minRpm + (self.maxRpm - self.minRpm) * 0.25 then
1848 self:shiftGroup(true)
1849 end
1850 end
1851 elseif self.groupType == VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT then
1852 -- if we are stuck in a gear we wait a few seconds and then shift up
1853 -- this only applies for power shift groups since we expect a normal group shift with clutch is also not possible like the gear shift
1854 if MathUtil.sign(self.gearGroups[self.activeGearGroupIndex].ratio) == MathUtil.sign(self.gearGroups[self.activeGearGroupIndex + 1].ratio) then
1855 self.gearGroupUpShiftTimer = self.gearGroupUpShiftTimer + dt
1856 if self.gearGroupUpShiftTimer > self.gearGroupUpShiftTime then
1857 self.gearGroupUpShiftTimer = 0
1858 self:shiftGroup(true)
1859 end
1860 else
1861 self.gearGroupUpShiftTimer = 0
1862 end
1863 end
1864 else
1865 self.gearGroupUpShiftTimer = 0
1866 end
1867 else
1868 self.gearGroupUpShiftTimer = 0
1869 end
1870
1871 -- in case we are in the first gear and below 25% of the rpm and in the group we are we would not have any gear to start we shift a group down
1872 if self.gear == 1 then
1873 if self.lastRealMotorRpm < self.minRpm + (self.maxRpm - self.minRpm) * 0.25 then
1874 local _, maxFactorGroup = self:getBestStartGear(self.currentGears)
1875 if maxFactorGroup < self.activeGearGroupIndex then
1876 self:setGearGroup(maxFactorGroup)
1877 end
1878 end
1879 end
1880 end
1881 end
1882 end
1883 newGear = math.min(math.max(newGear, 1), #self.currentGears)
1884 end
1885 end
1886
1887 -- prevent transmission from downshifting when it just upshifted. So at least try the new gear for 3sec, maybe we get the rpm higher
1888 self.allowGearChangeTimer = self.allowGearChangeTimer - dt
1889 if self.allowGearChangeTimer > 0 and acceleratorPedal * self.currentDirection > 0 then
1890 if newGear < self.gear then
1891 if self.allowGearChangeDirection ~= MathUtil.sign(newGear-self.gear) then
1892 --log("prevent from shifting again in the other direction", self.allowGearChangeDirection, newGear, self.gear)
1893 newGear = self.gear
1894 end
1895 end
1896 end
1897 end
1898 end
1899 end
1900 if newGear ~= self.gear or forceGearChange then
1901 if newGear ~= self.bestGearSelected then
1902 self.bestGearSelected = -1
1903 end
1904
1905 self.targetGear = newGear
1906 self.previousGear = self.gear
1907 self.gear = 0
1908 self.minGearRatio = 0
1909 self.maxGearRatio = 0
1910 self.autoGearChangeTimer = self.autoGearChangeTime
1911 self.gearChangeTimer = self.gearChangeTime
1912 self.lastGearChangeTime = g_time
1913 adjAcceleratorPedal = 0
1914
1915 local directionMultiplier = self.directionChangeUseGear and self.currentDirection or 1
1916 SpecializationUtil.raiseEvent(self.vehicle, "onGearChanged", self.gear * directionMultiplier, self.targetGear * directionMultiplier, self.gearChangeTimer)
1917
1918 if self.gearChangeTimer == 0 then
1919 self.gearChangeTimer = -1
1920 self.allowGearChangeTimer = 3000
1921 self.allowGearChangeDirection = MathUtil.sign(self.targetGear-self.previousGear)
1922
1923 self:applyTargetGear()
1924 end
1925 end
1926 end
1927
1928 if self.gearShiftMode == VehicleMotor.SHIFT_MODE_MANUAL_CLUTCH then
1929 if self.backwardGears or self.forwardGears then
1930 local curRatio, tarRatio
1931 if self.currentGears[self.gear] ~= nil then
1932 tarRatio = self.currentGears[self.gear].ratio * self:getGearRatioMultiplier()
1933 curRatio = math.min(self.motorRotSpeed / self.differentialRotSpeed, 5000)
1934 end
1935
1936 local ratio = 0
1937 if tarRatio ~= nil then
1938 ratio = MathUtil.lerp(math.abs(tarRatio), math.abs(curRatio), math.min(self.manualClutchValue, 0.9) / 0.9 * 0.5) * MathUtil.sign(tarRatio)
1939 end
1940 self.minGearRatio, self.maxGearRatio = ratio, ratio
1941
1942 if self.manualClutchValue == 0 and self.maxGearRatio ~= 0 then
1943 local factor = (self:getClutchRotSpeed() * 30 / math.pi + 50) / self:getNonClampedMotorRpm()
1944
1945 if factor < 0.2 then
1946 self.stallTimer = self.stallTimer + dt
1947
1948 if self.stallTimer > 500 then
1949 self.vehicle:stopMotor()
1950 self.stallTimer = 0
1951 end
1952 else
1953 self.stallTimer = 0
1954 end
1955 else
1956 self.stallTimer = 0
1957 end
1958 end
1959 end
1960
1961 if self:getUseAutomaticGearShifting() then
1962 if math.abs(self.vehicle.lastSpeed) > 0.0003 then
1963 if self.backwardGears or self.forwardGears then
1964 if (self.currentDirection > 0 and adjAcceleratorPedal < 0) -- driving forwards and braking
1965 or (self.currentDirection < 0 and adjAcceleratorPedal > 0) then -- driving backwards and braking
1966 adjAcceleratorPedal = 0
1967 brakePedal = 1
1968 end
1969 end
1970 end
1971 end
1972
1973 return adjAcceleratorPedal, brakePedal
1974end

updateSmoothLoadPercentage

Description
Update smoothed motor load percentage
Definition
updateSmoothLoadPercentage(float dt, float rawLoadPercentage)
Arguments
floatdttime since last update
floatrawLoadPercentageraw load percentage from motor
Code
1105function VehicleMotor:updateSmoothLoadPercentage(dt, rawLoadPercentage)
1106 local lastSmoothedLoad = self.smoothedLoadPercentage
1107
1108 local maxSpeed = self:getMaximumForwardSpeed() * 3.6
1109 if self.vehicle.movingDirection < 0 then
1110 maxSpeed = self:getMaximumBackwardSpeed() * 3.6
1111 end
1112
1113 local speedPercentage = math.max(math.min(self.vehicle:getLastSpeed() / maxSpeed, 1), 0)
1114
1115 -- adjustment factor is 0.65 at min speed and 0.05 at max speed (so we counteract fast acceleration changes at max speed)
1116 local factor = 0.05 + (1 - speedPercentage) * 0.3
1117 if rawLoadPercentage < self.smoothedLoadPercentage then
1118 if self.gearType ~= VehicleMotor.TRANSMISSION_TYPE.POWERSHIFT or (g_time - self.lastGearChangeTime) > 200 then
1119 factor = factor * 0.2
1120
1121 -- rapid load drop while clutch is engaged
1122 if self:getClutchPedal() > 0.75 then
1123 factor = factor * 5
1124 end
1125
1126 -- if we are decelerating we want to have -1 load immediately
1127 if rawLoadPercentage < 0 then
1128 factor = factor * 2.5
1129 end
1130 else
1131 -- while power shifting we reduce the load drop to have almost no load difference, only rpm difference
1132 factor = factor * 0.05
1133 end
1134 end
1135 local invFactor = 1 - factor
1136
1137 self.smoothedLoadPercentage = invFactor * self.smoothedLoadPercentage + factor * rawLoadPercentage
1138
1139 -- load change charge that is increased while the load inceases really fast and fades out afterwards
1140 -- used to drop the motor rpm
1141 local difference = math.max(self.smoothedLoadPercentage - lastSmoothedLoad, 0)
1142 self.loadPercentageChangeCharge = self.loadPercentageChangeCharge + difference
1143 self.loadPercentageChangeCharge = math.min(math.max(self.loadPercentageChangeCharge - dt * 0.0005, 0), 1)
1144end

updateStartGearValues

Description
Update the gear start parameters
Definition
updateStartGearValues(table gears)
Arguments
tablegearsgears
Return Values
integerbestGearbest gear to start
Code
1150function VehicleMotor:updateStartGearValues(dt)
1151 local totalMass = self.vehicle:getTotalMass()
1152 local totalMassOnGround = 0 -- total mass of vehicles with wheels, excluding self.vehicle
1153 local vehicleMass = self.vehicle:getTotalMass(true)
1154
1155 -- if the mass changes faster than 10kg/sec
1156 if math.abs(totalMass - self.startGearValues.lastMass) > 0.00001 * dt then
1157 self.startGearValues.lastMass = totalMass
1158 self.idleGearChangeTimer = 500 -- block gear changing while mass is changing (e.g. while vehicle is filling)
1159 end
1160
1161 -- get maxForce sum of all tools attached and calculate the mass of all wheeled tools
1162 local maxForce = 0
1163 local vehicles = self.vehicle:getChildVehicles()
1164 for _, vehicle in ipairs(vehicles) do
1165 if vehicle ~= self.vehicle then
1166 if vehicle.spec_powerConsumer ~= nil then
1167 if vehicle.spec_powerConsumer.maxForce ~= nil then
1168 local multiplier = vehicle:getPowerMultiplier()
1169 if multiplier ~= 0 then
1170 maxForce = maxForce + vehicle.spec_powerConsumer.maxForce
1171 end
1172 end
1173 end
1174
1175 if vehicle.spec_leveler ~= nil then
1176 maxForce = maxForce + math.abs(vehicle.spec_leveler.lastForce)
1177 end
1178
1179 if vehicle.spec_wheels ~= nil then
1180 if #vehicle.spec_wheels.wheels > 0 then
1181 totalMassOnGround = totalMassOnGround + vehicle:getTotalMass(true)
1182 end
1183 end
1184 end
1185 end
1186
1187 -- get average center of mass and direction of all attachments that got wheels and
1188 local comX, comY, comZ = 0, 0, 0
1189 local dirX, dirY, dirZ = 0, 0, 0
1190 for _, vehicle in ipairs(vehicles) do
1191 if vehicle ~= self.vehicle then
1192 if vehicle.spec_wheels ~= nil then
1193 if #vehicle.spec_wheels.wheels > 0 then
1194 local objectMass = vehicle:getTotalMass(true)
1195 local percentage = objectMass / totalMassOnGround
1196 local cx, cy, cz = vehicle:getOverallCenterOfMass()
1197 comX, comY, comZ = comX + cx * percentage, comY + cy * percentage, comZ + cz * percentage
1198
1199 local iDirX, iDirY, iDirZ = vehicle:getVehicleWorldDirection()
1200 dirX, dirY, dirZ = dirX + iDirX * percentage, dirY + iDirY * percentage, dirZ + iDirZ * percentage
1201 end
1202 end
1203 end
1204 end
1205
1206 local vdx, vdy, vdz = self.vehicle:getVehicleWorldDirection()
1207
1208 if VehicleDebug.state == VehicleDebug.DEBUG_TRANSMISSION then
1209 local vX, vY, vZ = getWorldTranslation(self.vehicle.components[1].node)
1210 DebugUtil.drawDebugGizmoAtWorldPos(comX, comY, comZ, dirX, dirY, dirZ, 0, 1, 0, "TOOLS DIR", false)
1211 DebugUtil.drawDebugGizmoAtWorldPos(vX, vY, vZ, vdx, vdy, vdz, 0, 1, 0, "VEHICLE DIR", false)
1212 end
1213
1214 -- increase the calculated mass depending in which angle the tools are to the motorized vehicle
1215 -- X&Z difference normal influence and the Y difference has a higher (factor 6.6) influence
1216 local diffXZ, diffY = 0, 0
1217 if dirX ~= 0 or dirY ~= 0 or dirZ ~= 0 then
1218 diffXZ = math.max(math.abs(dirX-vdx), math.abs(dirZ-vdz))
1219
1220 -- only use positive direction offset if trailer is "below" the tractor
1221 diffY = math.max(dirY-vdy, 0)
1222 end
1223
1224 -- full direction mass incluence is only used while additional weight is 5 tons
1225 -- so for really light tools it has more or less no impact since the tractor can also easily pull them out of there position
1226 local massDirectionInfluenceFactor = math.min((totalMass - vehicleMass) / 5, 1)
1227
1228 self.startGearValues.massDirectionDifferenceXZ = diffXZ
1229 self.startGearValues.massDirectionDifferenceY = diffY
1230 self.startGearValues.massDirectionFactor = (1 + diffXZ * massDirectionInfluenceFactor) * (1 + (diffY / 0.15) * massDirectionInfluenceFactor)
1231
1232 -- we use directly the PowerConsumer functions since the external torque is 0 while the clutch is engaged
1233 local neededPtoTorque = PowerConsumer.getTotalConsumedPtoTorque(self.vehicle, nil, nil, true) / self:getPtoMotorRpmRatio()
1234 local ptoPower = self.peakMotorPowerRotSpeed * neededPtoTorque
1235 self.startGearValues.availablePower = self.peakMotorPower - ptoPower
1236
1237 -- if we have a tool force active we increase it while the pto consumes power
1238 local maxForcePowerFactor = 1 + (ptoPower / self.peakMotorPower) * 0.75
1239
1240 -- max force is influencing the mass to tow by factor 2 in tons
1241 local mass = (totalMass + (maxForce * maxForcePowerFactor)) / vehicleMass
1242 mass = ((mass - 1) * 0.5 + 1) * vehicleMass
1243
1244 self.startGearValues.maxForce = maxForce
1245 self.startGearValues.mass = mass
1246 self.startGearValues.slope = self.vehicle:getVehicleWorldXRot()
1247
1248 self.startGearValues.massFactor = (self.startGearValues.mass * self.startGearValues.massDirectionFactor) / (((self.startGearValues.availablePower/100-1)*50+100) * 0.4)
1249end

writeGearDataToStream

Description
Writes current gear data to stream
Definition
writeGearDataToStream()
Code
720function VehicleMotor:writeGearDataToStream(streamId)
721 streamWriteUIntN(streamId, MathUtil.sign(self.currentDirection) + 1, 2)
722
723 if streamWriteBool(streamId, self.backwardGears ~= nil or self.forwardGears ~= nil) then
724 streamWriteUIntN(streamId, self.targetGear, 6)
725 streamWriteBool(streamId, self.targetGear ~= self.gear)
726 streamWriteBool(streamId, self.currentGears == self.forwardGears)
727
728 if self.gearGroups ~= nil then
729 streamWriteUIntN(streamId, self.activeGearGroupIndex, 5)
730 end
731 end
732end