rusefi/unit_tests/logicdata.cpp

500 lines
9.0 KiB
C++

/*
* @file logicdata.cpp
*
* Based on LogicdataStreamFile.java by andreika
*
* Created on: Jul 19, 2020
* @author Andrey Belomutskiy, (c) 2012-2020
*/
#include "logicdata.h"
#include <stdio.h>
#include <stdlib.h>
#include <cstdint>
#include <string.h>
#define frequency 1000000
#define frequencyDiv 10
#define magic 0x7f
#define BLOCK 0x15
#define CHANNEL_BLOCK 0x16
#define SUB 0x54
#define title "Data save2"
#define FLAG_NOTEMPTY 2
#define FLAG_NOTEMPTY_LONG 3
#define FLAG_EMPTY 5
#define LOGIC4 0x40FD
#define LOGIC8 0x673B
#define SIGN_FLAG 0x80000000L
// todo: numChannels 7 or numChannels 8 does not work? :(
#define numChannels 6
#define reservedDurationInSamples 10
static const char *channelNames[] = { "Primary", "Secondary", "TDC",
"Sync", "Coil", "Injector", "Channel 6", "Channel 7" };
static int CHANNEL_FLAGS[] = { 0x13458b, 0x0000ff, 0x00a0f9, 0x00ffff, 0x00ff00,
0xff0000, 0xf020a0, };
static FILE *ptr;
static uint32_t realDurationInSamples;
static uint32_t scaledDurationInSamples;
static void writeByte(uint8_t value) {
fwrite(&value, 1, sizeof(value), ptr);
}
static void writeAs(int64_t value, int numBytes) {
if (value == 0) {
writeByte(0);
} else {
writeByte(numBytes);
for (int i = 0; i < numBytes; i++) {
writeByte((uint8_t) ((value >> (i * 8)) & 0xff));
}
}
}
// This is the main secret of this format! :)
static void write(int64_t value) {
if (value < 0 || value > 0xFFFFFFFFL) {
writeAs(value, 8);
} else if (value == 0) {
writeByte(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);
}
}
static void writeString(const char *value) {
int len = strlen(value);
write(len);
for (int i = 0; i < len; i++) {
writeByte(value[i]);
}
}
// todo: some C++ magic would allow us to drop 'count' parameter
// todo: Look at efi::size in util
static void write(int values[], int count) {
for (int i = 0; i < count; i++) {
write(values[i]);
}
}
static void write(int value, int num) {
for (int i = 0; i < num; i++)
write(value);
}
static void writeId(int i1, int i2) {
write((numChannels == 4) ? LOGIC4 : LOGIC8);
write(i1);
write(i2);
}
static void writeHeader() {
writeByte(magic);
write(strlen(title));
writeString(title);
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);
writeId(0, 0);
write(0);
write(0);
}
static void writeDouble(double value) {
static_assert(sizeof(double) == 8);
if (value == 0.0) {
writeByte(0);
} else {
writeByte(8);
char *ptr = (char*) (void*) &value;
for (int i = 0; i < 8; i++) {
writeByte(ptr[i]);
}
}
}
static void writeChannelHeader(int ch) {
write(0xff);
write(ch);
writeString(channelNames[ch]);
write(0, 2);
writeDouble(1.0);
write(0);
writeDouble(0.0);
write(1); // or 2
writeDouble(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);
}
}
}
static void writeEdges(int64_t *chDeltas, bool useLongDeltas, int numEdges) {
for (int i = 0; i < numEdges; i++) {
uint64_t d = chDeltas[i];
// set 16-bit 'sign' flag
if (!useLongDeltas && (d & SIGN_FLAG) == SIGN_FLAG)
d = (d & 0x7fff) | (SIGN_FLAG >> 16);
writeByte((uint8_t) (d & 0xff));
writeByte((uint8_t) ((d >> 8) & 0xff));
if (useLongDeltas) {
writeByte((uint8_t) ((d >> 16) & 0xff));
writeByte((uint8_t) ((d >> 24) & 0xff));
}
}
writeByte(0x00);
}
static void writeRaw(int value, int num) {
for (int i = 0; i < num; i++) {
writeByte(value);
}
}
static void writeChannelData(int ch, int64_t *chDeltas, int chLastState,
int lastRecord, bool useLongDeltas, int numEdges) {
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);
write(lastRecord);
uint32_t numSamplesLeft = realDurationInSamples - lastRecord;
write(numSamplesLeft);
write(chLastState);
int chFlag =
(numEdges == 0) ?
FLAG_EMPTY :
(useLongDeltas ? FLAG_NOTEMPTY_LONG : FLAG_NOTEMPTY);
write(chFlag);
if (ch == 0) {
write(0);
write(BLOCK);
write(0, 11);
if (useLongDeltas) {
write(BLOCK);
write(0, 6);
}
write(BLOCK);
} else {
write(0, 10);
if (useLongDeltas) {
write(0, 5);
}
}
write(numEdges);
write(0);
write(numEdges);
write(0);
write(numEdges);
writeEdges(chDeltas, useLongDeltas, numEdges);
if (ch == 0) {
write(BLOCK);
write(0, 6);
if (!useLongDeltas) {
write(BLOCK);
write(0, 6);
}
write(BLOCK);
} else {
write(0, 4);
if (!useLongDeltas) {
write(0, 5);
}
}
if (numEdges == 0) {
write(0, 5);
return;
}
write(1);
write(0);
write(1);
write(0);
write(1);
write(0, 16);
writeRaw(0xFF, 8);
writeRaw(chFlag, 1);
writeRaw(0x00, 7);
}
static void writeChannelDataHeader() {
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);
int SUB_ARRAY[] = { SUB, SUB, 0, SUB, 0, SUB };
write(SUB_ARRAY, 6);
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(0, 0);
write(BLOCK);
int SAM_ARRAY[] = { (int)realDurationInSamples, (int)realDurationInSamples,
(int)realDurationInSamples };
write(SAM_ARRAY, 3);
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);
int ARR_6[] = { 0, 1, 1, 0, 1, 0x13 };
write(ARR_6, 6);
write(SUB);
write(BLOCK);
write(0);
write(realDurationInSamples);
write(0, 2);
write(numChannels);
int ARR_3[] = { 1, 0, 1 };
write(ARR_3, 3);
}
static void writeTimingMarker() {
write(BLOCK);
write(numChannels + 2);
write(0, 4);
writeString("Timing Marker Pair");
writeString("A1");
writeString("A2");
write(0, 2);
write(SUB);
write(0, 9);
}
static void writeChannelDataFooter() {
write(0, 3);
write(1);
write(1);
write(0);
write(numChannels);
}
static int getChannelState(int ch, const CompositeEvent* event) {
switch (ch) {
case 0:
return event->primaryTrigger;
case 1:
return event->secondaryTrigger;
case 2:
return event->isTDC;
case 3:
return event->sync;
case 4:
return event->coil;
case 5:
return event->injector;
}
return -1;
}
static void writeEvents(const std::vector<CompositeEvent>& events) {
size_t count = events.size();
// we need at least 2 records
if (count < 2)
return;
uint32_t firstRecordTs = events[1].timestamp;
uint32_t lastRecordTs = events[count - 1].timestamp;
// 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();
int64_t *chDeltas = (int64_t*) malloc(sizeof(int64_t) * count);
bool useLongDeltas = false;
for (int ch = 0; ch < numChannels; ch++) {
int chPrevState = -1;
uint32_t prevTs = 0;
int deltaCount = 0;
for (size_t i = 0; i < count; i++) {
const CompositeEvent* event = &events[i];
int chState = getChannelState(ch, event);
uint32_t ts = event->timestamp;
if (chPrevState == -1) {
chPrevState = chState;
}
if (chState != chPrevState) {
int64_t delta = ts - prevTs;
if (delta > 0x7fff) {
useLongDeltas = true;
}
// encode state
if (chState == 0)
delta |= SIGN_FLAG;
chDeltas[deltaCount++] = delta;
prevTs = ts;
chPrevState = chState;
}
}
writeChannelData(ch, chDeltas, chPrevState, prevTs, useLongDeltas,
deltaCount);
}
free(chDeltas);
writeChannelDataFooter();
}
static void writeFooter() {
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);
writeDouble(1.0);
write(SUB);
write(0, 6);
write(1);
write(0, 4);
write(1);
write(0x29); // ???
write(SUB);
writeTimingMarker();
}
void writeFile(const char * fileName, const std::vector<CompositeEvent>& events) {
ptr = fopen(fileName, "wb");
writeHeader();
writeEvents(events);
writeFooter();
fclose(ptr);
}