289 lines
13 KiB
Markdown
289 lines
13 KiB
Markdown
|
|
---
|
|||
|
|
source_files:
|
|||
|
|
- DataPRO/TiltMIF/CRC16.cs
|
|||
|
|
- DataPRO/TiltMIF/Tilt_MIF.cs
|
|||
|
|
generated_at: "2026-04-16T03:45:24.803223+00:00"
|
|||
|
|
model: "Qwen/Qwen3-Coder-Next-FP8"
|
|||
|
|
schema_version: 1
|
|||
|
|
sha256: "e84d6b3eeba2c51d"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Documentation: TiltMIF Module
|
|||
|
|
|
|||
|
|
## 1. Purpose
|
|||
|
|
|
|||
|
|
This module provides data structures and utilities for reading, writing, and manipulating MIF (Memory Information Format) data files used by the Tilt sensor system. It defines the `CRC16` static class for computing CRC-16 checksums using the CCITT polynomial (0xA001), and the `Tilt_MIF` class which encapsulates the full binary layout of a MIF file—including core sensor calibration data (e.g., temperature, tilt sensor coefficients, offsets, ranges), configuration metadata (e.g., system ID, location, axis labeling, mount offsets), and associated CRC validation fields. The module supports both minimal (128-byte) and extended (256-byte) MIF formats, enabling serialization to and from binary and XML representations.
|
|||
|
|
|
|||
|
|
## 2. Public Interface
|
|||
|
|
|
|||
|
|
### CRC16 Class (Static)
|
|||
|
|
|
|||
|
|
- **`public static ushort ComputeChecksum(byte[] bytes)`**
|
|||
|
|
Computes the CRC-16 checksum of the input `bytes` using a precomputed lookup table initialized with polynomial `0xA001`. Returns a 16-bit unsigned integer checksum.
|
|||
|
|
|
|||
|
|
### Tilt_MIF Class
|
|||
|
|
|
|||
|
|
#### Properties (Core MIF Fields)
|
|||
|
|
|
|||
|
|
- **`public DateTime TimeStamp { get; set; }`**
|
|||
|
|
Timestamp of the last operation (e.g., XML write). Set by `WriteXML`.
|
|||
|
|
|
|||
|
|
- **`public string SystemName { get; set; }`**
|
|||
|
|
Machine name at time of XML write. Set by `WriteXML`.
|
|||
|
|
|
|||
|
|
- **`public string UserName { get; set; }`**
|
|||
|
|
Current user name at time of XML write. Set by `WriteXML`.
|
|||
|
|
|
|||
|
|
- **`public string BuildVersion { get; set; }`**
|
|||
|
|
Assembly version string (format `major.minor.build`) at time of XML write. Set by `WriteXML`.
|
|||
|
|
|
|||
|
|
- **`public uint MIFVersion { get; set; }`**
|
|||
|
|
MIF file format version (stored as `uint` in binary layout).
|
|||
|
|
|
|||
|
|
- **`public string SerialNumber { get; set; }`**
|
|||
|
|
10-character serial number, padded with nulls and trimmed on read/write.
|
|||
|
|
|
|||
|
|
- **`public ushort HardwareConfiguration { get; set; }`**
|
|||
|
|
Hardware configuration flags (16-bit unsigned integer).
|
|||
|
|
|
|||
|
|
- **`public float TemperatureCF { get; set; }`**
|
|||
|
|
Temperature calibration factor.
|
|||
|
|
|
|||
|
|
- **`public short TemperatureOffset { get; set; }`**
|
|||
|
|
Temperature calibration offset.
|
|||
|
|
|
|||
|
|
- **`public float TiltSensorCF1, TiltSensorCF2, TiltSensorCF3 { get; set; }`**
|
|||
|
|
Tilt sensor calibration factors for axes 1–3.
|
|||
|
|
|
|||
|
|
- **`public short TiltSensorOffset1, TiltSensorOffset2, TiltSensorOffset3 { get; set; }`**
|
|||
|
|
Tilt sensor offsets for axes 1–3.
|
|||
|
|
|
|||
|
|
- **`public float TiltSensorRange1, TiltSensorRange2, TiltSensorRange3 { get; set; }`**
|
|||
|
|
Tilt sensor measurement ranges (degrees) for axes 1–3.
|
|||
|
|
|
|||
|
|
- **`public float TiltCalibrationCF1` through `TiltCalibrationCF18 { get; set; }`**
|
|||
|
|
18 tilt calibration coefficients (stored in `_tiltCalibrations[0..17]`).
|
|||
|
|
|
|||
|
|
- **`public ushort MIF_CRC16 { get; set; }`**
|
|||
|
|
CRC-16 value read from or written to bytes 126–127 of the MIF file.
|
|||
|
|
|
|||
|
|
- **`public ushort ComputedCRC16 { get; set; }`**
|
|||
|
|
CRC-16 computed over bytes 0–125 of the MIF file.
|
|||
|
|
|
|||
|
|
- **`public bool ReadError { get; set; }`**
|
|||
|
|
Set to `true` if MIF CRC validation fails or `MIF_CRC16 == 0` during deserialization.
|
|||
|
|
|
|||
|
|
#### Properties (Config MIF Fields, only present in 256-byte format)
|
|||
|
|
|
|||
|
|
- **`public bool _hasConfig { get; set; }`** *(private)*
|
|||
|
|
Indicates whether the instance contains configuration data.
|
|||
|
|
|
|||
|
|
- **`public ushort Config_CRC16 { get; set; }`**
|
|||
|
|
CRC-16 value read from or written to bytes 130–131 of the 256-byte MIF file.
|
|||
|
|
|
|||
|
|
- **`public ushort ComputedCRC16_Config { get; set; }`**
|
|||
|
|
CRC-16 computed over bytes 132–255 of the 256-byte MIF file.
|
|||
|
|
|
|||
|
|
- **`public bool ReadError_Config { get; set; }`**
|
|||
|
|
Set to `true` if config CRC validation fails during deserialization.
|
|||
|
|
|
|||
|
|
- **`public string SystemID { get; set; }`**
|
|||
|
|
16-character system identifier, padded with nulls and trimmed.
|
|||
|
|
|
|||
|
|
- **`public string Location { get; set; }`**
|
|||
|
|
16-character location string, padded with nulls and trimmed.
|
|||
|
|
|
|||
|
|
- **`public float Tolerance { get; set; } = 2`**
|
|||
|
|
Default tolerance value (degrees).
|
|||
|
|
|
|||
|
|
- **`public bool ArmChecklist { get; set; }`**
|
|||
|
|
Bit 0 of `_configBits[0]`.
|
|||
|
|
|
|||
|
|
- **`public bool InvertAxis1, InvertAxis2, InvertAxis3 { get; set; }`**
|
|||
|
|
Bits 1–3 of `_configBits[0]`.
|
|||
|
|
|
|||
|
|
- **`public byte AxisToIgnore { get; set; }`**
|
|||
|
|
Bits 0–1 of `_configBits[1]` (0–2, mapped to `Axis` enum).
|
|||
|
|
|
|||
|
|
- **`public Axis AxisLabel1, AxisLabel2, AxisLabel3 { get; set; }`**
|
|||
|
|
Encoded axis labels (X/Y/Z) for each physical axis, stored in `_configBits[1]` bits 2–7.
|
|||
|
|
|
|||
|
|
- **`public float MountOffsetAxis1, MountOffsetAxis2, MountOffsetAxis3 { get; set; }`**
|
|||
|
|
Mounting offsets (degrees) for axes 1–3.
|
|||
|
|
|
|||
|
|
- **`public float TargetAngleAxis1, TargetAngleAxis2, TargetAngleAxis3 { get; set; }`**
|
|||
|
|
Target angles (degrees) for axes 1–3.
|
|||
|
|
|
|||
|
|
- **`public short PreEventADCAxis1, PreEventADCAxis2, PreEventADCAxis3 { get; set; }`**
|
|||
|
|
Pre-event ADC thresholds for axes 1–3.
|
|||
|
|
|
|||
|
|
- **`public short PostEventADCAxis1, PostEventADCAxis2, PostEventADCAxis3 { get; set; }`**
|
|||
|
|
Post-event ADC thresholds for axes 1–3.
|
|||
|
|
|
|||
|
|
#### Nested Types
|
|||
|
|
|
|||
|
|
- **`public enum MIFAttributes`**
|
|||
|
|
Enum of 30 core MIF attributes (e.g., `MIFVersion`, `SerialNumber`, `TiltCalibrationCF1`, etc.).
|
|||
|
|
|
|||
|
|
- **`public class MIFAttribute`**
|
|||
|
|
Holds a `MIFAttributes` key and its string representation (`MIFValue`).
|
|||
|
|
|
|||
|
|
- **`public enum ConfigAttributes`**
|
|||
|
|
Enum of 22 configuration attributes (e.g., `SystemID`, `Location`, `AxisLabel1`, etc.).
|
|||
|
|
|
|||
|
|
- **`public class ConfigAttribute`**
|
|||
|
|
Holds a `ConfigAttributes` key and its string representation (`ConfigValue`).
|
|||
|
|
|
|||
|
|
- **`public enum Axis`**
|
|||
|
|
`X = 0`, `Y = 1`, `Z = 2`.
|
|||
|
|
|
|||
|
|
#### Public Methods
|
|||
|
|
|
|||
|
|
- **`public List<MIFAttribute> GetTiltAttributes()`**
|
|||
|
|
Returns a list of all `MIFAttributes` with their string values (via `GetMIFAttribute`).
|
|||
|
|
|
|||
|
|
- **`public Dictionary<string, object> GetTiltAttributeDictionary()`**
|
|||
|
|
Returns a dictionary mapping `MIFAttributes.ToString()` to their actual values.
|
|||
|
|
|
|||
|
|
- **`public void SetMIFAttribute(MIFAttributes attribute, string newValue)`**
|
|||
|
|
Parses `newValue` and assigns it to the corresponding property (e.g., `ushort.Parse`, `float.Parse`, `short.Parse`, or string copy for `SerialNumber`). Throws on parse failure.
|
|||
|
|
|
|||
|
|
- **`public object GetMIFAttribute(MIFAttributes attribute)`**
|
|||
|
|
Returns the current value of the specified attribute (boxed), or `null` if unknown.
|
|||
|
|
|
|||
|
|
- **`public List<ConfigAttribute> GetTiltConfigAttributes()`**
|
|||
|
|
Returns a list of all `ConfigAttributes` with string values.
|
|||
|
|
|
|||
|
|
- **`public Dictionary<string, object> GetTiltConfigAttributeDictionary()`**
|
|||
|
|
Returns a dictionary mapping `ConfigAttributes.ToString()` to their actual values.
|
|||
|
|
|
|||
|
|
- **`public void SetConfigAttribute(ConfigAttributes attribute, string newValue)`**
|
|||
|
|
Parses `newValue` and assigns it to the corresponding config property. Supports `bool.Parse`, `float.Parse`, `byte.Parse`, `short.Parse`, and `Enum.Parse` for `Axis`.
|
|||
|
|
|
|||
|
|
- **`public object GetConfigAttribute(ConfigAttributes attribute)`**
|
|||
|
|
Returns the current value of the specified config attribute (boxed), or `null` if unknown.
|
|||
|
|
|
|||
|
|
- **`public double[] GetTiltCalibrations()`**
|
|||
|
|
Returns the 18 calibration coefficients as `double[]`.
|
|||
|
|
|
|||
|
|
- **`public void SetTiltCalibrations(float[] calibrations)`**
|
|||
|
|
Sets `_tiltCalibrations` from a `float[]`. Throws `InvalidDataException` if length ≠ 18.
|
|||
|
|
|
|||
|
|
- **`public void SetTiltCalibrations(double[] calibrations)`**
|
|||
|
|
Overload that converts `double[]` to `float[]` before calling the above.
|
|||
|
|
|
|||
|
|
- **`public byte[] GetBytes()`**
|
|||
|
|
Serializes the instance to a byte array. For 128-byte MIF: writes header, sensor data, reserved, then CRC. For 256-byte MIF: appends config section (system ID, location, tolerance, config bits, offsets, angles, ADC thresholds, reserved bytes) and its CRC. Uses `Encoding.UTF8` for strings.
|
|||
|
|
|
|||
|
|
- **`public void WriteXML(FileStream file)`**
|
|||
|
|
Serializes the instance to XML using `XmlSerializer`. Sets `TimeStamp`, `SystemName`, `UserName`, and `BuildVersion` before writing. Closes the `FileStream` in `finally`.
|
|||
|
|
|
|||
|
|
- **`public string GetVersionString()`**
|
|||
|
|
Returns assembly version as `"major.minor.build"` (e.g., `"1.2.0003"`), or `"N/A"` on exception.
|
|||
|
|
|
|||
|
|
#### Static Fields
|
|||
|
|
|
|||
|
|
- **`public static int MIF_BYTE_LENGTH = 128`**
|
|||
|
|
Length of minimal MIF file.
|
|||
|
|
|
|||
|
|
- **`public static int MIF_CONFIG_BYTE_LENGTH = 256`**
|
|||
|
|
Length of extended MIF file (with config section).
|
|||
|
|
|
|||
|
|
#### Constructors
|
|||
|
|
|
|||
|
|
- **`public Tilt_MIF()`**
|
|||
|
|
Default constructor; `_hasConfig = false`.
|
|||
|
|
|
|||
|
|
- **`public Tilt_MIF(bool hasConfig)`**
|
|||
|
|
Sets `_hasConfig` to `hasConfig`.
|
|||
|
|
|
|||
|
|
- **`public Tilt_MIF(byte[] mifBytes)`**
|
|||
|
|
Deserializes from binary. Validates CRCs for both MIF and config sections (if present). Sets `ReadError` or `ReadError_Config` on mismatch. Reads fields in order: `MIFVersion`, `_serialNumber`, `HardwareConfiguration`, `TemperatureCF`, `TemperatureOffset`, tilt sensor arrays, calibrations, reserved, CRC16, then config section (if length = 256).
|
|||
|
|
|
|||
|
|
## 3. Invariants
|
|||
|
|
|
|||
|
|
- **CRC Validation**:
|
|||
|
|
- For 128-byte MIF: `MIF_CRC16` must equal `ComputedCRC16` (computed over bytes 0–125). If `MIF_CRC16 == 0`, `ReadError` is set.
|
|||
|
|
- For 256-byte MIF: `Config_CRC16` must equal `ComputedCRC16_Config` (computed over bytes 132–255). If mismatch, `ReadError_Config` is set.
|
|||
|
|
|
|||
|
|
- **String Fields**:
|
|||
|
|
- `SerialNumber`, `SystemID`, `Location` are fixed-width (10, 16, 16 chars respectively), padded with nulls and trimmed on read/write.
|
|||
|
|
|
|||
|
|
- **Array Sizes**:
|
|||
|
|
- `_tiltSensorCF`, `_tiltSensorOffset`, `_tiltSensorRange`, `_mountOffset`, `_targetAngle`, `_preEventADC`, `_postEventADC` each have exactly 3 elements.
|
|||
|
|
- `_tiltCalibrations` has exactly 18 elements (`TILTCAL_COUNT = 18`).
|
|||
|
|
- `_configBits` has exactly 2 bytes.
|
|||
|
|
|
|||
|
|
- **Binary Layout**:
|
|||
|
|
- MIF section is always 128 bytes. Config section (if present) is 128 bytes (bytes 128–255), with CRC at offset 128+2 = 130.
|
|||
|
|
- CRCs are stored in little-endian format.
|
|||
|
|
|
|||
|
|
- **CRC Polynomial**:
|
|||
|
|
- Always uses polynomial `0xA001` (reversed from 0x8005) for CCITT CRC-16.
|
|||
|
|
|
|||
|
|
## 4. Dependencies
|
|||
|
|
|
|||
|
|
### Dependencies *of* this module:
|
|||
|
|
- **`System`** (core runtime)
|
|||
|
|
- **`System.Collections.Generic`**
|
|||
|
|
- **`System.IO`**
|
|||
|
|
- **`System.Linq`**
|
|||
|
|
- **`System.Text`**
|
|||
|
|
- **`System.Xml.Serialization`** (used in `WriteXML`)
|
|||
|
|
|
|||
|
|
### Dependencies *on* this module:
|
|||
|
|
- Not specified in source, but `Tilt_MIF` is likely used by:
|
|||
|
|
- File I/O components (to read/write `.mif` files)
|
|||
|
|
- UI or configuration tools (to inspect/edit attributes)
|
|||
|
|
- Sensor calibration or validation pipelines
|
|||
|
|
|
|||
|
|
## 5. Gotchas
|
|||
|
|
|
|||
|
|
- **String Padding Behavior**:
|
|||
|
|
`SerialNumber`, `SystemID`, and `Location` are stored as fixed-length char arrays (`char[10]`, `char[16]`). On set, `value.ToCharArray().CopyTo(...)` truncates if `value.Length > array.Length`. On get, nulls are replaced with spaces and trimmed—so `"ABC\0\0"` becomes `"ABC"`.
|
|||
|
|
|
|||
|
|
- **Config CRC Validation Logic**:
|
|||
|
|
`ComputedCRC16_Config` is computed over bytes *after* the MIF section’s CRC (i.e., bytes 132–255), *not* including the config CRC itself. This is correct for CRC-16, but easy to misread.
|
|||
|
|
|
|||
|
|
- **`PostEventADCAxis2`/`3` Bug in `SetConfigAttribute`**:
|
|||
|
|
In `SetConfigAttribute`, the cases for `PostEventADCAxis2` and `PostEventADCAxis3` incorrectly assign to `PostEventADCAxis1`:
|
|||
|
|
```csharp
|
|||
|
|
case ConfigAttributes.PostEventADCAxis2:
|
|||
|
|
PostEventADCAxis1 = short.Parse(newValue); // ❌ Should be PostEventADCAxis2
|
|||
|
|
case ConfigAttributes.PostEventADCAxis3:
|
|||
|
|
PostEventADCAxis1 = short.Parse(newValue); // ❌ Should be PostEventADCAxis3
|
|||
|
|
```
|
|||
|
|
This is likely a copy-paste error.
|
|||
|
|
|
|||
|
|
- **`GetVersionString` Fallback**:
|
|||
|
|
Returns `"N/A"` on any exception (e.g., missing assembly metadata), which may mask issues.
|
|||
|
|
|
|||
|
|
- **No Explicit Endianness Handling**:
|
|||
|
|
Uses `BitConverter` (assumes little-endian, standard on .NET Core/.NET 5+ on most platforms), but this may break on big-endian systems.
|
|||
|
|
|
|||
|
|
- **`MIF_CRC16 == 0` Check**:
|
|||
|
|
`ReadError` is set if `MIF_CRC16 == 0`, even if `ComputedCRC16 == 0`. This may be overly strict if zero is a valid (though unlikely) checksum.
|
|||
|
|
|
|||
|
|
- **`GetBytes()` Uses UTF-8 for Strings**:
|
|||
|
|
`Encoding.UTF8.GetBytes(...)` is used for `SerialNumber`, `SystemID`, `Location`. This is safe for ASCII, but non-ASCII characters will expand beyond 1 char = 1 byte, potentially corrupting fixed-length fields.
|
|||
|
|
|
|||
|
|
- **No Bounds Checking in `SetTiltCalibrations`**:
|
|||
|
|
`SetTiltCalibrations(float[])` throws `InvalidDataException` only on length mismatch, not on `null`.
|
|||
|
|
|
|||
|
|
- **`_reservedU16` and `_reservedConfigBytes`**:
|
|||
|
|
Reserved fields are read/written but never exposed or validated. Changes to layout may break compatibility.
|
|||
|
|
|
|||
|
|
- **`GetTiltCalibrations()` Returns `double[]`**:
|
|||
|
|
Converts `float` values to `double`, losing precision unnecessarily if caller expects `float`.
|
|||
|
|
|
|||
|
|
- **`WriteXML` Closes Stream**:
|
|||
|
|
`WriteXML` closes the `FileStream` in `finally`, which may be unexpected if the caller intends to reuse the stream.
|
|||
|
|
|
|||
|
|
- **No Thread Safety**:
|
|||
|
|
Static `_table` in `CRC16` is read-only after static constructor, but instance fields in `Tilt_MIF` are not thread-safe.
|
|||
|
|
|
|||
|
|
- **`AxisToIgnore` Range Not Validated**:
|
|||
|
|
`AxisToIgnore` accepts any `byte`, but only values `0–2` (or `3` for “none”) are meaningful. No validation is performed.
|
|||
|
|
|
|||
|
|
- **`Config_CRC16` Validation Skipped on Mismatch**:
|
|||
|
|
If `Config_CRC16 != ComputedCRC16_Config`, `ReadError_Config` is set but parsing continues (fields like `_systemID` may be uninitialized or stale).
|