Skip to content

Commit 6da56fc

Browse files
marknolanclaude
andcommitted
DEV-793 Support midday/midnight splits of LSM6DSV tagged-FIFO data blocks
The gen-1 accelerometers (LIS2DW12/LSM6DS3) get midday/midnight CSV splits through the generic fixed-size-packet path: each split half byte-advances by dataPacketSize*sampleCount. The LSM6DSV tagged FIFO cannot be walked that way (its raw layout interleaves accel/gyro/mag/timestamp entries behind a count prefix), which is why the byte-offset walk carried a deliberate fail-loud IllegalStateException for split LSM6DSV blocks. Replaces the guard with an entry-aware split: both halves of a split block are handed the SAME raw block bytes - the LSM6DSV entry parser selects each half's aligned-sample range from the part's metadata-time sampleCount (first half = leading range, second half = trailing range), mag entries are assigned to halves by FIFO arrival order relative to the aligned boundary, and the byte walk advances by nothing for the first half and by the raw on-disk size for the second, keeping every subsequent block aligned. The metadata-time sample-split already worked in the aligned-sample domain and is unchanged. Validated with an RWC-shifted copy of the DEV-793 B9 recording (GSR 128 Hz + accel 60 Hz straddling 00:00 device time): accel CSV ends 23:59:59.985 / resumes 00:00:00.001 and GSR ends 23:59:59.995 / resumes 00:00:00.003 (one sample period each), and the concatenated split output is byte-for-byte identical to the unsplit parse (58,752 GSR + 26,705 accel rows). Full ASM_PC_00005 suite green (130 tests) - all existing LSM6DSV references bit-identical through the parser refactor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0381fe4 commit 6da56fc

2 files changed

Lines changed: 56 additions & 14 deletions

File tree

