From 72f07f304f551a5e08629170f58e2a325e03f578 Mon Sep 17 00:00:00 2001 From: Collin Kidder Date: Mon, 7 Dec 2015 21:47:41 -0500 Subject: [PATCH] Addition of the range state window (it sort of works now too!), refactoring of some DBC code, fixed some DBC calcs, fixed a bug in the frame playback window, incremented version number to show that a lot has been done. --- SavvyCAN.pro | 9 +- config.h | 4 +- dbchandler.cpp | 62 +------ frameplaybackwindow.cpp | 2 + mainwindow.cpp | 83 ++++++---- mainwindow.h | 2 + mainwindow.ui | 12 ++ rangestatewindow.cpp | 359 ++++++++++++++++++++++++++++++++++++++++ rangestatewindow.h | 39 +++++ rangestatewindow.ui | 207 +++++++++++++++++++++++ utility.h | 61 +++++++ 11 files changed, 742 insertions(+), 98 deletions(-) create mode 100644 rangestatewindow.cpp create mode 100644 rangestatewindow.h create mode 100644 rangestatewindow.ui diff --git a/SavvyCAN.pro b/SavvyCAN.pro index ec123de..18d595e 100644 --- a/SavvyCAN.pro +++ b/SavvyCAN.pro @@ -38,7 +38,8 @@ SOURCES += main.cpp\ discretestatewindow.cpp \ scriptingwindow.cpp \ scriptcontainer.cpp \ - canfilter.cpp + canfilter.cpp \ + rangestatewindow.cpp HEADERS += mainwindow.h \ can_structs.h \ @@ -66,7 +67,8 @@ HEADERS += mainwindow.h \ discretestatewindow.h \ scriptingwindow.h \ scriptcontainer.h \ - canfilter.h + canfilter.h \ + rangestatewindow.h FORMS += mainwindow.ui \ graphingwindow.ui \ @@ -82,7 +84,8 @@ FORMS += mainwindow.ui \ mainsettingsdialog.ui \ firmwareuploaderwindow.ui \ discretestatewindow.ui \ - scriptingwindow.ui + scriptingwindow.ui \ + rangestatewindow.ui DISTFILES += diff --git a/config.h b/config.h index b3e533a..6e1c7c4 100644 --- a/config.h +++ b/config.h @@ -1,12 +1,12 @@ #ifndef CONFIG #define CONFIG -#define VERSION 143 +#define VERSION 144 //try to keep this in sync. //SavvyCAN will complain if you connect a GVRET board with a revision //less than this number. -#define CURRENT_GVRET_VER 325 //330 is the newest but it is WIP +#define CURRENT_GVRET_VER 331 #endif // CONFIG diff --git a/dbchandler.cpp b/dbchandler.cpp index db6257e..b39e35e 100644 --- a/dbchandler.cpp +++ b/dbchandler.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "utility.h" DBCHandler::DBCHandler(QObject *parent) : QObject(parent) { @@ -538,8 +539,7 @@ QString DBCHandler::processSignal(const CANFrame &frame, const DBC_SIGNAL &sig) { int64_t result = 0; - int bit; - int sigSize; + bool isSigned = false; if (sig.valType == STRING) { QString buildString; @@ -547,62 +547,10 @@ QString DBCHandler::processSignal(const CANFrame &frame, const DBC_SIGNAL &sig) int bytes = sig.signalSize / 8; for (int x = 0; x < bytes; x++) buildString.append(frame.data[startByte + x]); return buildString; - } + } - sigSize = sig.signalSize; - - if (sig.intelByteOrder) - { - bit = sig.startBit; - for (int bitpos = 0; bitpos < sigSize; bitpos++) - { - if (frame.data[bit / 8] & (1 << (bit % 8))) - result += (1ULL << bitpos); - - bit++; - } - } - else //motorola / big endian mode - { - bit = sig.startBit; - for (int bitpos = 0; bitpos < sigSize; bitpos++) - { - if (frame.data[bit / 8] & (1 << (bit % 8))) - result += (1ULL << (sigSize - bitpos - 1)); - - if ((bit % 8) == 0) - bit += 15; - else bit--; - - } - } - - if (sig.valType == SIGNED_INT) - { - int mask = (1 << (sig.signalSize - 1)); - if ((result & mask) == mask) //is the highest bit possible for this signal size set? - { - /* - * if so we need to also set every bit higher in the result int too. - * This leads to the below two lines that are nasty. Here's the theory behind that... - * If the value is signed and the highest bit is set then it is negative. To create - * a negative value out of this even though the variable result is 64 bit we have to - * run 1's all of the way up to bit 63 in result. -1 is all ones for whatever size integer - * you have. So, it's 64 1's in this case. - * signedMask is done this way: - * first you take the signal size and shift 1 up that far. Then subtract one. Lets - * see that for a 16 bit signal: - * (1 << 16) - 1 = the first 16 bits set as 1's. So far so good. We then negate the whole - * thing which flips all bits. Thus signedMask ends up with 1's everwhere that the signal - * doesn't take up in the 64 bit signed integer result. Then, result has an OR operation on - * it with the old value and -1 masked so that the the 1 bits from -1 don't overwrite bits from the - * actual signal. This extends the sign bits out so that the integer result reads as the proper negative - * value. We dont need to do any of this if the sign bit wasn't set. - */ - int signedMask = ~((1 << sig.signalSize) - 1); - result = (-1 & signedMask) | result; - } - } + if (sig.valType == SIGNED_INT) isSigned = true; + result = Utility::processIntegerSignal(frame.data, sig.startBit, sig.signalSize, sig.intelByteOrder, isSigned); double endResult = ((double)result * sig.factor) + sig.bias; result = (int) endResult; diff --git a/frameplaybackwindow.cpp b/frameplaybackwindow.cpp index 64b66fc..8e76c7c 100644 --- a/frameplaybackwindow.cpp +++ b/frameplaybackwindow.cpp @@ -388,6 +388,7 @@ void FramePlaybackWindow::btnLoadFile() currentSeqNum = 0; currentSeqItem = &seqItems[0]; } + refreshIDList(); updateFrameLabel(); } } @@ -416,6 +417,7 @@ void FramePlaybackWindow::btnLoadLive() currentSeqNum = 0; currentSeqItem = &seqItems[0]; } + refreshIDList(); updateFrameLabel(); btnStopClick(); } diff --git a/mainwindow.cpp b/mainwindow.cpp index 24fa362..19f6bef 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -110,47 +110,48 @@ MainWindow::MainWindow(QWidget *parent) : firmwareUploaderWindow = NULL; discreteStateWindow = NULL; scriptingWindow = NULL; + rangeWindow = NULL; dbcHandler = new DBCHandler; bDirty = false; inhibitFilterUpdate = false; model->setDBCHandler(dbcHandler); - connect(ui->btnConnect, SIGNAL(clicked(bool)), this, SLOT(connButtonPress())); - connect(ui->actionOpen_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleLoadFile())); - connect(ui->actionGraph_Dta, SIGNAL(triggered(bool)), this, SLOT(showGraphingWindow())); - connect(ui->actionFrame_Data_Analysis, SIGNAL(triggered(bool)), this, SLOT(showFrameDataAnalysis())); - connect(ui->btnClearFrames, SIGNAL(clicked(bool)), this, SLOT(clearFrames())); - connect(ui->actionSave_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveFile())); - connect(ui->actionSave_Filtered_Log_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveFilteredFile())); - connect(ui->actionLoad_Filter_Definition, SIGNAL(triggered(bool)), this, SLOT(handleLoadFilters())); - connect(ui->actionSave_Filter_Definition, SIGNAL(triggered(bool)), this, SLOT(handleSaveFilters())); - connect(ui->action_Playback, SIGNAL(triggered(bool)), this, SLOT(showPlaybackWindow())); - connect(ui->btnBaudSet, SIGNAL(clicked(bool)), this, SLOT(changeBaudRates())); - connect(ui->actionFlow_View, SIGNAL(triggered(bool)), this, SLOT(showFlowViewWindow())); - connect(ui->action_Custom, SIGNAL(triggered(bool)), this, SLOT(showFrameSenderWindow())); - connect(ui->actionLoad_DBC_File, SIGNAL(triggered(bool)), this, SLOT(handleLoadDBC())); - connect(ui->actionSave_DBC_File, SIGNAL(triggered(bool)), this, SLOT(handleSaveDBC())); - connect(ui->canFramesView, SIGNAL(clicked(QModelIndex)), this, SLOT(gridClicked(QModelIndex))); - connect(ui->canFramesView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(gridDoubleClicked(QModelIndex))); - connect(ui->cbInterpret, SIGNAL(toggled(bool)), this, SLOT(interpretToggled(bool))); - connect(ui->cbOverwrite, SIGNAL(toggled(bool)), this, SLOT(overwriteToggled(bool))); - connect(ui->actionEdit_Messages_Signals, SIGNAL(triggered(bool)), this, SLOT(showDBCEditor())); - connect(ui->btnCaptureToggle, SIGNAL(clicked(bool)), this, SLOT(toggleCapture())); - connect(ui->actionExit_Application, SIGNAL(triggered(bool)), this, SLOT(exitApp())); - connect(ui->actionFuzzy_Scope, SIGNAL(triggered(bool)), this, SLOT(showFuzzyScopeWindow())); - connect(ui->actionRange_State, SIGNAL(triggered(bool)), this, SLOT(showRangeWindow())); - connect(ui->actionSave_Decoded_Frames, SIGNAL(triggered(bool)), this, SLOT(handleSaveDecoded())); - connect(ui->actionSingle_Multi_State, SIGNAL(triggered(bool)), this, SLOT(showSingleMultiWindow())); - connect(ui->actionFile_Comparison, SIGNAL(triggered(bool)), this, SLOT(showComparisonWindow())); - connect(ui->actionScripting_INterface, SIGNAL(triggered(bool)), this, SLOT(showScriptingWindow())); - connect(ui->btnNormalize, SIGNAL(clicked(bool)), this, SLOT(normalizeTiming())); - connect(ui->actionPreferences, SIGNAL(triggered(bool)), this, SLOT(showSettingsDialog())); - connect(model, SIGNAL(updatedFiltersList()), this, SLOT(updateFilterList())); - connect(ui->listFilters, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(filterListItemChanged(QListWidgetItem*))); - connect(ui->btnFilterAll, SIGNAL(clicked(bool)), this, SLOT(filterSetAll())); - connect(ui->btnFilterNone, SIGNAL(clicked(bool)), this, SLOT(filterClearAll())); - connect(ui->actionFirmware_Uploader, SIGNAL(triggered(bool)), this, SLOT(showFirmwareUploaderWindow())); + connect(ui->btnConnect, &QAbstractButton::clicked, this, &MainWindow::connButtonPress); + connect(ui->actionOpen_Log_File, &QAction::triggered, this, &MainWindow::handleLoadFile); + connect(ui->actionGraph_Dta, &QAction::triggered, this, &MainWindow::showGraphingWindow); + connect(ui->actionFrame_Data_Analysis, &QAction::triggered, this, &MainWindow::showFrameDataAnalysis); + connect(ui->btnClearFrames, &QAbstractButton::clicked, this, &MainWindow::clearFrames); + connect(ui->actionSave_Log_File, &QAction::triggered, this, &MainWindow::handleSaveFile); + connect(ui->actionSave_Filtered_Log_File, &QAction::triggered, this, &MainWindow::handleSaveFilteredFile); + connect(ui->actionLoad_Filter_Definition, &QAction::triggered, this, &MainWindow::handleLoadFilters); + connect(ui->actionSave_Filter_Definition, &QAction::triggered, this, &MainWindow::handleSaveFilters); + connect(ui->action_Playback, &QAction::triggered, this, &MainWindow::showPlaybackWindow); + connect(ui->btnBaudSet, &QAbstractButton::clicked, this, &MainWindow::changeBaudRates); + connect(ui->actionFlow_View, &QAction::triggered, this, &MainWindow::showFlowViewWindow); + connect(ui->action_Custom, &QAction::triggered, this, &MainWindow::showFrameSenderWindow); + connect(ui->actionLoad_DBC_File, &QAction::triggered, this, &MainWindow::handleLoadDBC); + connect(ui->actionSave_DBC_File, &QAction::triggered, this, &MainWindow::handleSaveDBC); + connect(ui->canFramesView, &QAbstractItemView::clicked, this, &MainWindow::gridClicked); + connect(ui->canFramesView, &QAbstractItemView::doubleClicked, this, &MainWindow::gridDoubleClicked); + connect(ui->cbInterpret, &QAbstractButton::toggled, this, &MainWindow::interpretToggled); + connect(ui->cbOverwrite, &QAbstractButton::toggled, this, &MainWindow::overwriteToggled); + connect(ui->actionEdit_Messages_Signals, &QAction::triggered, this, &MainWindow::showDBCEditor); + connect(ui->btnCaptureToggle, &QAbstractButton::clicked, this, &MainWindow::toggleCapture); + connect(ui->actionExit_Application, &QAction::triggered, this, &MainWindow::exitApp); + connect(ui->actionFuzzy_Scope, &QAction::triggered, this, &MainWindow::showFuzzyScopeWindow); + connect(ui->actionRange_State_2, &QAction::triggered, this, &MainWindow::showRangeWindow); + connect(ui->actionSave_Decoded_Frames, &QAction::triggered, this, &MainWindow::handleSaveDecoded); + connect(ui->actionSingle_Multi_State_2, &QAction::triggered, this, &MainWindow::showSingleMultiWindow); + connect(ui->actionFile_Comparison, &QAction::triggered, this, &MainWindow::showComparisonWindow); + connect(ui->actionScripting_INterface, &QAction::triggered, this, &MainWindow::showScriptingWindow); + connect(ui->btnNormalize, &QAbstractButton::clicked, this, &MainWindow::normalizeTiming); + connect(ui->actionPreferences, &QAction::triggered, this, &MainWindow::showSettingsDialog); + connect(model, &CANFrameModel::updatedFiltersList, this, &MainWindow::updateFilterList); + connect(ui->listFilters, &QListWidget::itemChanged, this, &MainWindow::filterListItemChanged); + connect(ui->btnFilterAll, &QAbstractButton::clicked, this, &MainWindow::filterSetAll); + connect(ui->btnFilterNone, &QAbstractButton::clicked, this, &MainWindow::filterClearAll); + connect(ui->actionFirmware_Uploader, &QAction::triggered, this, &MainWindow::showFirmwareUploaderWindow); lbStatusConnected.setText(tr("Not connected")); updateFileStatus(); @@ -244,6 +245,12 @@ MainWindow::~MainWindow() delete scriptingWindow; } + if (rangeWindow) + { + rangeWindow->close(); + delete rangeWindow; + } + delete ui; delete dbcHandler; model->clearFrames(); @@ -1053,7 +1060,11 @@ void MainWindow::showScriptingWindow() void MainWindow::showRangeWindow() { - //not done yet + if (!rangeWindow) + { + rangeWindow = new RangeStateWindow(model->getListReference()); + } + rangeWindow->show(); } void MainWindow::showFuzzyScopeWindow() diff --git a/mainwindow.h b/mainwindow.h index c4c3bc5..33ca328 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -20,6 +20,7 @@ #include "firmwareuploaderwindow.h" #include "discretestatewindow.h" #include "scriptingwindow.h" +#include "rangestatewindow.h" namespace Ui { class MainWindow; @@ -124,6 +125,7 @@ private: DiscreteStateWindow *discreteStateWindow; FirmwareUploaderWindow *firmwareUploaderWindow; ScriptingWindow *scriptingWindow; + RangeStateWindow *rangeWindow; //various private storage QLabel lbStatusConnected; diff --git a/mainwindow.ui b/mainwindow.ui index 516ea6d..09596fc 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -304,6 +304,8 @@ + + @@ -448,6 +450,16 @@ Scripting Interface + + + Range State + + + + + Single/Multi State + + diff --git a/rangestatewindow.cpp b/rangestatewindow.cpp new file mode 100644 index 0000000..066a058 --- /dev/null +++ b/rangestatewindow.cpp @@ -0,0 +1,359 @@ +#include "rangestatewindow.h" +#include "ui_rangestatewindow.h" +#include "mainwindow.h" +#include "utility.h" + +RangeStateWindow::RangeStateWindow(const QVector *frames, QWidget *parent) : + QDialog(parent), + ui(new Ui::RangeStateWindow) +{ + ui->setupUi(this); + + modelFrames = frames; + + ui->graphSignal->xAxis->setRange(0, 8); + ui->graphSignal->yAxis->setRange(-10, 265); //run range a bit outside possible number so they aren't plotted in a hard to see place + ui->graphSignal->xAxis->setVisible(false); + ui->graphSignal->yAxis->setVisible(false); + + //ui->graphSignal->axisRect()->setupFullAxesBox(); + + ui->cbSignalMode->addItem(tr("Big Endian")); + ui->cbSignalMode->addItem(tr("Little Endian")); + ui->cbSignalMode->addItem(tr("Try Both")); + + ui->cbSignedMode->addItem(tr("Signed Value")); + ui->cbSignedMode->addItem(tr("Unsigned Value")); + ui->cbSignedMode->addItem(tr("Try Both")); + +//lambda expressions used here because these are tiny functions that don't have any reason to be full named functions +//unfortunately the fact that valueChanged is overloaded makes the syntax here HORRIBLE. That sucks. + connect(ui->spinMaxSigSize, static_cast(&QSpinBox::valueChanged), + [=](int newVal) + { + if (newVal < ui->spinMinSigSize->value()) ui->spinMinSigSize->setValue(newVal); + }); + + connect(ui->spinMinSigSize, static_cast(&QSpinBox::valueChanged), + [=](int newVal) + { + if (newVal > ui->spinMaxSigSize->value()) ui->spinMaxSigSize->setValue(newVal); + }); + + connect(ui->btnAllFilter, &QAbstractButton::clicked, + [=]() + { + for (int i = 0; i < ui->listFilter->count(); i++) + { + QListWidgetItem *item = ui->listFilter->item(i); + item->setCheckState(Qt::Checked); + idFilters[Utility::ParseStringToNum(item->text())] = true; + } + }); + + connect(ui->btnNoneFilter, &QAbstractButton::clicked, + [=]() + { + for (int i = 0; i < ui->listFilter->count(); i++) + { + QListWidgetItem *item = ui->listFilter->item(i); + item->setCheckState(Qt::Unchecked); + idFilters[Utility::ParseStringToNum(item->text())] = false; + } + }); + + connect(ui->listFilter, &QListWidget::itemChanged, + [=](QListWidgetItem *item) + { + bool isChecked = false; + int id = Utility::ParseStringToNum(item->text()); + if (item->checkState() == Qt::Checked) isChecked = true; + idFilters[id] = isChecked; + }); + + connect(ui->btnRecalc, &QAbstractButton::clicked, this, &RangeStateWindow::recalcButton); + connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int))); +} + +RangeStateWindow::~RangeStateWindow() +{ + delete ui; +} + +void RangeStateWindow::showEvent(QShowEvent* event) +{ + QDialog::showEvent(event); + + readSettings(); + + refreshFilterList(); +} + +void RangeStateWindow::closeEvent(QCloseEvent *event) +{ + Q_UNUSED(event); + writeSettings(); +} + +void RangeStateWindow::readSettings() +{ + QSettings settings; + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + resize(settings.value("RangeStateView/WindowSize", QSize(765, 615)).toSize()); + move(settings.value("RangeStateView/WindowPos", QPoint(50, 50)).toPoint()); + } +} + +void RangeStateWindow::writeSettings() +{ + QSettings settings; + + if (settings.value("Main/SaveRestorePositions", false).toBool()) + { + settings.setValue("RangeStateView/WindowSize", size()); + settings.setValue("RangeStateView/WindowPos", pos()); + } +} + +void RangeStateWindow::updatedFrames(int numFrames) +{ + CANFrame thisFrame; + if (numFrames == -1) //all frames deleted. We don't need to do a thing on this window but erase everything in the filters section + { + ui->listFilter->clear(); + idFilters.clear(); + } + else if (numFrames == -2) //all new set of frames. Reset + { + refreshFilterList(); + } + else //just got some new frames. See if we need to update the filters list. Otherwise nothing to do - no recalc happens until the button is pressed + { + for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++) + { + thisFrame = modelFrames->at(i); + if (!idFilters.contains(thisFrame.ID)) + { + idFilters.insert(thisFrame.ID, true); + QListWidgetItem* listItem = new QListWidgetItem(Utility::formatNumber(thisFrame.ID), ui->listFilter); + listItem->setFlags(listItem->flags() | Qt::ItemIsUserCheckable); // set checkable flag + listItem->setCheckState(Qt::Checked); //default all filters to be set active + } + } + } +} + +void RangeStateWindow::refreshFilterList() +{ + int id; + + idFilters.clear(); + ui->listFilter->clear(); + + for (int i = 0; i < modelFrames->length(); i++) + { + id = modelFrames->at(i).ID; + if (!idFilters.contains(id)) + { + idFilters.insert(id, true); + QListWidgetItem* listItem = new QListWidgetItem(Utility::formatNumber(id), ui->listFilter); + listItem->setFlags(listItem->flags() | Qt::ItemIsUserCheckable); // set checkable flag + listItem->setCheckState(Qt::Checked); //default all filters to be set active + } + } + + ui->listFilter->sortItems(); +} + +void RangeStateWindow::recalcButton() +{ + QHash::iterator iter; + int id; + + ui->listCandidates->clear(); + + for (iter = idFilters.begin(); iter != idFilters.end(); ++iter) + { + if (iter.value() == true) + { + qDebug() << "Processing for ID: " << iter.key(); + //so, we're supposed to process this frame ID. We'll need to create a frame cache for it + frameCache.clear(); + frameCache.reserve(modelFrames->count()); //block allocate more than enough space + id = iter.key(); + for (int j = 0; j < modelFrames->count(); j++) + { + if (modelFrames->at(j).ID == id) frameCache.append(modelFrames->at(j)); + } + //now we've got a list with all the same ID. Time to send it off for processing + signalsFactory(); + } + } +} + +/* + * Uses the settings exposed to the user to generate a set of candidate signals that should be checked. + * The user could specify signal sizes, granularity, endian type and we generate all the permutations from there + * Should process from max to min and stop when a valid signal is found (at least as an option) to declutter a bit. + * Mostly what we're interested in is the largest signal that matches +*/ +void RangeStateWindow::signalsFactory() +{ + int minSig = ui->spinMinSigSize->value(); + int maxSig = ui->spinMaxSigSize->value(); + int granularity = ui->spinGranularity->value(); + int sigType = ui->cbSignalMode->currentIndex() + 1; + int signedType = ui->cbSignedMode->currentIndex() + 1; + int maxBits = frameCache.at(0).len * 8; + int sens = ui->slideSensitivity->value(); + + for (int sigSize = maxSig; sigSize >= minSig; sigSize--) + { + for (int startBit = 0; startBit < maxBits; startBit += granularity) + { + if (sigType & 1) + { + if (signedType & 1) processSignal(startBit, sigSize, sens, true, true); + if (signedType & 2) processSignal(startBit, sigSize, sens, true, false); + } + if (sigType & 2) + { + if (signedType & 1) processSignal(startBit, sigSize, sens, false, true); + if (signedType & 2) processSignal(startBit, sigSize, sens, false, false); + } + //have to try both types even with 8 bit and smaller signals + //because they could cross byte boundaries. Could check whether they + //do and not try both types if it is impossible. + } + } +} + +/* + * Given the signal we generate the relevant data and figure out whether this signal seems to be a smooth range signal +*/ +void RangeStateWindow::processSignal(int startBit, int bitLength, int sensitivity, bool bigEndian, bool isSigned) +{ + qDebug() << "S:" << startBit << " B:" << bitLength << " X:" << sensitivity << " E:" << bigEndian; + + QVector scaledVals; + QVector diff1; + QVector diff2; + int64_t valu; + int64_t highestValue = -1000000000000LL; + int64_t lowestValue = 1000000000000LL; + double multiplier; + int numFrames = frameCache.count(); + + scaledVals.reserve(frameCache.count()); + diff1.reserve(frameCache.count() - 1); + diff2.reserve(frameCache.count() - 2); + + int i; + + for (i = 0; i < numFrames; i++) + { + valu = Utility::processIntegerSignal(frameCache.at(i).data, startBit, bitLength, !bigEndian, isSigned); + if (valu < lowestValue) lowestValue = valu; + if (valu > highestValue) highestValue = valu; + } + + qDebug() << "Min: " << lowestValue << " Max: " << highestValue; + + //multiplier is calculated such that the range of the signal is scaled down to the value of sensitivity. + //so, if we have a sensitivity of 50 then the range is scaled so that it is 50 + //this has the effect of smoothing the data a bit by completely removing the fine detail. It's irrelevant to + //an algorithm like this anyway and just gets in the way. + //Also, we're going to offset by lowest value so that everything is based at a value of 0 as the low end. + //All of this drastically modifies the actual signal but that's OK, we're looking for patterns here not + //being faithful to the signal (what a dirty cheater) + int range = highestValue - lowestValue; + multiplier = (double)sensitivity / (double)range; + + for (i = 0; i < numFrames; i++) scaledVals.append((int)((Utility::processIntegerSignal(frameCache.at(i).data, startBit, bitLength, !bigEndian, isSigned) - lowestValue) * multiplier)); + + for (i = 1; i < numFrames; i++) + { + valu = scaledVals[i-1] - scaledVals[i]; + diff1.append(valu); + } + + for (i = 1; i < diff1.count(); i++) + { + valu = diff1[i-1] - diff1[i]; + diff2.append(valu); + } + + //now the differences are all stored so let's go through and see if they seem to suggest a ramping sort of signal or not. + //for a first test lets let through any signal where the acceleration values are all much smaller than the signal range + bool isGood = true; + int comparisonValue = abs(sensitivity / 10); + qDebug() << "range: " << sensitivity << " comparisonvalue: " << comparisonValue; + int overValues = 0; + for (i = 0; i < diff2.count(); i++) + { + if (abs(diff1[i]) > comparisonValue) overValues++; + } + if (overValues > (numFrames / 5000)) isGood = false; + qDebug() << "Is this signal good: " << isGood << " Num overs: " << overValues; + if (isGood) + { + createGraph(scaledVals); + //qDebug() << "Graphing"; + QString temp; + temp = "ID: " + QString::number(frameCache.at(0).ID, 16) + " startBit: " + QString::number(startBit) + " len: " + QString::number(bitLength) + "BigEndian: " + QString::number(bigEndian) + " Signed: " + QString::number(isSigned); + ui->listCandidates->addItem(temp); + } + +} + +//graphs the vector such that the X axis is just the index into the vector and Y is perfectly graphed within the window +void RangeStateWindow::createGraph(QVector values) +{ + + int tempVal; + float minval=1000000, maxval = -100000; + + int numEntries = values.count(); + + ui->graphSignal->clearGraphs(); + + QVector x(numEntries), y(numEntries); + + for (int j = 0; j < numEntries; j++) + { + tempVal = values[j]; + + x[j] = j; + + y[j] = tempVal; + if (y[j] < minval) minval = y[j]; + if (y[j] > maxval) maxval = y[j]; + } + + qDebug() << "YMin: " << minval << " YMax: " << maxval; + + double ymin, ymax; + + ymin = minval; + + if (ymin < 0) ymin *= 1.2; + else ymin *= 0.8; + + ymax = maxval; + if (ymax < 0) ymax * 0.8; + else ymax *= 1.2; + + + ui->graphSignal->addGraph(); + ui->graphSignal->graph()->setName("Graph"); + ui->graphSignal->graph()->setData(x,y); + ui->graphSignal->graph()->setLineStyle(QCPGraph::lsLine); //connect points with lines + QPen graphPen; + graphPen.setColor(Qt::black); + graphPen.setWidth(1); + ui->graphSignal->graph()->setPen(graphPen); + ui->graphSignal->xAxis->setRange(0, numEntries); + ui->graphSignal->yAxis->setRange(ymin, ymax); + ui->graphSignal->replot(); +} diff --git a/rangestatewindow.h b/rangestatewindow.h new file mode 100644 index 0000000..b10edbe --- /dev/null +++ b/rangestatewindow.h @@ -0,0 +1,39 @@ +#ifndef RANGESTATEWINDOW_H +#define RANGESTATEWINDOW_H + +#include +#include "can_structs.h" + +namespace Ui { +class RangeStateWindow; +} + +class RangeStateWindow : public QDialog +{ + Q_OBJECT + +public: + explicit RangeStateWindow(const QVector *frames, QWidget *parent = 0); + ~RangeStateWindow(); + void showEvent(QShowEvent*); + +private slots: + void updatedFrames(int); + void recalcButton(); + +private: + Ui::RangeStateWindow *ui; + const QVector *modelFrames; + QVector frameCache; + QHash idFilters; + + void refreshFilterList(); + void closeEvent(QCloseEvent *event); + void readSettings(); + void writeSettings(); + void signalsFactory(); + void processSignal(int startBit, int bitLength, int sensitivity, bool bigEndian, bool isSigned); + void createGraph(QVector values); +}; + +#endif // RANGESTATEWINDOW_H diff --git a/rangestatewindow.ui b/rangestatewindow.ui new file mode 100644 index 0000000..f9a7507 --- /dev/null +++ b/rangestatewindow.ui @@ -0,0 +1,207 @@ + + + RangeStateWindow + + + + 0 + 0 + 763 + 938 + + + + Range State Window + + + + + + + + Candidate Signals: + + + + + + + + + + Graphical Representation of Selected Signal: + + + + + + + + 0 + 0 + + + + + + + + + + + + + + Sensitivity + + + + + + + 10 + + + 250 + + + Qt::Horizontal + + + + + + + Min Signal Size: + + + + + + + 1 + + + 32 + + + + + + + Max Signal Size: + + + + + + + 1 + + + 32 + + + + + + + Granularity: + + + + + + + 1 + + + 32 + + + + + + + Signal Mode: + + + + + + + + + + Signed Mode: + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + ID Filter: + + + + + + + + + + All + + + + + + + None + + + + + + + + + + + Recalculate Candidate Signals + + + + + + + + QCustomPlot + QWidget +
qcustomplot.h
+ 1 +
+
+ + +
diff --git a/utility.h b/utility.h index 9e86fa7..42fab5e 100644 --- a/utility.h +++ b/utility.h @@ -113,6 +113,67 @@ public: { return (value1 * (1.0 - samplePoint)) + (value2 * samplePoint); } + + static int64_t processIntegerSignal(const uint8_t *data, int startBit, int sigSize, bool littleEndian, bool isSigned) + { + + int64_t result = 0; + int bit; + + if (littleEndian) + { + bit = startBit; + for (int bitpos = 0; bitpos < sigSize; bitpos++) + { + if (data[bit / 8] & (1 << (bit % 8))) + result += (1ULL << bitpos); + bit++; + } + } + else //motorola / big endian mode + { + bit = startBit; + for (int bitpos = 0; bitpos < sigSize; bitpos++) + { + if (data[bit / 8] & (1 << (bit % 8))) + result += (1ULL << (sigSize - bitpos - 1)); + + if ((bit % 8) == 0) + bit += 15; + else bit--; + + } + } + + if (isSigned) + { + int64_t mask = (1ULL << (sigSize - 1)); + if ((result & mask) == mask) //is the highest bit possible for this signal size set? + { + /* + * if so we need to also set every bit higher in the result int too. + * This leads to the below two lines that are nasty. Here's the theory behind that... + * If the value is signed and the highest bit is set then it is negative. To create + * a negative value out of this even though the variable result is 64 bit we have to + * run 1's all of the way up to bit 63 in result. -1 is all ones for whatever size integer + * you have. So, it's 64 1's in this case. + * signedMask is done this way: + * first you take the signal size and shift 1 up that far. Then subtract one. Lets + * see that for a 16 bit signal: + * (1 << 16) - 1 = the first 16 bits set as 1's. So far so good. We then negate the whole + * thing which flips all bits. Thus signedMask ends up with 1's everwhere that the signal + * doesn't take up in the 64 bit signed integer result. Then, result has an OR operation on + * it with the old value and -1 masked so that the the 1 bits from -1 don't overwrite bits from the + * actual signal. This extends the sign bits out so that the integer result reads as the proper negative + * value. We dont need to do any of this if the sign bit wasn't set. + */ + int64_t signedMask = ~((1ULL << sigSize) - 1); + result = (-1LL & signedMask) | result; + } + } + + return result; + } }; #endif // UTILITY_H