I've recently discovered how to add math channels to the E46 standard script that I have been using and wanted to share them here. The calculations have mostly come from the book "analysis techniques for racecar data acquisition" by Jorge Segers. I've condensed some of the calculations and scaling factors to reduce the length of the equations and so it may not be completely clear from the script.I have not tested these out on a track yet, just driving around the block. I make no claims to their accuracy or usefulness.
I will copy the whole script in the next post but wanted to explain each of the math channels individually. The channel Id should be added to you list of channels at the top of the script.
The calculation should be added after the "processCAN(CAN_chan)" line which can be found immediatly after the can map section.
i.e.
function onTick()
processCAN(CAN_chan)
ADD MATH CHANNELS HERE
end
Gear Calculator. This is for the E46 M3 with standard diff ratio (3.62)
gearId=addChannel("Gear",10,0,0,6)
local a1=getChannel(rpmId)
local a2=getChannel(clutchId)
local a3=getChannel(gearId)
local w3=getChannel(lrWheelId)
if a2<0.5 then
setChannel(gearId,(w3/a1)*240)
else
setChannel(gearId,a3)
end
This channel works by dividing the wheel speed by the rpm and multiplying that value by 240. Using no decimal places it matches the gear position. The gear calculator runs when the clutch is engaged. When the clutch is disengaged, the gear calculator remembers the last position. This will prevent erroneous numbers when blipping the throttle on a down shift.
Wheel Longitudinal slip.
This channel will calculate the forward/aft slip of the individual wheels. This will measure wheelspin during acceleration and wheel lockup under braking.
Channels:
lfslipId=addChannel("LFWheelSlip",10,1,0,100,"%")
rfslipId=addChannel("RFWheelSlip",10,1,0,100,"%")
lrslipId=addChannel("LRWheelSlip",10,1,0,100,"%")
rrslipId=addChannel("RRWheelSlip",10,1,0,100,"%")
Calculation:
local w1=getChannel(lfWheelId)
local w2=getChannel(rfWheelId)
local w3=getChannel(lrWheelId)  not needed here is using with gear calculator above.
local w4=getChannel(rrWheelId)
if w4 > 10 then
setChannel(lfslipId,100(50*(w3+w4))/w1)
setChannel(rfslipId,100(50*(w3+w4))/w2)
setChannel(lrslipId,100(50*(w1+w2))/w3)
setChannel(rrslipId,100(50*(w1+w2))/w4)
end
This one works by tacking the average speed of the wheels on one axel and comparing that to the speed of a wheel on the other axel. The calculation is only performed at greater than 10 mph otherwise it provide lager slip angles when speeds are low. I also increased the wheel speed channel to 1 decimal place to get better accuracy with these calculations.
Sharing Math Channels for E46 M3

 Posts: 27
 Joined: Thu Apr 09, 2015 8:18 pm