ShimmerDriver/src/main/java/com/shimmerresearch/verisense/VerisenseDevice.java

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,9 +2027,20 @@ private void parseDataBlockDataLsm6dsv(DataBlockDetails dataBlockDetails, byte[]
20272027
int numEntries = (byteBuffer[currentByteIndex] & 0xFF) | ((byteBuffer[currentByteIndex+1] & 0xFF) << 8);
20282028
int entriesStart = currentByteIndex + 2;
20292029

2030+
boolean accelEn = isSensorEnabled(Configuration.Verisense.SENSOR_ID.LSM6DSV_ACCEL);
2031+
boolean gyroEn = isSensorEnabled(Configuration.Verisense.SENSOR_ID.LSM6DSV_GYRO);
2032+
boolean magEn = isSensorEnabled(Configuration.Verisense.SENSOR_ID.LSM6DSV_MAG);
2033+
2034+
// The aligned (accel/gyro) reference stream drives per-sample timing and, for
2035+
// midday/midnight-split blocks, defines each half's share of the FIFO. Each mag
2036+
// entry records how many reference entries preceded it in the FIFO so it can be
2037+
// assigned to the correct half by arrival order.
2038+
int refTag = accelEn ? LSM6DSV_TAG_ACCEL : LSM6DSV_TAG_GYRO;
20302039
List<byte[]> accelSamples = new ArrayList<byte[]>();
20312040
List<byte[]> gyroSamples = new ArrayList<byte[]>();
20322041
List<byte[]> magSamples = new ArrayList<byte[]>();
2042+
List<Integer> magAlignedPos = new ArrayList<Integer>();
2043+
int alignedSeen = 0;
20332044
for(int k=0;k<numEntries;k++) {
20342045
int eo = entriesStart + k*7;
20352046
int tag = ((byteBuffer[eo] & 0xFF) >> 3) & 0x1F;
@@ -2038,17 +2049,42 @@ private void parseDataBlockDataLsm6dsv(DataBlockDetails dataBlockDetails, byte[]
20382049
System.arraycopy(byteBuffer, eo+1, xyz, 0, 6);
20392050
if(tag==LSM6DSV_TAG_ACCEL) { accelSamples.add(xyz); }
20402051
else if(tag==LSM6DSV_TAG_GYRO) { gyroSamples.add(xyz); }
2041-
else { magSamples.add(xyz); }
2052+
else {
2053+
magSamples.add(xyz);
2054+
magAlignedPos.add(alignedSeen);
2055+
}
2056+
if(tag==refTag) { alignedSeen++; }
20422057
}
20432058
// tag 0x04 (timestamp) intentionally skipped
20442059
}
20452060

2046-
boolean accelEn = isSensorEnabled(Configuration.Verisense.SENSOR_ID.LSM6DSV_ACCEL);
2047-
boolean gyroEn = isSensorEnabled(Configuration.Verisense.SENSOR_ID.LSM6DSV_GYRO);
2048-
boolean magEn = isSensorEnabled(Configuration.Verisense.SENSOR_ID.LSM6DSV_MAG);
2061+
int alignedCountFull = accelEn ? accelSamples.size() : (gyroEn ? gyroSamples.size() : 0);
2062+
2063+
// Midday/midnight-split support: both halves of a split block are handed the SAME
2064+
// raw block bytes (see the byte-offset walk in PayloadContentsDetailsV8orAbove);
2065+
// each half parses only its own aligned-sample range. The metadata-time split set
2066+
// each part's sampleCount in the aligned-sample domain.
2067+
int alignedFrom = 0;
2068+
int alignedTo = alignedCountFull;
2069+
if(dataBlockDetails.isFirstPartOfSplitDataBlock()) {
2070+
alignedTo = Math.min(dataBlockDetails.getSampleCount(), alignedCountFull);
2071+
} else if(dataBlockDetails.isSecondPartOfSplitDataBlock()) {
2072+
alignedFrom = Math.max(0, alignedCountFull - dataBlockDetails.getSampleCount());
2073+
}
2074+
int alignedCount = alignedTo - alignedFrom;
20492075

2050-
int alignedCount = accelEn ? accelSamples.size() : (gyroEn ? gyroSamples.size() : 0);
2051-
int magCount = magEn ? magSamples.size() : 0;
2076+
// Mag entries are assigned to a split half by FIFO arrival order relative to the
2077+
// aligned boundary: an entry that arrived before the first aligned sample of the
2078+
// second half belongs to the first half.
2079+
List<byte[]> magSamplesPart = new ArrayList<byte[]>();
2080+
if(magEn) {
2081+
for(int i=0;i<magSamples.size();i++) {
2082+
int pos = magAlignedPos.get(i);
2083+
boolean inPart = dataBlockDetails.isSecondPartOfSplitDataBlock() ? (pos > alignedFrom) : (pos <= alignedTo);
2084+
if(inPart) { magSamplesPart.add(magSamples.get(i)); }
2085+
}
2086+
}
2087+
int magCount = magSamplesPart.size();
20522088
dataBlockDetails.setupOjcArray(alignedCount + magCount);
20532089

20542090
// SensorDetails for per-stream enable toggling. Accel + gyro are synchronous (same
@@ -2078,7 +2114,7 @@ private void parseDataBlockDataLsm6dsv(DataBlockDetails dataBlockDetails, byte[]
20782114
if(magDet!=null) { magDet.setIsEnabled(false); }
20792115
int alignedPacketSize = getExpectedDataPacketSize(SENSORS.LSM6DSV);
20802116
double timeMsCurrentSample = startTimeMs;
2081-
for(int i=0;i<alignedCount;i++) {
2117+
for(int i=alignedFrom;i<alignedTo;i++) {
20822118
byte[] byteBuf = new byte[alignedPacketSize];
20832119
int offset = 0;
20842120
if(accelEn && i<accelSamples.size()) { System.arraycopy(accelSamples.get(i), 0, byteBuf, offset, 6); }
@@ -2092,7 +2128,7 @@ private void parseDataBlockDataLsm6dsv(DataBlockDetails dataBlockDetails, byte[]
20922128
}
20932129
}
20942130

2095-
// Pass 2: mag stream (only mag enabled), spread evenly across the block duration
2131+
// Pass 2: mag stream (only mag enabled), spread evenly across this part's duration
20962132
if(magCount>0) {
20972133
if(accelDet!=null) { accelDet.setIsEnabled(false); }
20982134
if(gyroDet!=null) { gyroDet.setIsEnabled(false); }
@@ -2112,7 +2148,7 @@ private void parseDataBlockDataLsm6dsv(DataBlockDetails dataBlockDetails, byte[]
21122148
double magTimeMs = startTimeMs;
21132149
for(int i=0;i<magCount;i++) {
21142150
byte[] byteBuf = new byte[magPacketSize];
2115-
System.arraycopy(magSamples.get(i), 0, byteBuf, 0, 6);
2151+
System.arraycopy(magSamplesPart.get(i), 0, byteBuf, 0, 6);
21162152
ObjectCluster ojcCurrent = buildMsgForSensorList(byteBuf, commType, dataBlockDetails.listOfSensorClassKeys, magTimeMs);
21172153
dataHandler(ojcCurrent);
21182154
dataBlockDetails.setOjcArrayAtIndex(ojcIndex++, ojcCurrent);

ShimmerDriver/src/main/java/com/shimmerresearch/verisense/payloaddesign/PayloadContentsDetailsV8orAbove.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -420,12 +420,18 @@ public void parsePayloadSensorData() {
420420
// dataPacketSize*sampleCount recomputation cannot reproduce).
421421
if(dataBlockDetails.isFirstPartOfSplitDataBlock() || dataBlockDetails.isSecondPartOfSplitDataBlock()) {
422422
if(dataBlockDetails.datablockSensorId==DATABLOCK_SENSOR_ID.LSM6DSV) {
423-
// A split FIFO block cannot be byte-walked from sample counts; supporting
424-
// it needs a FIFO-entry-aware split. Fail loudly rather than misparse
425-
// every subsequent block (no such recording exists yet - see DEV-793).
426-
throw new IllegalStateException("Midday/midnight-split LSM6DSV FIFO data blocks are not supported by the byte-offset walk");
423+
// A split tagged-FIFO block cannot be byte-walked from recomputed
424+
// sample counts (the raw layout interleaves tag/mag/timestamp
425+
// entries). Instead BOTH halves are handed the same raw block bytes
426+
// and the LSM6DSV entry parser selects each half's aligned-sample
427+
// range - so the first half advances by nothing and the second half
428+
// advances by the raw on-disk size, keeping subsequent blocks aligned.
429+
if(dataBlockDetails.isSecondPartOfSplitDataBlock()) {
430+
currentByteIndex += dataBlockDetails.getQtySensorDataBytesInDatablockRaw();
431+
}
432+
} else {
433+
currentByteIndex += dataBlockDetails.qtySensorDataBytesInDatablock;
427434
}
428-
currentByteIndex += dataBlockDetails.qtySensorDataBytesInDatablock;
429435
} else {
430436
currentByteIndex += dataBlockDetails.getQtySensorDataBytesInDatablockRaw();
431437
}

0 commit comments

Comments
 (0)