Handle S300 alternate tail RPM frames
This commit is contained in:
parent
c2f92db217
commit
375c2510ae
@ -28,6 +28,10 @@ bool isS300PlaceholderFrame(Uint8List frame) {
|
|||||||
frame[16] == 0xC4;
|
frame[16] == 0xC4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasS300TailRpmPayload(Uint8List frame) {
|
||||||
|
return _s300TailRpmRaw(frame) != null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses a 128-byte S300 ECU response frame into a [SensorState].
|
/// Parses a 128-byte S300 ECU response frame into a [SensorState].
|
||||||
/// Throws [ArgumentError] if frame length is wrong, or if [validateChecksum]
|
/// Throws [ArgumentError] if frame length is wrong, or if [validateChecksum]
|
||||||
/// is true and the NEG8 checksum fails.
|
/// is true and the NEG8 checksum fails.
|
||||||
@ -39,13 +43,12 @@ SensorState parseS300(Uint8List frame, {bool validateChecksum = true}) {
|
|||||||
throw ArgumentError('S300 frame NEG8 checksum failed');
|
throw ArgumentError('S300 frame NEG8 checksum failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some S300 Bluetooth frames carry live RPM in the tail payload at 82..83,
|
// Some S300 Bluetooth frames carry live RPM in the tail payload,
|
||||||
// while the header bytes stay fixed at 1b 00 14 00 and decode to a bogus
|
// while the header bytes stay fixed at 1b 00 14 00 and decode to a bogus
|
||||||
// 4864 rpm. Prefer the tail value when it looks like real engine speed.
|
// 4864 rpm. Prefer the tail value when it looks like real engine speed.
|
||||||
final bool hasTailPayload = _hasS300TailPayload(frame);
|
final int? tailRpmRaw = _s300TailRpmRaw(frame);
|
||||||
final int rpmRaw =
|
final int rpmRaw = tailRpmRaw ?? _readUint16BE(frame, 2);
|
||||||
hasTailPayload ? _readUint16LE(frame, 82) : _readUint16BE(frame, 2);
|
final double rpm = (tailRpmRaw != null ? rpmRaw - 1089 : rpmRaw - 256)
|
||||||
final double rpm = (hasTailPayload ? rpmRaw - 1089 : rpmRaw - 256)
|
|
||||||
.clamp(0, 12000)
|
.clamp(0, 12000)
|
||||||
.toDouble();
|
.toDouble();
|
||||||
|
|
||||||
@ -185,8 +188,11 @@ int _readUint16LE(Uint8List frame, int offset) {
|
|||||||
return frame[offset] | (frame[offset + 1] << 8);
|
return frame[offset] | (frame[offset + 1] << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _hasS300TailPayload(Uint8List frame) {
|
int? _s300TailRpmRaw(Uint8List frame) {
|
||||||
if (!isS300PlaceholderFrame(frame)) return false;
|
if (!isS300PlaceholderFrame(frame)) return null;
|
||||||
final rpm = _readUint16LE(frame, 82);
|
for (final offset in const [82, 86]) {
|
||||||
return rpm > 300 && rpm < 12000;
|
final rpm = _readUint16LE(frame, offset);
|
||||||
|
if (rpm > 300 && rpm < 12000) return rpm;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,9 @@ final sensorStateProvider = StreamProvider<SensorState>((ref) async* {
|
|||||||
|
|
||||||
await for (final frame in btNotifier.frameStream) {
|
await for (final frame in btNotifier.frameStream) {
|
||||||
if (ecuType == EcuType.s300) {
|
if (ecuType == EcuType.s300) {
|
||||||
|
if (isS300PlaceholderFrame(frame) && !hasS300TailRpmPayload(frame)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
yield parseS300(frame, validateChecksum: false);
|
yield parseS300(frame, validateChecksum: false);
|
||||||
} else {
|
} else {
|
||||||
yield parseKPro(frame);
|
yield parseKPro(frame);
|
||||||
|
|||||||
@ -190,8 +190,7 @@ void main() {
|
|||||||
expect(isS300PlaceholderFrame(frame), isTrue);
|
expect(isS300PlaceholderFrame(frame), isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('uses calibrated tail payload RPM for S300 Bluetooth prefix frames',
|
test('uses calibrated tail payload RPM at offset 82', () {
|
||||||
() {
|
|
||||||
final frame = Uint8List(128);
|
final frame = Uint8List(128);
|
||||||
final prefix = [
|
final prefix = [
|
||||||
0x1B,
|
0x1B,
|
||||||
@ -222,5 +221,68 @@ void main() {
|
|||||||
final state = parseS300(frame);
|
final state = parseS300(frame);
|
||||||
expect(state.rpm, equals(2033.0));
|
expect(state.rpm, equals(2033.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('uses calibrated tail payload RPM at offset 86', () {
|
||||||
|
final frame = Uint8List(128);
|
||||||
|
final prefix = [
|
||||||
|
0x1B,
|
||||||
|
0x00,
|
||||||
|
0x14,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xF2,
|
||||||
|
0x03,
|
||||||
|
0x18,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x10,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xC4,
|
||||||
|
];
|
||||||
|
for (var i = 0; i < prefix.length; i++) {
|
||||||
|
frame[i] = prefix[i];
|
||||||
|
}
|
||||||
|
frame[86] = 0x31;
|
||||||
|
frame[87] = 0x0C;
|
||||||
|
frame[127] = calculateNeg8(frame.sublist(0, 127));
|
||||||
|
|
||||||
|
expect(hasS300TailRpmPayload(frame), isTrue);
|
||||||
|
final state = parseS300(frame);
|
||||||
|
expect(state.rpm, equals(2032.0));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('detects empty S300 Bluetooth prefix frames', () {
|
||||||
|
final frame = Uint8List(128);
|
||||||
|
final prefix = [
|
||||||
|
0x1B,
|
||||||
|
0x00,
|
||||||
|
0x14,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
0xFF,
|
||||||
|
0xF2,
|
||||||
|
0x03,
|
||||||
|
0x18,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x10,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0xC4,
|
||||||
|
];
|
||||||
|
for (var i = 0; i < prefix.length; i++) {
|
||||||
|
frame[i] = prefix[i];
|
||||||
|
}
|
||||||
|
frame[127] = calculateNeg8(frame.sublist(0, 127));
|
||||||
|
|
||||||
|
expect(isS300PlaceholderFrame(frame), isTrue);
|
||||||
|
expect(hasS300TailRpmPayload(frame), isFalse);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user