The brakeG channel is to separate out the areodynamic drag force from the decceleration GFroce to leave just the braking effort. This channel was created as there is no brake pressure channel on the CANBUS. The math is from this website http://www.jameshakewill.com/braketrace.pdf
The totalG channel is to assess the transition from braking to turing to ensure.
brakegId=addChannel("BrakeG",10,2,0,1.5,"G")
totalgId=addChannel("TotalG",10,2,0,1.5,"G")
local g1=getImu(0)
local u3=getGpsSpeed()
local b2=(1*g1)((u3^2/10000)
if b2>0 then
setChannel(brakegId,b2)
end
local g2=getImu(1)
local g3=((g1^2)+(g2^2))^(0.5)
setChannel(totalgId,g3)
And finally Calculated Power. This uses a lot of numbers about the car that may be slightly different on mine than others. For example, this calculation uses the weight (3500lbs including me), tyre size (255/40/17 for me) as well as Cd, frontal, area, gear ratios and a few other things that i have probably forgotten. There were a lot of individual calculations that I tried to reduce to the bare minimum to keep the code as small as possible.
Channel:
powerId=addChannel("power",10,0,0,400,"WHP")
Calculation:
local p1=468.2+(0.080845*(u3)^2)
local p2=(16232+0.22125*(a1/u3)^2)*g1
if g1>0 then
setChannel(powerId,(p1+p2)*(u3/1677.85))
end
The totalG channel is to assess the transition from braking to turing to ensure.
brakegId=addChannel("BrakeG",10,2,0,1.5,"G")
totalgId=addChannel("TotalG",10,2,0,1.5,"G")
local g1=getImu(0)
local u3=getGpsSpeed()
local b2=(1*g1)((u3^2/10000)
if b2>0 then
setChannel(brakegId,b2)
end
local g2=getImu(1)
local g3=((g1^2)+(g2^2))^(0.5)
setChannel(totalgId,g3)
And finally Calculated Power. This uses a lot of numbers about the car that may be slightly different on mine than others. For example, this calculation uses the weight (3500lbs including me), tyre size (255/40/17 for me) as well as Cd, frontal, area, gear ratios and a few other things that i have probably forgotten. There were a lot of individual calculations that I tried to reduce to the bare minimum to keep the code as small as possible.
Channel:
powerId=addChannel("power",10,0,0,400,"WHP")
Calculation:
local p1=468.2+(0.080845*(u3)^2)
local p2=(16232+0.22125*(a1/u3)^2)*g1
if g1>0 then
setChannel(powerId,(p1+p2)*(u3/1677.85))
end

 Posts: 27
 Joined: Thu Apr 09, 2015 8:18 pm
tickRate=30
CAN_baud=500000
CAN_chan=0
tpsId=addChannel("TPS",10,0,0,100,"%")
coolantId=addChannel("Coolant",1,0,0,255,"C")
oilTempId=addChannel("OilTemp",1,0,0,255,"C")
rpmId=addChannel("RPM",10,1,0,10000)
steerId=addChannel("Steering",10,1,360,360,"Deg.")
brakeId=addChannel("BrakeSw",10,0,0,1)
clutchId=addChannel("Clutch",10,0,0,1)
lfWheelId=addChannel("LFWheelSpd",10,1,0,200,"MPH")
rfWheelId=addChannel("RFWheelSpd",10,1,0,200,"MPH")
lrWheelId=addChannel("LRWheelSpd",10,1,0,200,"MPH")
rrWheelId=addChannel("RRWheelSpd",10,1,0,200,"MPH")
fuelId=addChannel("Fuel",1,0,0,100,"%")
extTempId=addChannel("ExtTemp",1,0,0,120,"F")
gearId=addChannel("Gear",10,0,0,6)
lfslipId=addChannel("LFWheelSlip",10,1,0,100,"%")
rfslipId=addChannel("RFWheelSlip",10,1,0,100,"%")
lrslipId=addChannel("LRWheelSlip",10,1,0,100,"%")
rrslipId=addChannel("RRWheelSlip",10,1,0,100,"%")
brakegId=addChannel("BrakeG",10,2,0,1.5,"G")
totalgId=addChannel("TotalG",10,2,0,1.5,"G")
powerId=addChannel("power",10,0,0,400,"WHP")
function rpmFilter(value)
if value > 500 then startLogging() else stopLogging() end
return value
end
function brakeFilter(value)
return bit.rshift(bit.band(value,0x10),4)
end
function clutchFilter(value)
return bit.band(value,0x01)
end
function processWheel(id,data,offset)
local highByte = bit.band(data[offset+2], 0x1F)
local lowByte = data[offset+1]
local value = highByte*256+lowByte
value = (value*0.0388357)0.3
setChannel(id, value)
end
function processSteering(data)
local steer=0
if data[2]>127 then
steer=1*(((data[2]128)*256)+data[1])
else
steer=(data[2]*256)+data[1]
end
setChannel(steerId,(steer*0.045))
end
function fuelFilter(value)
value = bit.band(value,0x7F)
return value/0.6283
end
function extTempFilter(value)
local temp = bit.band(value,0x7F)
if value>127 then
temp =1*temp
end
end
CAN_map = {
[496] = function(data) processWheel(lfWheelId,data,0) processWheel(rfWheelId,data,2) processWheel(lrWheelId,data,4) processWheel(rrWheelId,data,6) end,
[339] = function(data) map_chan(brakeId,data,0,1,1,0,brakeFilter) end,
[809] = function(data) map_chan(tpsId,data,5,1,0.392156863,0) map_chan(coolantId,data,1,1,0.75,48) map_chan(clutchId,data,3,1,1,0,clutchFilter) map_chan(cruiseId,data,3,1,1,0) end,
[1349] = function(data) map_chan(oilTempId,data,4,1,1,48) end,
[790] = function(data) map_chan(rpmId,data,2,2,0.15625,0,rpmFilter) end,
[501] = function (data) processSteering(data) end,
[1555] = function (data) map_chan(fuelId,data,2,1,1,0,fuelFilter) end,
[1557] = function (data) map_chan(extTempId,data,3,1,1,0,extTempFilter) end
}
function onTick()
processCAN(CAN_chan)
local a1=getChannel(rpmId)
local a2=getChannel(clutchId)
local a3=getChannel(gearId)
local w3=getChannel(lrWheelId)
if a2<0.5 then
setChannel(gearId,(w3/a1)*240)
else
setChannel(gearId,a3)
end
local w1=getChannel(lfWheelId)
local w2=getChannel(rfWheelId)
local w4=getChannel(rrWheelId)
if w4 > 10 then
setChannel(lfslipId,100(50*(w3+w4))/w1)
setChannel(rfslipId,100(50*(w3+w4))/w2)
setChannel(lrslipId,100(50*(w1+w2))/w3)
setChannel(rrslipId,100(50*(w1+w2))/w4)
end
local g1=getImu(0)
local u3=getGpsSpeed()
local b2=(1*g1)((u3^2/10000)
if b2>0 then
setChannel(brakegId,b2)
end
local g2=getImu(1)
local g3=((g1^2)+(g2^2))^(0.5)
setChannel(totalgId,g3)
local p1=468.2+(0.080845*(u3)^2)
local p2=(16232+0.22125*(a1/u3)^2)*g1
if g1>0 then
setChannel(powerId,(p1+p2)*(u3/1677.85))
end
end
function processCAN(chan)
local msg = 0
repeat
local id, e, data = rxCAN(chan, 0)
if id ~= nil then
local map = CAN_map[id]
if map ~= nil then
map(data)
end
end
msg = msg+1
until id == nil or msg>100
end
function map_chan(cid, data, offset, len, mult, add, filter)
if offset + len > #data then return end
offset = offset + 1
local value=0
local shift=1
while len>0 do
value=value+(data[offset]*shift)
shift=shift*256
offset=offset+1
len=len1
end
local cv = value * mult + add
if filter ~= nil then cv = filter(cv) end
setChannel(cid, cv)
end
initCAN(CAN_chan, CAN_baud)
setTickRate(tickRate)
CAN_baud=500000
CAN_chan=0
tpsId=addChannel("TPS",10,0,0,100,"%")
coolantId=addChannel("Coolant",1,0,0,255,"C")
oilTempId=addChannel("OilTemp",1,0,0,255,"C")
rpmId=addChannel("RPM",10,1,0,10000)
steerId=addChannel("Steering",10,1,360,360,"Deg.")
brakeId=addChannel("BrakeSw",10,0,0,1)
clutchId=addChannel("Clutch",10,0,0,1)
lfWheelId=addChannel("LFWheelSpd",10,1,0,200,"MPH")
rfWheelId=addChannel("RFWheelSpd",10,1,0,200,"MPH")
lrWheelId=addChannel("LRWheelSpd",10,1,0,200,"MPH")
rrWheelId=addChannel("RRWheelSpd",10,1,0,200,"MPH")
fuelId=addChannel("Fuel",1,0,0,100,"%")
extTempId=addChannel("ExtTemp",1,0,0,120,"F")
gearId=addChannel("Gear",10,0,0,6)
lfslipId=addChannel("LFWheelSlip",10,1,0,100,"%")
rfslipId=addChannel("RFWheelSlip",10,1,0,100,"%")
lrslipId=addChannel("LRWheelSlip",10,1,0,100,"%")
rrslipId=addChannel("RRWheelSlip",10,1,0,100,"%")
brakegId=addChannel("BrakeG",10,2,0,1.5,"G")
totalgId=addChannel("TotalG",10,2,0,1.5,"G")
powerId=addChannel("power",10,0,0,400,"WHP")
function rpmFilter(value)
if value > 500 then startLogging() else stopLogging() end
return value
end
function brakeFilter(value)
return bit.rshift(bit.band(value,0x10),4)
end
function clutchFilter(value)
return bit.band(value,0x01)
end
function processWheel(id,data,offset)
local highByte = bit.band(data[offset+2], 0x1F)
local lowByte = data[offset+1]
local value = highByte*256+lowByte
value = (value*0.0388357)0.3
setChannel(id, value)
end
function processSteering(data)
local steer=0
if data[2]>127 then
steer=1*(((data[2]128)*256)+data[1])
else
steer=(data[2]*256)+data[1]
end
setChannel(steerId,(steer*0.045))
end
function fuelFilter(value)
value = bit.band(value,0x7F)
return value/0.6283
end
function extTempFilter(value)
local temp = bit.band(value,0x7F)
if value>127 then
temp =1*temp
end
end
CAN_map = {
[496] = function(data) processWheel(lfWheelId,data,0) processWheel(rfWheelId,data,2) processWheel(lrWheelId,data,4) processWheel(rrWheelId,data,6) end,
[339] = function(data) map_chan(brakeId,data,0,1,1,0,brakeFilter) end,
[809] = function(data) map_chan(tpsId,data,5,1,0.392156863,0) map_chan(coolantId,data,1,1,0.75,48) map_chan(clutchId,data,3,1,1,0,clutchFilter) map_chan(cruiseId,data,3,1,1,0) end,
[1349] = function(data) map_chan(oilTempId,data,4,1,1,48) end,
[790] = function(data) map_chan(rpmId,data,2,2,0.15625,0,rpmFilter) end,
[501] = function (data) processSteering(data) end,
[1555] = function (data) map_chan(fuelId,data,2,1,1,0,fuelFilter) end,
[1557] = function (data) map_chan(extTempId,data,3,1,1,0,extTempFilter) end
}
function onTick()
processCAN(CAN_chan)
local a1=getChannel(rpmId)
local a2=getChannel(clutchId)
local a3=getChannel(gearId)
local w3=getChannel(lrWheelId)
if a2<0.5 then
setChannel(gearId,(w3/a1)*240)
else
setChannel(gearId,a3)
end
local w1=getChannel(lfWheelId)
local w2=getChannel(rfWheelId)
local w4=getChannel(rrWheelId)
if w4 > 10 then
setChannel(lfslipId,100(50*(w3+w4))/w1)
setChannel(rfslipId,100(50*(w3+w4))/w2)
setChannel(lrslipId,100(50*(w1+w2))/w3)
setChannel(rrslipId,100(50*(w1+w2))/w4)
end
local g1=getImu(0)
local u3=getGpsSpeed()
local b2=(1*g1)((u3^2/10000)
if b2>0 then
setChannel(brakegId,b2)
end
local g2=getImu(1)
local g3=((g1^2)+(g2^2))^(0.5)
setChannel(totalgId,g3)
local p1=468.2+(0.080845*(u3)^2)
local p2=(16232+0.22125*(a1/u3)^2)*g1
if g1>0 then
setChannel(powerId,(p1+p2)*(u3/1677.85))
end
end
function processCAN(chan)
local msg = 0
repeat
local id, e, data = rxCAN(chan, 0)
if id ~= nil then
local map = CAN_map[id]
if map ~= nil then
map(data)
end
end
msg = msg+1
until id == nil or msg>100
end
function map_chan(cid, data, offset, len, mult, add, filter)
if offset + len > #data then return end
offset = offset + 1
local value=0
local shift=1
while len>0 do
value=value+(data[offset]*shift)
shift=shift*256
offset=offset+1
len=len1
end
local cv = value * mult + add
if filter ~= nil then cv = filter(cv) end
setChannel(cid, cv)
end
initCAN(CAN_chan, CAN_baud)
setTickRate(tickRate)
This is quite fantastic, thank you for sharing. How is the data working for you so far?
We are soon introducing the nextgen CAN mapping capabilities, which will offload the raw CAN mapping out of Lua, and allow Lua to do what it's best at  creating calculated / math channels, just like you've done here.
Video preview: https://www.youtube.com/watch?v=zc1Bi__0IDk
Thanks again,
We are soon introducing the nextgen CAN mapping capabilities, which will offload the raw CAN mapping out of Lua, and allow Lua to do what it's best at  creating calculated / math channels, just like you've done here.
Video preview: https://www.youtube.com/watch?v=zc1Bi__0IDk
Thanks again,

 Posts: 27
 Joined: Thu Apr 09, 2015 8:18 pm
Mustangkev, is your BrakeG code working? As written i don't think the value will update when the value swings negative and is set to zero. Mine looks like this:
This way setChannel is called no matter what.
Code: Select all
function updateBrakeG()
local accel_x = getImu(0)
local brake = accel_x  speed^2/50000 maybe 10000
if brake < 0 then
brake = 0
end
setChannel(brakeg_id, brake)
end

 Posts: 27
 Joined: Thu Apr 09, 2015 8:18 pm