.logicdata files are a bit broken #1482

This commit is contained in:
rusefi 2020-06-05 15:36:47 -04:00
parent 34daa4dd03
commit 6977a7ec39
2 changed files with 487 additions and 2 deletions

View File

@ -14,6 +14,7 @@ import com.rusefi.core.Pair;
import com.rusefi.core.Sensor;
import com.rusefi.core.SensorCentral;
import com.rusefi.io.*;
import com.rusefi.stream.LogicdataStreamFile;
import com.rusefi.stream.StreamFile;
import com.rusefi.stream.TSHighSpeedLog;
import com.rusefi.stream.VcdStreamFile;
@ -80,7 +81,9 @@ public class BinaryProtocol implements BinaryProtocolCommands {
private boolean isCompositeLoggerEnabled;
private long lastLowRpmTime = System.currentTimeMillis();
private List<StreamFile> compositeLogs = Arrays.asList(new VcdStreamFile(getFileName("rusEFI_trigger_log_")), new TSHighSpeedLog(getFileName("rusEFI_trigger_log_")));
private List<StreamFile> compositeLogs = Arrays.asList(new VcdStreamFile(getFileName("rusEFI_trigger_log_")),
new TSHighSpeedLog(getFileName("rusEFI_trigger_log_")),
new LogicdataStreamFile(getFileName("rusEFI_trigger_log_", ".logicdata")));
public boolean isClosed;
/**
@ -121,7 +124,12 @@ public class BinaryProtocol implements BinaryProtocolCommands {
@NotNull
public static String getFileName(String prefix) {
return FileLog.DIR + prefix + FileLog.getDate() + ".csv";
return getFileName(prefix, ".csv");
}
@NotNull
public static String getFileName(String prefix, String fileType) {
return FileLog.DIR + prefix + FileLog.getDate() + fileType;
}
public void doSend(final String command, boolean fireEvent) throws InterruptedException {

View File

@ -0,0 +1,477 @@
package com.rusefi.stream;
import com.rusefi.composite.CompositeEvent;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
/**
* Support for Saleae .logicdata format.
* (c) andreika 2020
*/
public class LogicdataStreamFile extends StreamFile {
private static final int frequency = 1000000;
private static final int frequencyDiv = 10;
private static final char magic = 0x7f;
private static final String title = "Data save2";
private static final int BLOCK = 0x15;
private static final int CHANNEL_BLOCK = 0x16;
private static final int SUB = 0x54;
private static final int FLAG_NOTEMPTY = 2;
private static final int FLAG_EMPTY = 5;
private static final int LOGIC4 = 0x40FD;
private static final int LOGIC8 = 0x673B;
private static final int [] CHANNEL_FLAGS = { 0x13458b, 0x0000ff, 0x00a0f9, 0x00ffff, 0x00ff00, 0xff0000, 0xf020a0, };
private static int numChannels = 6;
private static int reservedDurationInSamples = 10;
private static int realDurationInSamples = 0;
private static int scaledDurationInSamples = 0;
private static int int4or5 = 4;
private final String fileName;
public LogicdataStreamFile(String fileName) {
this.fileName = fileName;
}
/*
public static void writeLogicdata(List<CompositeEvent> events, OutputStream stream) throws IOException {
LogicdataStreamFile saver = new LogicdataStreamFile();
saver.stream = stream;
saver.writeHeader();
saver.appendEvents(events);
saver.close();
}
*/
@Override
public void append(List<CompositeEvent> events) {
try {
if (stream == null) {
stream = new FileOutputStream(fileName);
writeHeader();
}
appendEvents(events);
} catch (IOException e) {
// ignoring this one
}
}
private void appendEvents(List<CompositeEvent> events) throws IOException {
// we need at least 2 records
if (events == null || events.size() < 2)
return;
int firstRecordTs = events.get(1).getTimestamp();
int lastRecordTs = events.get(events.size() - 1).getTimestamp();
// we don't know the total duration, so we create a margin after the last record which equals to the duration of the first event
realDurationInSamples = lastRecordTs + firstRecordTs;
scaledDurationInSamples = realDurationInSamples / 4;
writeChannelDataHeader();
// we need to split the combined events into separate channels
for (int ch = 0; ch < numChannels; ch++) {
List<Integer> chDeltas = new ArrayList<Integer>();
int chPrevState = -1;
int prevTs = 0;
for (CompositeEvent event : events) {
int chState = getChannelState(ch, event);
int ts = event.getTimestamp();
if (chPrevState == -1) {
chPrevState = chState;
}
if (chState != chPrevState) {
int delta = ts - prevTs;
if (delta > 0x7fff) {
// todo: split too long events?
throw new IOException();
}
// encode state
if (chState == 0)
delta |= 0x8000;
chDeltas.add(delta);
prevTs = ts;
chPrevState = chState;
}
}
writeChannelData(ch, chDeltas, chPrevState, prevTs);
}
writeChannelDataFooter();
stream.flush();
}
private int getChannelState(int ch, CompositeEvent event) {
switch (ch) {
case 0:
return event.isPrimaryTriggerAsInt();
case 1:
return event.isSecondaryTriggerAsInt();
case 2:
return event.isTrgAsInt();
case 3:
return event.isSyncAsInt();
case 4:
return event.isCoil();
case 5:
return event.isInjector();
}
return -1;
}
////////////////////////////////////////////////////////////////////
private void writeHeader() throws IOException {
stream.write(magic);
write(title.length());
write(title);
stream.flush();
write(BLOCK);
write(SUB);
write(frequency);
write(0);
write(reservedDurationInSamples);
write(frequency / frequencyDiv);
write(0, 2);
write(numChannels);
write(BLOCK);
write(0);
write(BLOCK);
for (int i = 0; i < numChannels; i++) {
writeId(i, 1);
}
write(0);
write(BLOCK);
int4or5 = (numChannels == 4) ? 4 : 5;
writeId(int4or5, int4or5);
write(SUB);
write(0);
}
private void writeChannelHeader(int ch) throws IOException {
write(0xff);
write(ch);
write("Channel " + ch);
write(0, 2);
write(1.0);
write(0);
write(0.0);
write(1); // or 2
write(0.0); // or 1.0
// this part sounds like the 'next' pointer?
if (ch == numChannels - 1) {
write(0);
} else {
writeId(1 + ch, 1);
for (int i = 0; i < 3; i++) {
write((CHANNEL_FLAGS[ch] >> (i * 8)) & 0xff);
}
}
}
private void writeChannelDataHeader() throws IOException {
write(BLOCK);
write(scaledDurationInSamples);
write(0, 5);
write(numChannels);
write(0, 3);
writeId(0, 1);
write(0);
write(BLOCK);
write(0, 3);
for (int i = 0; i < numChannels; i++) {
writeChannelHeader(i);
}
write(BLOCK);
write(new int[]{ SUB, SUB, 0, SUB, 0, SUB });
write(0, 6);
write(BLOCK);
write(0, 2);
write(realDurationInSamples);
write(0);
write(SUB);
write(reservedDurationInSamples);
write(frequency / frequencyDiv);
write(0, 2);
write(SUB);
write(0, 2);
write(1);
write(0, 3);
writeId(int4or5, 0);
write(BLOCK);
write(new int[]{ realDurationInSamples, realDurationInSamples, realDurationInSamples });
write(0);
write(SUB);
write(0);
write(BLOCK);
write(0);
write(BLOCK);
write(SUB, 4);
write(0);
write(BLOCK);
write(frequency);
write(0, 3);
write(1);
write(0, 3);
writeId(0, 0);
write(new int[]{ 0, 1, 1, 0, 1, 0x13 });
write(SUB);
write(BLOCK);
write(0);
write(realDurationInSamples);
write(0, 2);
write(numChannels);
write(new int[]{ 1, 0, 1 });
}
private void writeChannelData(int ch, List<Integer> chDeltas, int chLastState, int lastRecord) throws IOException {
int numEdges = chDeltas.size();
if (numEdges == 0)
lastRecord = 0;
write(CHANNEL_BLOCK);
// channel#0 is somehow special...
if (ch == 0) {
write(SUB);
write(BLOCK);
}
write(ch + 1);
write(0);
write(realDurationInSamples);
write(1);
writeAs(lastRecord, 2);
int numSamplesLeft = realDurationInSamples - lastRecord;
write(numSamplesLeft);
write(chLastState);
if (numEdges == 0) { // empty
write(FLAG_EMPTY);
write(0, 30);
return;
}
write(FLAG_NOTEMPTY);
if (ch == 0) {
write(0);
write(BLOCK);
write(0, 11);
write(BLOCK);
} else {
write(0, 10);
}
write(numEdges);
write(0);
write(numEdges);
write(0);
write(numEdges);
writeEdges(chDeltas);
if (ch == 0) {
write(BLOCK);
write(0, 6);
write(BLOCK);
write(0, 6);
write(BLOCK);
} else {
write(0, 9);
}
write(1);
write(0);
write(1);
write(0);
write(1);
write(0, 16);
writeRaw(0xFF, 8);
writeRaw(0x02, 1);
writeRaw(0x00, 7);
}
private void writeEdges(List<Integer> chDeltas) throws IOException {
for (int d : chDeltas) {
stream.write(d & 0xff);
stream.write((d >> 8) & 0xff);
}
stream.write(0x00);
}
private void writeChannelDataFooter() throws IOException {
write(0, 3);
write(1);
write(1);
write(0);
write(numChannels);
}
protected void writeFooter() throws IOException {
write(BLOCK);
for (int i = 0; i < numChannels; i++) {
writeId(i, 1);
}
write(1);
writeId(numChannels, 0x15);
for (int i = 0; i < numChannels; i++) {
writeId(i, 1);
}
write(1);
write(0);
write(frequency);
write(0, 16);
write(0x01);
write(0x23); // ???
write(SUB);
write(BLOCK);
write(numChannels + 1);
write(0);
write(0xFFFFFFFFFFFFFFFFL);
write(0xFFFFFFFFL);
write(1);
write(0, 3);
write(BLOCK);
write(0);
write(BLOCK);
write(0);
write(1.0);
write(SUB);
write(0, 6);
write(1);
write(0, 4);
write(1);
write(0x29); // ???
write(SUB);
writeTimingMarker();
stream.flush();
}
private void writeTimingMarker() throws IOException {
write(BLOCK);
write(numChannels + 2);
write(0, 4);
write("Timing Marker Pair");
write("A1");
write("A2");
write(0, 2);
write(SUB);
write(0, 9);
}
private void writeId(int i1, int i2) throws IOException {
write((numChannels == 4) ? LOGIC4 : LOGIC8);
write(i1);
write(i2);
}
///////////////////////////////////////////////////////////////////////
private void write(int value, int num) throws IOException {
for (int i = 0; i < num; i++)
write(value);
}
private void write(int [] values) throws IOException {
for (int i = 0; i < values.length; i++)
write(values[i]);
}
private void writeAs(long value, int numBytes) throws IOException {
if (value == 0) {
stream.write(0);
} else {
stream.write(numBytes);
for (int i = 0; i < numBytes; i++) {
stream.write((byte)((value >> (i * 8)) & 0xff));
}
}
}
// This is the main secret of this format! :)
private void write(long value) throws IOException {
if (value < 0 || value > 0xFFFFFFFFL) {
writeAs(value, 8);
} else if (value == 0) {
stream.write(0);
} else if (value <= 0xff) {
writeAs(value, 1);
} else if (value <= 0xffff) {
writeAs(value, 2);
} else if (value <= 0xffffff) {
writeAs(value, 3);
} else {
writeAs(value, 4);
}
}
private void write(double value) throws IOException {
if (value == 0.0) {
stream.write(0);
} else {
stream.write(8);
// doubles are saved little-endian, sorry Java :)
ByteBuffer bb = ByteBuffer.allocate(8);
bb.order(ByteOrder.LITTLE_ENDIAN);
bb.putDouble(value);
bb.rewind();
for (int i = 0; i < 8; i++) {
stream.write(bb.get());
}
}
}
private void write(String str) throws IOException {
write(str.length());
for (char c : str.toCharArray()) {
stream.write(c);
}
}
private void writeRaw(int value, int num) throws IOException {
for (int i = 0; i < num; i++) {
stream.write(value);
}
}
}