Bus Data Support¶
MeasFlow provides first-class support for automotive bus data, compatible with MDF4 specifications.
Supported Bus Types¶
- CAN 2.0A/B - Controller Area Network (11-bit and 29-bit identifiers)
- CAN-FD - CAN with Flexible Data-Rate
- LIN - Local Interconnect Network
- FlexRay - High-speed automotive bus
- Ethernet - Automotive Ethernet (100BASE-T1, 1000BASE-T1)
- MOST - Media Oriented Systems Transport
Architecture¶
BusChannelDefinition
├── BusConfig (CAN/CAN-FD/LIN/FlexRay/Ethernet/MOST)
├── Frames[]
│ ├── FrameId, PayloadLength, Flags, Direction
│ ├── Signals[] (direct signals when no PDU layer)
│ └── Pdus[]
│ ├── Signals[]
│ ├── MultiplexConfig
│ ├── E2EProtection
│ ├── SecOcConfig
│ └── ContainedPdus[] (AUTOSAR I-PDU Mux)
└── ValueTables[]
Recording Bus Data¶
CAN Example¶
using var writer = MeasFile.CreateWriter("can_trace.meas");
// Configure CAN bus
var canConfig = new CanBusConfig
{
BusType = CanBusType.CanFd,
BaudRate = 500_000,
DataBaudRate = 2_000_000
};
var busGroup = writer.AddBusGroup("CAN1", canConfig);
// Define frames
var engineFrame = busGroup.DefineCanFrame("Engine", 0x123, 8);
engineFrame.Signals.Add(new SignalDefinition
{
Name = "RPM",
StartBit = 0,
BitLength = 16,
ByteOrder = ByteOrder.Intel,
Factor = 0.25,
Offset = 0,
Unit = "rpm"
});
engineFrame.Signals.Add(new SignalDefinition
{
Name = "Temperature",
StartBit = 16,
BitLength = 8,
Factor = 1.0,
Offset = -40,
Unit = "°C"
});
// Write frames
byte[] payload = new byte[8];
while (recording)
{
// Get frame from CAN hardware
var frame = ReadCanFrame();
busGroup.WriteFrame(MeasTimestamp.Now, frame.Id, frame.Data);
writer.Flush();
}
with meas.Writer("can_trace.meas") as writer:
# Configure CAN bus
can_config = meas.CanBusConfig(
bus_type=meas.CanBusType.CanFd,
baud_rate=500_000,
data_baud_rate=2_000_000
)
bus_group = writer.add_bus_group("CAN1", can_config)
# Define frames
engine_frame = bus_group.define_can_frame("Engine", 0x123, 8)
engine_frame.add_signal(
name="RPM",
start_bit=0,
bit_length=16,
byte_order="intel",
factor=0.25,
offset=0,
unit="rpm"
)
# Write frames
while recording:
frame = read_can_frame()
bus_group.write_frame(time.time_ns(), frame.id, frame.data)
writer.flush()
Signal Definitions¶
Basic Signal Properties¶
| Property | Description |
|---|---|
| Name | Signal identifier |
| StartBit | Bit position in frame (0-based) |
| BitLength | Number of bits |
| ByteOrder | Intel (little-endian) or Motorola (big-endian) |
| Factor | Scaling factor (physical = raw × factor + offset) |
| Offset | Offset value |
| Unit | Physical unit (e.g., "rpm", "°C") |
| ValueTable | Enum mappings (0 → "Off", 1 → "On") |
Value Tables¶
Map raw values to human-readable strings:
var gearSignal = new SignalDefinition
{
Name = "Gear",
StartBit = 24,
BitLength = 3,
ValueTable = "GearStates"
};
busGroup.ValueTables.Add(new ValueTable
{
Name = "GearStates",
Entries = new Dictionary<long, string>
{
{ 0, "Neutral" },
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" },
{ 4, "Fourth" },
{ 5, "Fifth" }
}
});
PDU Layer (AUTOSAR)¶
I-PDU Support¶
Protocol Data Units represent logical message groups:
var pdu = frame.AddPdu(new PduDefinition
{
Name = "EnginePDU",
StartByte = 0,
Length = 8
});
pdu.Signals.Add(new SignalDefinition
{
Name = "RPM",
StartBit = 0,
BitLength = 16,
// ... signal properties
});
Container-PDU¶
Multiple I-PDUs multiplexed in one frame:
var containerPdu = frame.AddPdu(new PduDefinition
{
Name = "Container",
StartByte = 0,
Length = 64,
IsContainer = true
});
containerPdu.ContainedPdus.Add(new ContainedPduDefinition
{
Name = "PDU_A",
HeaderId = 0x01,
PayloadLength = 16
});
containerPdu.ContainedPdus.Add(new ContainedPduDefinition
{
Name = "PDU_B",
HeaderId = 0x02,
PayloadLength = 32
});
Multiplexing¶
MUX Signals¶
Signals whose presence depends on multiplexer values:
var muxSignal = new SignalDefinition
{
Name = "MuxSelector",
StartBit = 0,
BitLength = 8,
IsMultiplexer = true
};
var signal_A = new SignalDefinition
{
Name = "Value_A",
StartBit = 8,
BitLength = 16,
MultiplexValue = 0 // Present when MuxSelector == 0
};
var signal_B = new SignalDefinition
{
Name = "Value_B",
StartBit = 8,
BitLength = 16,
MultiplexValue = 1 // Present when MuxSelector == 1
};
Nested Multiplexing¶
Multiple levels of multiplexing:
pdu.MultiplexConfig = new MultiplexConfig
{
Type = MultiplexType.ValueBased,
SwitchSignal = "Level1_Mux",
NestedLevels = new[]
{
new MultiplexLevel
{
SwitchSignal = "Level2_Mux",
Conditions = new[] { 0, 1, 2 }
}
}
};
E2E Protection¶
AUTOSAR End-to-End protection for data integrity:
pdu.E2EProtection = new E2EProtectionConfig
{
Profile = E2EProfile.Profile01,
DataId = 0x1234,
CrcOffset = 0, // CRC at byte 0
CounterOffset = 1 // Counter at byte 1
};
// Supported profiles: 01-11
SecOC (Secure Onboard Communication)¶
Authentication and encryption:
pdu.SecOcConfig = new SecOcConfig
{
AuthAlgorithm = SecOcAuthAlgorithm.CmacAes,
AuthTxBitLength = 64,
AuthRxBitLength = 64,
FreshnessValueLength = 64,
FreshnessValueTxBitLength = 24,
FreshnessValueRxBitLength = 24
};
Signal Decoding¶
Extract physical values from raw frames:
using var reader = MeasFile.OpenRead("can_trace.meas");
var busGroup = reader["CAN1"];
// Decode single signal
var rpm = busGroup.DecodeSignal("RPM");
Console.WriteLine($"Average RPM: {rpm.ToArray().Average()}");
// Decode all signals in frame
var engineSignals = busGroup.DecodeFrame("Engine");
foreach (var signal in engineSignals)
{
Console.WriteLine($"{signal.Key}: {signal.Value.ToArray().Average()}");
}
with meas.Reader("can_trace.meas") as reader:
bus_group = reader["CAN1"]
# Decode single signal
rpm = bus_group.decode_signal("RPM")
print(f"Average RPM: {rpm.mean()}")
# Decode all signals in frame
engine_signals = bus_group.decode_frame("Engine")
for name, values in engine_signals.items():
print(f"{name}: {values.mean()}")
Raw Frame Wire Format¶
Each bus type has a specific binary layout:
CAN/CAN-FD¶
- arbId: 29-bit CAN identifier (extended format)
- dlc: Data Length Code
- flags: CAN-FD flags (BRS, ESI, etc.)
LIN¶
- frameId: LIN frame identifier
- nad: Node Address
- checksum: LIN checksum
FlexRay¶
- slotId: FlexRay slot identifier
- cycle: Cycle counter
- flags: Channel A/B, startup, etc.
Ethernet¶
- macDst/macSrc: MAC addresses
- etherType: Ethernet type/length
- vlan: VLAN tag (optional)
Common Use Cases¶
Automotive Test Automation¶
Record CAN traffic during vehicle tests:
// Record
using var writer = MeasFile.CreateWriter("drive_cycle.meas");
var can = writer.AddBusGroup("CAN_Powertrain", canConfig);
// ... define frames and signals
RecordTestDrive(can);
// Analyze
using var reader = MeasFile.OpenRead("drive_cycle.meas");
var rpm = reader["CAN_Powertrain"].DecodeSignal("RPM");
var maxRpm = rpm.ToArray().Max();
Console.WriteLine($"Max RPM: {maxRpm}");
Signal Validation¶
Verify signal ranges and patterns:
var speed = busGroup.DecodeSignal("VehicleSpeed");
if (speed.ToArray().Any(s => s > 200))
{
Console.WriteLine("Warning: Unrealistic speed detected");
}
Cross-Language Analysis¶
Record with C, analyze with Python:
# Analyze in Python
with meas.Reader("recording.meas") as reader:
rpm = reader["CAN1"].decode_signal("RPM")
plt.plot(rpm)
plt.show()