AsamMdf 0.4.0
dotnet add package AsamMdf --version 0.4.0
NuGet\Install-Package AsamMdf -Version 0.4.0
<PackageReference Include="AsamMdf" Version="0.4.0" />
<PackageVersion Include="AsamMdf" Version="0.4.0" />
<PackageReference Include="AsamMdf" />
paket add AsamMdf --version 0.4.0
#r "nuget: AsamMdf, 0.4.0"
#:package AsamMdf@0.4.0
#addin nuget:?package=AsamMdf&version=0.4.0
#tool nuget:?package=AsamMdf&version=0.4.0
AsamMdf - MDF4 File Reader for .NET
A .NET library for reading ASAM MDF (Measurement Data Format) version 4.x files. This library provides a simple and efficient API to access measurement data, channel information, and metadata from MDF4 files commonly used in automotive and industrial measurement applications.
If this library meets your requirements, please consider acquiring a commercial license for full functionality and professional support.
For licensing inquiries, please contact: info@uptux.de
Features
✅ Comprehensive MDF4 Support
- Read channel data with automatic type conversion
- Support for all standard data types (integers, floats, strings, byte arrays)
- CANopen Date and Time types
- Complex numbers (half/single/double precision, LE/BE) - MDF 4.2.0
- MIME sample and MIME stream (embedded binary data with content-type) - MDF 4.2.0
- Event blocks (EVBLOCK) – markers, triggers, recording info, capture blocks
- Variable-length signal data (VLSD)
- Compressed data (Deflate, Transpose+Deflate)
- Physical and virtual master channels
✅ Advanced Conversions
- Linear, rational, and algebraic conversions
- Lookup tables (value-to-value, value-to-text, text-to-text, text-to-value)
- Partial conversions with value ranges
- Automatic unit conversion
✅ Array Channels (CABLOCK)
- Multi-dimensional arrays (maps, curves, classification results)
- Compact storage (CN template) for arrays with fixed axes
- Automatic array structure parsing and flattening
✅ Performance
- Streaming API for memory-efficient data access
- Support for compressed data blocks
- Lazy loading of channel data
Quick Start
Basic Usage
using AsamMdf;
// Open an MDF4 file
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
// Get all channel names
var channelNames = reader.GetChannelNames();
Console.WriteLine($"Found {channelNames.Count()} channels");
// Read a specific channel
var channelReader = reader.GetChannelReader("Speed");
var values = channelReader.GetValues<double>().ToList();
Console.WriteLine($"Channel 'Speed' has {values.Count} samples");
}
Reading Channel Data with Master Channels
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
// Get a data channel
var speedChannel = reader.GetChannelReader("Speed");
// Get associated master channels (time, angle, etc.)
var masterChannels = speedChannel.GetMasterChannels();
if (masterChannels.Count > 0)
{
var timeChannel = masterChannels.First();
var timestamps = timeChannel.GetValues<double>().ToList();
var speeds = speedChannel.GetValues<double>().ToList();
// Data is synchronized - same index corresponds to same measurement point
for (int i = 0; i < timestamps.Count; i++)
{
Console.WriteLine($"Time: {timestamps[i]:F3}s, Speed: {speeds[i]:F1} km/h");
}
}
}
Reading Different Data Types
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
// Integer channel
var counterReader = reader.GetChannelReader("Counter");
var counterValues = counterReader.GetValues<int>().ToList();
// Floating-point channel
var tempReader = reader.GetChannelReader("Temperature");
var tempValues = tempReader.GetValues<float>().ToList();
// String channel (VLSD)
var statusReader = reader.GetChannelReader("Status");
var statusValues = statusReader.GetValues<string>().ToList();
// Byte array channel
var dataReader = reader.GetChannelReader("RawData");
var dataValues = dataReader.GetValues<byte[]>().ToList();
}
Reading Array Channel Data
using (var reader = new Mdf4FileReader("calibration_data.mf4"))
{
// Read a 2D array channel (e.g., engine calibration map)
var mapReader = reader.GetChannelReader("KF_EngineMap");
// Check if it's an array channel
if (mapReader.IsArrayChannel)
{
// Get array data
var arrayValues = mapReader.GetArrayValues<double>().ToList();
Console.WriteLine($"Number of samples: {arrayValues.Count}");
// Process each sample
foreach (var sample in arrayValues)
{
Console.WriteLine($"Array dimensions: [{string.Join(", ", sample.Dimensions)}]");
Console.WriteLine($"Total elements: {sample.ElementCount}");
// Access individual array elements
// For 2D array with dimensions [8, 8]:
var firstElement = sample.GetValueAt(0, 0); // Element at [0,0]
var element_2_3 = sample.GetValueAt(2, 3); // Element at [2,3]
// Or access flattened array directly
var allValues = sample.Values; // T[] array in row-major order
// Convert to multi-dimensional array
if (sample.Dimensions.Length == 2)
{
var multiDimArray = (double[,])sample.ToMultiDimensionalArray();
Console.WriteLine($"Value at [2,3]: {multiDimArray[2, 3]}");
}
}
}
}
Array Channel Details
Array channels (CABLOCK) are used for multi-dimensional calibration data such as:
- Engine maps (2D: RPM × Load)
- Curves (1D: Lookup tables)
- Classification results (N-D: Multi-parameter classifications)
- Signal matrices (2D+: Video frames, sensor arrays)
using AsamMdf;
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
var channelReader = reader.GetChannelReader("MapData");
// Check if it's an array channel
if (channelReader.IsArrayChannel)
{
// Read and process array data
foreach (var arrayData in channelReader.GetArrayValues<double>())
{
// Work with the array
Console.WriteLine($"Sample with {arrayData.ElementCount} elements");
// Efficient access to array elements
for (int i = 0; i < arrayData.Values.Length; i++)
{
var value = arrayData.Values[i];
// Process value...
}
}
}
}
Reading CANopen Date and Time
using AsamMdf;
using AsamMdf.Types;
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
// Read CANopen Date as DateTime
var dateReader = reader.GetChannelReader("DateChannel");
var dates = dateReader.GetValues<DateTime>().ToList();
// Or get CANopen Date objects for full access
var canOpenDates = dateReader.GetValues<CanOpenDate>().ToList();
foreach (var date in canOpenDates.Take(5))
{
Console.WriteLine($"Date: {date.GetFullYear()}-{date.Month:D2}-{date.Day:D2}");
Console.WriteLine($"Time: {date.Hours:D2}:{date.Minutes:D2}");
Console.WriteLine($"Day of week: {date.DayOfWeek}, DST: {date.IsSummerTime}");
}
// Read CANopen Time
var timeReader = reader.GetChannelReader("TimeChannel");
var times = timeReader.GetValues<DateTime>().ToList();
}
Reading Complex Numbers (MDF 4.2.0)
Note: Complex number support is fully implemented according to ASAM MDF 4.2.0 specification but has not yet been tested against real-world MDF4 files. Please report any issues you encounter.
using AsamMdf;
using AsamMdf.Types;
using (var reader = new Mdf4FileReader("fft_measurement.mf4"))
{
// Read complex numbers (e.g., FFT data)
var fftReader = reader.GetChannelReader("FFT_Signal");
var fftData = fftReader.GetValues<ComplexNumber>().ToList();
foreach (var complexValue in fftData.Take(5))
{
Console.WriteLine($"Complex: {complexValue}"); // "3 + 4i"
Console.WriteLine($" Magnitude: {complexValue.Magnitude}"); // 5.0
Console.WriteLine($" Phase: {complexValue.PhaseDegrees}°"); // 53.13°
Console.WriteLine($" Polar: {complexValue.ToPolarString()}"); // "5.000000∠53.13°"
}
// Or read only magnitudes directly
var magnitudes = fftReader.GetValues<double>().ToList();
// Perform operations on complex numbers
var z1 = fftData[0];
var z2 = fftData[1];
var sum = z1 + z2;
var product = z1 * z2;
}
Reading MIME Sample Data (MDF 4.2.0)
Note: MIME sample support is fully implemented according to ASAM MDF 4.2.0 specification but has not yet been tested against real-world MDF4 files. Please report any issues you encounter.
using AsamMdf;
using AsamMdf.Types;
using (var reader = new Mdf4FileReader("camera_recording.mf4"))
{
// Read MIME sample channel (e.g., camera images)
var cameraReader = reader.GetChannelReader("Front_Camera");
var images = cameraReader.GetValues<MimeSample>().ToList();
foreach (var image in images)
{
Console.WriteLine($"Image: {image.ContentType}, Size: {image.Size} bytes");
// Save image to file
string extension = image.ContentType switch
{
"image/png" => "png",
"image/jpeg" => "jpg",
_ => "bin"
};
image.SaveToFile($"frame_{DateTime.Now.Ticks}.{extension}");
}
// Or read as raw bytes if you don't need the MIME wrapper
var rawImages = cameraReader.GetValues<byte[]>().ToList();
// Or get string descriptions
var descriptions = cameraReader.GetValues<string>().ToList();
// e.g., "MimeSample(image/jpeg, 102400 bytes)"
}
Reading MIME Stream Data (MDF 4.2.0)
Note: MIME stream support is fully implemented according to ASAM MDF 4.2.0 specification but has not yet been tested against real-world MDF4 files. Please report any issues you encounter.
using AsamMdf;
using AsamMdf.Types;
using (var reader = new Mdf4FileReader("video_recording.mf4"))
{
// Read MIME stream channel (e.g., video frames)
var videoReader = reader.GetChannelReader("Video_Stream");
// Each value in a MIME stream represents a frame/chunk
var videoStream = new MimeStream("video/mpeg");
foreach (var frame in videoReader.GetValues<byte[]>())
{
videoStream.AddSample(frame);
}
// Save complete video stream
videoStream.SaveToFile("recorded_video.mpg");
Console.WriteLine($"Video recorded:");
Console.WriteLine($" Content-Type: {videoStream.ContentType}");
Console.WriteLine($" Total frames: {videoStream.SampleCount}");
Console.WriteLine($" Total size: {videoStream.TotalSize} bytes");
// Or process each frame individually without creating a stream
var frames = videoReader.GetValues<byte[]>().ToList();
for (int i = 0; i < frames.Count; i++)
{
Console.WriteLine($"Frame {i}: {frames[i].Length} bytes");
// Process frame...
}
}
Reading Events (Markers, Triggers, Recording Info)
Events in MDF4 are file-level time annotations – they are not channel data, but metadata about specific time points or time ranges within a measurement (e.g. user bookmarks, trigger conditions, capture block start/stop).
Events are read independently of channels via GetEvents().
using AsamMdf;
using AsamMdf.Types;
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
// Get all events from the file
var events = reader.GetEvents().ToList();
Console.WriteLine($"Found {events.Count} event(s):");
foreach (var ev in events)
{
Console.WriteLine($" {ev}");
// e.g. "[Marker] 90 deg @ 00:00:00.090 (Point, Tool)"
}
// Filter by type using LINQ
var markers = events.Where(e => e.EventType == EventType.Marker).ToList();
Console.WriteLine($"\nMarkers ({markers.Count}):");
foreach (var m in markers)
{
// TimeFromMeasurementStart is available when SyncType == Seconds
if (m.TimeFromMeasurementStart.HasValue)
Console.WriteLine($" '{m.Name}' at {m.TimeFromMeasurementStart.Value.TotalSeconds:F3}s");
else
Console.WriteLine($" '{m.Name}' (SyncBase={m.SyncBaseValue})");
}
// Find capture block ranges (Begin/End pairs)
var captureBegins = events.Where(e =>
e.EventType == EventType.AcquisitionInterrupt &&
e.RangeType == EventRangeType.Begin).ToList();
Console.WriteLine($"\nCapture blocks started: {captureBegins.Count}");
// Access individual event properties
var firstTrigger = events.FirstOrDefault(e => e.EventType == EventType.Trigger);
if (firstTrigger != null)
{
Console.WriteLine($"\nFirst trigger:");
Console.WriteLine($" Name : {firstTrigger.Name}");
Console.WriteLine($" Cause : {firstTrigger.Cause}");
Console.WriteLine($" SyncBase: {firstTrigger.SyncBaseValue}");
Console.WriteLine($" Factor : {firstTrigger.SyncFactor}");
Console.WriteLine($" PostProc: {firstTrigger.IsPostProcessing}");
}
}
Event types (EventType enum):
| Value | Meaning |
|-------|---------|
| Marker | User-defined bookmark |
| Trigger | Measurement trigger condition |
| Recording | Start / stop of recording |
| AcquisitionInterrupt | Capture block boundary (Begin/End pair) |
| RecordingInterrupt | Recording was interrupted |
| StartRecordingTrigger | Trigger that started the recording |
| StopRecordingTrigger | Trigger that stopped the recording |
Range types (EventRangeType enum): Point (single timestamp), Begin / End (paired interval).
Working with Conversions
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
// Channel with linear conversion (e.g., raw ADC -> physical units)
var sensorReader = reader.GetChannelReader("SensorVoltage");
// GetValues automatically applies conversion formula
var physicalValues = sensorReader.GetValues<double>().ToList();
// Values are returned in physical units (e.g., Volts, not raw ADC counts)
Console.WriteLine($"Sensor voltage: {physicalValues.First():F3} V");
}
Asynchronous API (Async/Await)
The library provides a comprehensive async API for non-blocking operations. This is perfect for UI applications, web APIs, and server scenarios where you need to keep threads free while reading metadata.
All async methods:
- Are non-blocking (don't freeze the current thread)
- Support
CancellationTokenfor timeouts and cancellation - Have the same functionality as sync equivalents
- Return
Task<T>for proper async/await support
using AsamMdf;
using System.Threading;
// Load metadata asynchronously (keeps UI responsive)
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
// Get channel names without blocking
var channelNames = await reader.GetChannelNamesAsync();
Console.WriteLine($"Found {channelNames.Count} channels");
// Get structured channel metadata
var metadata = await reader.GetChannelMetadataStructuredAsync("Speed");
Console.WriteLine($"Channel: {metadata.Name}, Unit: {metadata.Unit}");
// Get file-level metadata
var fileMetadata = await reader.GetFileMetadataAsync();
Console.WriteLine($"File created: {fileMetadata["StartTime"]}");
// Get channel reader
var reader = await reader.GetChannelReaderAsync("Speed");
var values = reader.GetValues<double>().ToList();
}
Async methods with timeout:
// Cancel if operation takes longer than 10 seconds
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
try
{
var names = await reader.GetChannelNamesAsync(cts.Token);
Console.WriteLine($"Successfully loaded {names.Count} channels");
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation timed out - file too large?");
}
}
Async methods in UI (WPF/WinForms):
// Keep UI responsive while loading large files
private async void LoadButton_Click(object sender, EventArgs e)
{
loadButton.Enabled = false;
statusLabel.Text = "Loading...";
try
{
using (var reader = new Mdf4FileReader(_filePath))
{
// Async loading - UI remains responsive
var channels = await reader.GetChannelNamesAsync();
// Update UI on UI thread
channelListBox.Items.Clear();
foreach (var channel in channels)
{
channelListBox.Items.Add(channel);
}
statusLabel.Text = $"Loaded {channels.Count} channels";
}
}
catch (Exception ex)
{
statusLabel.Text = $"Error: {ex.Message}";
}
finally
{
loadButton.Enabled = true;
}
}
Available async methods:
GetChannelNamesAsync(CancellationToken ct = default)GetChannelReaderAsync(string channelName, CancellationToken ct = default)GetFileMetadataAsync(CancellationToken ct = default)GetChannelMetadataAsync(string channelName, CancellationToken ct = default)GetChannelMetadataStructuredAsync(string channelName, CancellationToken ct = default)GetAttachmentsAsync(CancellationToken ct = default)GetAttachmentDataAsync(string fileName, CancellationToken ct = default)ExtractAttachmentAsync(string fileName, string outputPath, CancellationToken ct = default)
Working with Attachments
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
// List all attachments
var attachments = reader.GetAttachments();
foreach (var attachment in attachments)
{
Console.WriteLine($"File: {attachment.FileName}");
Console.WriteLine($" Type: {attachment.MimeType}");
Console.WriteLine($" Size: {attachment.OriginalSize} bytes");
Console.WriteLine($" Embedded: {attachment.IsEmbedded}");
Console.WriteLine($" Compressed: {attachment.IsCompressed}");
}
// Extract an embedded attachment
var embeddedAttachments = attachments.Where(a => a.IsEmbedded);
if (embeddedAttachments.Any())
{
var attachment = embeddedAttachments.First();
// Get attachment data (automatically decompressed if needed)
byte[] data = reader.GetAttachmentData(attachment.FileName);
// Or extract directly to a file
reader.ExtractAttachment(attachment.FileName, "output.txt");
}
}
Getting File Metadata
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
// Get file-level metadata
var fileMetadata = reader.GetFileMetadata();
Console.WriteLine($"MDF Version: {fileMetadata["FormatIdentifier"]}");
Console.WriteLine($"Created by: {fileMetadata["ProgramIdentifier"]}");
Console.WriteLine($"Start time: {fileMetadata["StartTime"]}");
Console.WriteLine($"Time zone offset: {fileMetadata["UTCTimeZoneOffset_min"]} minutes");
// Check for file comments with XML metadata
if (fileMetadata.ContainsKey("FileComment"))
{
Console.WriteLine($"Comment: {fileMetadata["FileComment"]}");
}
}
Getting Channel Metadata
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
// Get list of all channels
var channelNames = reader.GetChannelNames();
foreach (var channelName in channelNames)
{
// Get detailed metadata for each channel
var metadata = reader.GetChannelMetadata(channelName);
Console.WriteLine($"Channel: {metadata["ChannelName"]}");
Console.WriteLine($" Data Type: {metadata["DataType"]}");
Console.WriteLine($" Channel Type: {metadata["ChannelType"]}");
Console.WriteLine($" Sync Type: {metadata["SyncType"]}");
Console.WriteLine($" Bit Count: {metadata["BitCount"]}");
// Check for channel comments (may contain XML extensions)
if (metadata.ContainsKey("Comment"))
{
Console.WriteLine($" Comment: {metadata["Comment"]}");
}
// Check for unit information
if (metadata.ContainsKey("Unit"))
{
Console.WriteLine($" Unit: {metadata["Unit"]}");
}
// Check for conversion information
if (metadata.ContainsKey("ConversionType"))
{
Console.WriteLine($" Conversion: {metadata["ConversionType"]}");
}
}
// Or get metadata for a specific channel
var speedMetadata = reader.GetChannelMetadata("Speed");
bool isMaster = speedMetadata["ChannelType"] == "2"; // 2 = master channel
Console.WriteLine($"Speed channel is master: {isMaster}");
}
Advanced Usage
Filtering Master Channels by Sync Type
using (var reader = new Mdf4FileReader("measurement.mf4"))
{
var channelReader = reader.GetChannelReader("Data");
// Get only time-based master channels
var timeMasters = channelReader.GetMasterChannels(Channel_SyncType.Time);
// Get only angle-based master channels
var angleMasters = channelReader.GetMasterChannels(Channel_SyncType.Angle);
// Get all master channels
var allMasters = channelReader.GetMasterChannels(Channel_SyncType.Undefined);
}
Reading Compressed Data
using (var reader = new Mdf4FileReader("compressed_measurement.mf4"))
{
// Compressed data is automatically decompressed
var channelReader = reader.GetChannelReader("Speed");
var values = channelReader.GetValues<double>().ToList();
// Works transparently with Deflate and Transpose+Deflate compression
}
Memory-Efficient Streaming
using (var reader = new Mdf4FileReader("large_measurement.mf4"))
{
var channelReader = reader.GetChannelReader("Speed");
// Use IEnumerable for streaming without loading all data into memory
foreach (var value in channelReader.GetValues<double>())
{
// Process each value individually
ProcessValue(value);
}
}
Supported Data Types
Numeric Types
- Integer:
byte,sbyte,short,ushort,int,uint,long,ulong - Floating-point:
float,double - Byte order: Little-endian (LE) and Big-endian (BE)
String Types
- Fixed-length strings: SBC/Latin-1, UTF-8, UTF-16 LE/BE
- Variable-length strings: VLSD (Variable Length Signal Data)
Special Types
- Byte arrays: Fixed-length binary data
- CANopen Date: Date and time with day-of-week and DST flag
- CANopen Time: Time since epoch (Jan 1, 1984)
- Complex Numbers (MDF 4.2.0): Real and imaginary parts for FFT/signal processing
- Half precision (16-bit), Single precision (32-bit), Double precision (64-bit)
- Little-endian and Big-endian byte order
- Full arithmetic operations (+, -, *, /)
- ⚠️ Not yet tested with real-world files
- MIME Sample (MDF 4.2.0): Single embedded binary data with content-type
- Images (PNG, JPEG, GIF, BMP)
- Video frames (MPEG, MP4, AVI)
- Audio samples (WAV, MP3, OGG)
- JSON/XML data, Text files
- Custom binary formats
- ⚠️ Not yet tested with real-world files
- MIME Stream (MDF 4.2.0): Continuous stream of binary samples
- Video streams (multiple frames)
- Audio streams (audio chunks)
- Any multi-sample binary stream data
- ⚠️ Not yet tested with real-world files
Master Channels
- Physical master channels (ChannelType = 2): Time, angle, distance, index
- Virtual master channels (ChannelType = 3): Calculated from record index
Conversion Support
Linear Conversion
Formula: phys = int * P2 + P1
Rational Conversion
Formula: phys = (P1 * int² + P2 * int + P3) / (P4 * int² + P5 * int + P6)
Algebraic Conversion
Formula: User-defined algebraic expression (e.g., sin(X * 3.14159 / 180))
Lookup Conversions
- Value-to-Value: Map discrete input values to output values (with optional interpolation)
- Value-to-Text: Map numeric ranges to text descriptions
- Text-to-Value: Convert text inputs to numeric values
- Text-to-Text: Map text inputs to different text outputs
Partial Conversions
Multiple conversion rules with value ranges and default fallback
Feature Implementation Summary
✅ Fully Implemented Features (23 features)
- ✓ Fixed-length integer types (signed/unsigned, 8/16/32/64-bit, LE/BE)
- ✓ Floating-point types (32/64-bit, LE/BE)
- ✓ Fixed-length strings (SBC/Latin-1, UTF-8, UTF-16 LE/BE)
- ✓ Variable-length strings (VLSD)
- ✓ Byte arrays
- ✓ CANopen Date and Time types
- ✓ Complex numbers (MDF 4.2.0) - Real and imaginary parts, half/single/double precision, LE/BE ⚠️ Not yet tested with real files
- ✓ MIME sample (MDF 4.2.0) - Embedded binary data with content-type (images, video, audio, etc.) ⚠️ Not yet tested with real files
- ✓ MIME stream (MDF 4.2.0) - Continuous stream of binary samples ⚠️ Not yet tested with real files
- ✓ Array channels (CABLOCK) - Multi-dimensional arrays (Lookup, Curves, Classification results) with CN template storage
- ✓ Event blocks (EVBLOCK) - Markers, triggers, recording events, acquisition interrupts
- ✓ Physical master channels (ChannelType = 2)
- ✓ Virtual master channels (ChannelType = 3)
- ✓ Linear conversions
- ✓ Rational conversions
- ✓ Algebraic conversions
- ✓ Lookup conversions (value-to-value, value-to-text, text-to-text, text-to-value)
- ✓ Partial conversions with default rules
- ✓ Compressed data (Deflate, Transpose+Deflate)
- ✓ Data lists
- ✓ MIME synchronization channels
- ✓ Bit-level signal reading with sign extension
- ✓ File and channel metadata extraction (including XML comments and custom extensions)
- ✓ Attachments (embedded, compressed, external references with MD5 validation)
❌ Not Yet Implemented Features (6 features)
- ✗ CG/DG template array storage (Arrays with independent element records)
- ✗ Maximum-length signal data (MLSD, ChannelType = 5)
- ✗ Virtual data channels (ChannelType = 6) - calculated channels
- ✗ Sample reduction blocks (SRBLOCK) - downsampled data
- ✗ Non-byte-aligned signal reading (advanced)
- ✗ Overlapping signals
Test Coverage Statistics
- Total tests: 262 passing
- 18 Event tests (EVBLOCK – Marker, Trigger, Recording/AcquisitionInterrupt)
- 24 Complex Number tests (DataType 15/16)
- 48 MIME type tests (DataType 11/12)
- 17 Array channel tests (CABLOCK)
- 155 existing tests (data types, conversions, features, attachments)
- Test coverage: ~87%
- All remaining code focuses on MDF 4.x with comprehensive test coverage
- MDF 4.x focus: Library now focuses exclusively on MDF 4.x format
- Array support: CN template storage fully implemented and tested with real MDF4 files from ASAM standard
- Note: Complex Numbers and MIME types are fully implemented but not yet tested against real-world files
API Reference
Core Classes
Mdf4FileReader
Main entry point for reading MDF4 files.
public class Mdf4FileReader : IDisposable
{
public Mdf4FileReader(string filePath);
public IEnumerable<string> GetChannelNames();
public ChannelReader GetChannelReader(string channelName);
public IReadOnlyDictionary<string, string> GetFileMetadata();
public IReadOnlyDictionary<string, string> GetChannelMetadata(string channelName);
public IEnumerable<AttachmentInfo> GetAttachments();
public byte[] GetAttachmentData(string fileName);
public void ExtractAttachment(string fileName, string outputPath);
// Events (file-level annotations – NOT channel data)
public IEnumerable<MdfEvent> GetEvents();
public Task<IReadOnlyList<MdfEvent>> GetEventsAsync(CancellationToken ct = default);
public void Dispose();
}
AttachmentInfo
Contains information about an attachment in an MDF4 file.
public class AttachmentInfo
{
public string FileName { get; }
public string MimeType { get; }
public long OriginalSize { get; }
public long EmbeddedSize { get; }
public bool IsEmbedded { get; }
public bool IsCompressed { get; }
public bool HasMd5Checksum { get; }
public byte[] Md5Checksum { get; }
public string Comment { get; }
public int CreatorIndex { get; }
}
MdfEvent
Represents a single event (EVBLOCK) from an MDF4 file. Events are file-level time annotations, not channel data.
public sealed class MdfEvent
{
public string Name { get; } // Event name (from TXBLOCK)
public string? Comment { get; } // Optional description
public EventType EventType { get; } // Marker, Trigger, Recording, ...
public EventSyncType SyncType { get; } // Seconds, Index, Radians, Meter
public EventRangeType RangeType { get; } // Point, Begin, End
public EventCause Cause { get; } // Tool, Script, User, Error, Other
public long SyncBaseValue { get; } // Raw sync value
public double SyncFactor { get; } // Scale: physical = SyncBase * SyncFactor
public TimeSpan? TimeFromMeasurementStart { get; } // Convenience: only set when SyncType==Seconds
public ushort CreatorIndex { get; } // Index into file history list
public bool IsPostProcessing { get; } // Created during post-processing
}
ChannelReader
Provides access to channel data and metadata.
public class ChannelReader
{
public bool IsMasterChannel { get; }
public bool IsArrayChannel { get; }
public IEnumerable<T> GetValues<T>();
public IEnumerable<ArrayData<T>> GetArrayValues<T>(); // For array channels
public ChannelReader GetMasterChannelReader(Channel_SyncType syncType = Channel_SyncType.Undefined);
public List<ChannelReader> GetMasterChannels(Channel_SyncType syncType = Channel_SyncType.Undefined);
}
ArrayData<T>
Represents a single array sample with multi-dimensional structure.
public class ArrayData<T>
{
public T[] Values { get; } // Flattened array in row-major order
public int[] Dimensions { get; } // Size of each dimension
public int ElementCount { get; } // Total number of elements
public T GetValueAt(params int[] indices); // Access element by coordinates
public Array ToMultiDimensionalArray(); // Convert to System.Array
}
Channel_SyncType
Enumeration for master channel synchronization types.
public enum Channel_SyncType
{
None = 0,
Time = 1,
Angle = 2,
Distance = 3,
Index = 4,
Undefined = 99
}
CANopen Types
CanOpenDate
public class CanOpenDate
{
public ushort Milliseconds { get; }
public byte Minutes { get; }
public byte Hours { get; }
public bool IsSummerTime { get; }
public byte Day { get; }
public byte DayOfWeek { get; } // 1=Monday, 7=Sunday
public byte Month { get; }
public byte Year { get; } // 0-99, offset from 2000
public DateTime ToDateTime();
public int GetFullYear(); // Returns full year (e.g., 2024)
}
CanOpenTime
public class CanOpenTime
{
public uint Milliseconds { get; }
public ushort Days { get; }
public static readonly DateTime Epoch; // January 1, 1984
public DateTime ToDateTime();
public TimeSpan ToTimeSpan();
}
Complex Numbers and MIME Types (MDF 4.2.0)
ComplexNumber
public class ComplexNumber
{
public double Real { get; set; }
public double Imaginary { get; set; }
public double Magnitude { get; } // sqrt(Real² + Imaginary²)
public double Phase { get; } // atan2(Imaginary, Real) in radians
public double PhaseDegrees { get; } // Phase in degrees
public ComplexNumber Conjugate { get; } // Real - Imaginary*i
public ComplexNumber(double real, double imaginary);
public string ToString(); // "3 + 4i" format
public string ToPolarString(); // "5.000000∠53.13°" format
// Operators: +, -, *, /
}
MimeSample
public class MimeSample
{
public string ContentType { get; set; } // e.g., "image/png", "video/mpeg"
public byte[] Data { get; set; }
public int Size { get; } // Data.Length
public MimeSample(string contentType, byte[] data);
public void SaveToFile(string filePath);
}
MimeStream
public class MimeStream
{
public string ContentType { get; set; }
public List<byte[]> Samples { get; set; }
public int SampleCount { get; } // Number of samples
public long TotalSize { get; } // Total bytes across all samples
public MimeStream(string contentType);
public MimeStream(string contentType, IEnumerable<byte[]> samples);
public void AddSample(byte[] sample);
public byte[] GetConcatenatedData(); // Concatenate all samples
public void SaveToFile(string filePath);
}
Requirements
- .NET Standard 2.1 or higher
- NuGet packages:
- NCalc (for algebraic conversions)
- System.IO.Compression (for compressed data)
Changelog
Version 0.4.0 (Released - March 11, 2026)
- ✅ MLSD (maximum-length signal data) support
- ✅ Virtual data channels (ChannelType = 6)
- ✅ Event block support (EVBLOCK) –
GetEvents()/GetEventsAsync() - ✅ Unsorted data
- ✅ Improved: 280 passing tests
Version 0.3.1-beta (Released - February 5, 2026)
- Displays formatted message informing users about beta limitations (50 MB file size limit)
Version 0.3.0 (Released - January 22, 2026)
- ✅ New: Array channel support (CABLOCK) - CN template storage fully implemented
- Lookup tables (2D calibration maps)
- Curves (1D function tables)
- Classification results
- Signal matrices and multi-dimensional arrays
- ✅ Fixed: Vector MDF4 array channel reading (byte padding for BitConverter operations)
- ✅ Improved: 217 passing tests (was 200)
- ✅ Support for basic data types (integer, float, string, byte array)
- ✅ All standard conversion types
- ✅ Compressed data support
- ✅ CANopen Date and Time types
- ✅ Physical and virtual master channels
- ✅ VLSD (variable-length string data)
- ✅ File and channel metadata extraction
- ✅ Attachment extraction (embedded, compressed, external references)
- ✅ Complex numbers (MDF 4.2.0)
- ✅ MIME sample and stream (MDF 4.2.0)
- ⚠️ Note: Complex numbers and MIME types not yet tested against real-world files
Version 0.2.0
- ✅ Complex numbers support (MDF 4.2.0) - DataType 15/16
- ✅ MIME sample support (MDF 4.2.0) - DataType 11
- ✅ MIME stream support (MDF 4.2.0) - DataType 12
- ✅ 237 passing tests
Version 0.1.0
- ✅ Initial release
- ✅ Support for basic data types (integer, float, string, byte array)
- ✅ All standard conversion types
- ✅ Compressed data support
- ✅ CANopen Date and Time types
- ✅ Physical and virtual master channels
- ✅ VLSD (variable-length string data)
- ✅ File and channel metadata extraction
- ✅ Attachment extraction (embedded, compressed, external references)
- ✅ 126 passing tests
Last updated: March 11, 2026
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- NCalcSync (>= 5.11.0)
- System.Text.Json (>= 10.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.4.0 | 326 | 3/13/2026 |
| 0.3.3-beta | 138 | 2/18/2026 |
| 0.3.2-beta | 143 | 2/18/2026 |
| 0.3.1-beta | 154 | 2/5/2026 |
| 0.3.0-beta | 149 | 1/27/2026 |