270 lines
9.1 KiB
Kotlin
270 lines
9.1 KiB
Kotlin
package com.example.esp32aldldashboard.parser
|
|
|
|
data class ALDLFrame(
|
|
val rawBytes: ByteArray,
|
|
val iacPosition: Int,
|
|
val coolantTempC: Float,
|
|
val coolantTempF: Float,
|
|
val vehicleSpeedMPH: Int,
|
|
val mapVolts: Float,
|
|
val mapKpa: Float,
|
|
val engineSpeedRpm: Int,
|
|
val tpsVolts: Float,
|
|
val integrator: Int,
|
|
val o2SensorMv: Float,
|
|
val batteryVolts: Float,
|
|
val blm: Int,
|
|
val richLeanCrosses: Int,
|
|
val sparkAdvance: Float,
|
|
val egrDutyCycle: Float,
|
|
val matC: Float,
|
|
val matF: Float,
|
|
val bpwMs: Float,
|
|
val blmEnable: Boolean,
|
|
val quasiPulse: Boolean,
|
|
val asyncPulse: Boolean,
|
|
val isRich: Boolean,
|
|
val isClosedLoop: Boolean,
|
|
val isAcEnabled: Boolean,
|
|
val isParkNeutral: Boolean,
|
|
val isAcClutchEnabled: Boolean,
|
|
val isTccLocked: Boolean,
|
|
val isPowerSteeringCrampActive: Boolean,
|
|
val activeFaultCodes: List<Int>,
|
|
val timestamp: Long = System.currentTimeMillis()
|
|
) {
|
|
override fun equals(other: Any?): Boolean {
|
|
if (this === other) return true
|
|
if (javaClass != other?.javaClass) return false
|
|
other as ALDLFrame
|
|
return rawBytes.contentEquals(other.rawBytes)
|
|
}
|
|
|
|
override fun hashCode(): Int {
|
|
return rawBytes.contentHashCode()
|
|
}
|
|
}
|
|
|
|
object ALDLParser {
|
|
// MAT C interpolation table: key is raw value, value is Temp in C
|
|
private val matTableC = listOf(
|
|
0 to 200.0f,
|
|
12 to 150.0f,
|
|
13 to 145.0f,
|
|
14 to 140.0f,
|
|
16 to 135.0f,
|
|
18 to 130.0f,
|
|
21 to 125.0f,
|
|
23 to 120.0f,
|
|
26 to 115.0f,
|
|
30 to 110.0f,
|
|
34 to 105.0f,
|
|
39 to 100.0f,
|
|
44 to 95.0f,
|
|
50 to 90.0f,
|
|
56 to 85.0f,
|
|
64 to 80.0f,
|
|
72 to 75.0f,
|
|
81 to 70.0f,
|
|
92 to 65.0f,
|
|
102 to 60.0f,
|
|
114 to 55.0f,
|
|
126 to 50.0f,
|
|
139 to 45.0f,
|
|
152 to 40.0f,
|
|
165 to 35.0f,
|
|
177 to 30.0f,
|
|
189 to 25.0f,
|
|
199 to 20.0f,
|
|
209 to 15.0f,
|
|
218 to 10.0f,
|
|
225 to 5.0f,
|
|
231 to 0.0f,
|
|
237 to -5.0f,
|
|
241 to -10.0f,
|
|
245 to -15.0f,
|
|
247 to -20.0f,
|
|
250 to -25.0f,
|
|
251 to -30.0f,
|
|
255 to -40.0f
|
|
)
|
|
|
|
// MAT F interpolation table: key is raw value, value is Temp in F
|
|
private val matTableF = listOf(
|
|
0 to 392.0f,
|
|
12 to 302.0f,
|
|
13 to 293.0f,
|
|
14 to 284.0f,
|
|
16 to 275.0f,
|
|
18 to 266.0f,
|
|
21 to 257.0f,
|
|
23 to 248.0f,
|
|
26 to 239.0f,
|
|
30 to 230.0f,
|
|
34 to 221.0f,
|
|
39 to 212.0f,
|
|
44 to 203.0f,
|
|
50 to 194.0f,
|
|
56 to 185.0f,
|
|
64 to 176.0f,
|
|
72 to 167.0f,
|
|
81 to 158.0f,
|
|
92 to 149.0f,
|
|
102 to 140.0f,
|
|
114 to 131.0f,
|
|
126 to 122.0f,
|
|
139 to 113.0f,
|
|
152 to 104.0f,
|
|
165 to 95.0f,
|
|
177 to 86.0f,
|
|
189 to 77.0f,
|
|
199 to 68.0f,
|
|
209 to 59.0f,
|
|
218 to 50.0f,
|
|
225 to 41.0f,
|
|
231 to 32.0f,
|
|
237 to 23.0f,
|
|
241 to 14.0f,
|
|
245 to 5.0f,
|
|
247 to -4.0f,
|
|
250 to -13.0f,
|
|
251 to -22.0f,
|
|
255 to -40.0f
|
|
)
|
|
|
|
private fun interpolate(raw: Int, table: List<Pair<Int, Float>>): Float {
|
|
if (raw <= table.first().first) return table.first().second
|
|
if (raw >= table.last().first) return table.last().second
|
|
for (i in 0 until table.size - 1) {
|
|
val current = table[i]
|
|
val next = table[i + 1]
|
|
if (raw >= current.first && raw <= next.first) {
|
|
val span = next.first - current.first
|
|
if (span == 0) return current.second
|
|
val t = (raw - current.first).toFloat() / span
|
|
return current.second + t * (next.second - current.second)
|
|
}
|
|
}
|
|
return 0.0f
|
|
}
|
|
|
|
/**
|
|
* Parses a 25-byte raw data payload.
|
|
*/
|
|
fun parseFrame(data: ByteArray): ALDLFrame? {
|
|
if (data.size != 25) return null
|
|
|
|
val u = IntArray(25) { data[it].toInt() and 0xFF }
|
|
|
|
// Mappings based on 1-indexed btByteNumber in 24-INT10.ads (index = byteNumber - 1)
|
|
val iacPosition = u[3] // Byte 4
|
|
val coolantTempC = u[4] * 0.75f - 40.0f // Byte 5
|
|
val coolantTempF = u[4] * 1.35f - 40.0f // Byte 5
|
|
val vehicleSpeedMPH = u[5] // Byte 6
|
|
val mapVolts = u[6] * 0.019608f // Byte 7
|
|
val mapKpa = u[6] * 0.369f + 10.354f // Byte 7
|
|
val engineSpeedRpm = u[7] * 25 // Byte 8
|
|
val tpsVolts = u[8] * 0.019608f // Byte 9
|
|
val integrator = u[9] // Byte 10
|
|
val o2SensorMv = u[10] * 4.44f // Byte 11
|
|
|
|
val codesByte1 = u[11] // Byte 12
|
|
val codesByte2 = u[12] // Byte 13
|
|
val codesByte3 = u[13] // Byte 14
|
|
val miscByte1 = u[14] // Byte 15
|
|
val miscByte2 = u[15] // Byte 16
|
|
val miscByte3 = u[16] // Byte 17
|
|
|
|
val batteryVolts = u[17] * 0.1f // Byte 18
|
|
val blm = u[18] // Byte 19
|
|
val richLeanCrosses = u[19] // Byte 20
|
|
val sparkAdvance = u[20] * 0.351563f // Byte 21
|
|
val egrDutyCycle = u[21] * 0.392157f // Byte 22
|
|
|
|
// MAT (Air Temp) Interpolation
|
|
val matC = interpolate(u[22], matTableC) // Byte 23
|
|
val matF = interpolate(u[22], matTableF) // Byte 23
|
|
|
|
// BPW (Base Pulse Width) 16-bit
|
|
val rawBpw = (u[23] shl 8) or u[24] // Byte 24 (High), Byte 25 (Low)
|
|
val bpwMs = rawBpw * 0.015259f
|
|
|
|
// Status Flags Decoding
|
|
// Misc Byte 1 (Byte 15)
|
|
val blmEnable = (miscByte1 and 0x02) != 0 // bit 1
|
|
val quasiPulse = (miscByte1 and 0x08) != 0 // bit 3
|
|
val asyncPulse = (miscByte1 and 0x10) != 0 // bit 4
|
|
val isRich = (miscByte1 and 0x40) != 0 // bit 6 (1=RICH, 0=LEAN)
|
|
val isClosedLoop = (miscByte1 and 0x80) != 0 // bit 7 (1=CLOSED, 0=OPEN)
|
|
|
|
// Misc Byte 2 (Byte 16)
|
|
val isAcEnabled = (miscByte2 and 0x20) == 0 // bit 5 (0=ENABLED, 1=DISABLED/IDLE)
|
|
val isParkNeutral = (miscByte2 and 0x80) != 0 // bit 7 (1=PARK/NEUTRAL, 0=IN GEAR)
|
|
|
|
// Misc Byte 3 (Byte 17)
|
|
val isAcClutchEnabled = (miscByte3 and 0x01) != 0 // bit 0 (1=ENABLED)
|
|
val isTccLocked = (miscByte3 and 0x04) != 0 // bit 2 (1=LOCKED)
|
|
val isPowerSteeringCrampActive = (miscByte3 and 0x20) != 0 // bit 5 (1=ACTIVE)
|
|
|
|
// Active Fault Codes list based on code bits
|
|
val activeCodes = mutableListOf<Int>()
|
|
// Byte 12
|
|
if ((codesByte1 and 0x80) != 0) activeCodes.add(12) // bit 7
|
|
if ((codesByte1 and 0x40) != 0) activeCodes.add(13) // bit 6
|
|
if ((codesByte1 and 0x20) != 0) activeCodes.add(14) // bit 5
|
|
if ((codesByte1 and 0x10) != 0) activeCodes.add(15) // bit 4
|
|
if ((codesByte1 and 0x08) != 0) activeCodes.add(21) // bit 3
|
|
if ((codesByte1 and 0x04) != 0) activeCodes.add(22) // bit 2
|
|
if ((codesByte1 and 0x02) != 0) activeCodes.add(23) // bit 1
|
|
if ((codesByte1 and 0x01) != 0) activeCodes.add(24) // bit 0
|
|
// Byte 13
|
|
if ((codesByte2 and 0x80) != 0) activeCodes.add(25) // bit 7
|
|
if ((codesByte2 and 0x20) != 0) activeCodes.add(32) // bit 5
|
|
if ((codesByte2 and 0x10) != 0) activeCodes.add(33) // bit 4
|
|
if ((codesByte2 and 0x08) != 0) activeCodes.add(34) // bit 3
|
|
if ((codesByte2 and 0x04) != 0) activeCodes.add(35) // bit 2
|
|
if ((codesByte2 and 0x01) != 0) activeCodes.add(42) // bit 0
|
|
// Byte 14
|
|
if ((codesByte3 and 0x80) != 0) activeCodes.add(43) // bit 7
|
|
if ((codesByte3 and 0x40) != 0) activeCodes.add(44) // bit 6
|
|
if ((codesByte3 and 0x20) != 0) activeCodes.add(45) // bit 5
|
|
if ((codesByte3 and 0x10) != 0) activeCodes.add(51) // bit 4
|
|
if ((codesByte3 and 0x08) != 0) activeCodes.add(52) // bit 3
|
|
if ((codesByte3 and 0x04) != 0) activeCodes.add(53) // bit 2
|
|
if ((codesByte3 and 0x01) != 0) activeCodes.add(55) // bit 0
|
|
|
|
return ALDLFrame(
|
|
rawBytes = data,
|
|
iacPosition = iacPosition,
|
|
coolantTempC = coolantTempC,
|
|
coolantTempF = coolantTempF,
|
|
vehicleSpeedMPH = vehicleSpeedMPH,
|
|
mapVolts = mapVolts,
|
|
mapKpa = mapKpa,
|
|
engineSpeedRpm = engineSpeedRpm,
|
|
tpsVolts = tpsVolts,
|
|
integrator = integrator,
|
|
o2SensorMv = o2SensorMv,
|
|
batteryVolts = batteryVolts,
|
|
blm = blm,
|
|
richLeanCrosses = richLeanCrosses,
|
|
sparkAdvance = sparkAdvance,
|
|
egrDutyCycle = egrDutyCycle,
|
|
matC = matC,
|
|
matF = matF,
|
|
bpwMs = bpwMs,
|
|
blmEnable = blmEnable,
|
|
quasiPulse = quasiPulse,
|
|
asyncPulse = asyncPulse,
|
|
isRich = isRich,
|
|
isClosedLoop = isClosedLoop,
|
|
isAcEnabled = isAcEnabled,
|
|
isParkNeutral = isParkNeutral,
|
|
isAcClutchEnabled = isAcClutchEnabled,
|
|
isTccLocked = isTccLocked,
|
|
isPowerSteeringCrampActive = isPowerSteeringCrampActive,
|
|
activeFaultCodes = activeCodes
|
|
)
|
|
}
|
|
}
|