diff --git a/candatagrid.cpp b/candatagrid.cpp index 5943faf..1c908ed 100644 --- a/candatagrid.cpp +++ b/candatagrid.cpp @@ -72,7 +72,10 @@ CANDataGrid::CANDataGrid(QWidget *parent) : bytesToDraw = 8; //default to the old behavior for (int x = 0; x < 8; x++) for (int y = 0; y < 64; y++) + { textStates[y][x] = GridTextState::NORMAL; + heatData[y * 8 + x] = 0; + } blackBrush = QBrush(Qt::black); whiteBrush = QBrush(Qt::white); @@ -83,6 +86,26 @@ CANDataGrid::CANDataGrid(QWidget *parent) : grayBrush = QBrush(QColor(230,230,230)); xOffset = 0; yOffset = 0; + + //generate the palette + + /* //this one looks sort of like a fire. It was my first idea but never really got to where I liked it + for(int x = 1; x < 256; x++) + { + int lum = x * 2; + if (lum < 40) lum = 40; + if (lum > 250) lum = 250; + fire[x] = QColor::fromHsl((int)(x / 3.5), 255 - ((256 - x) / 10), lum); + } + */ + + for(int x = 1; x < 256; x++) + { + int hue = 256 - x; + fire[x] = QColor::fromHsl(hue, 255, 127); + } + + fire[0] = Qt::black; } CANDataGrid::~CANDataGrid() @@ -241,12 +264,12 @@ void CANDataGrid::paintCommonBeginning() neededXDivisions = 32; } - if (gridMode == GridMode::CHANGED_BITS) + if (gridMode != GridMode::SIGNAL_VIEW) { bigTextSize = qMin(viewport.size().height(), viewport.size().width()) / (textRestrict * 3.25); smallTextSize = qMin(viewport.size().height(), viewport.size().width()) / (textRestrict * 4.0); } - if (gridMode == GridMode::SIGNAL_VIEW) + else { bigTextSize = qMin(viewport.size().height(), viewport.size().width()) / (textRestrict * 3.50); smallTextSize = qMin(viewport.size().height(), viewport.size().width()) / (textRestrict * 5.5); @@ -342,45 +365,53 @@ void CANDataGrid::paintGridCells() if ((thisByte & (1 << bitIdx)) == (1 << bitIdx)) thisBit = true; if ((prevByte & (1 << bitIdx)) == (1 << bitIdx)) prevBit = true; - if (thisBit) + if (gridMode == GridMode::HEAT_VIEW) { - if (prevBit) - { - if ((signalColors.count() > 0) && (gridMode == GridMode::SIGNAL_VIEW)) painter->setBrush(blackHashBrush); - else painter->setBrush(blackBrush); - } - else - { - if ((signalColors.count() > 0) && (gridMode == GridMode::SIGNAL_VIEW)) painter->setBrush(greenHashBrush); - else painter->setBrush(greenBrush); - } + painter->setBrush(QBrush(fire[heatData[bit]])); } else { - if (prevBit) + if (thisBit) { - painter->setBrush(redBrush); + if (prevBit) + { + if ((signalColors.count() > 0) && (gridMode == GridMode::SIGNAL_VIEW)) painter->setBrush(blackHashBrush); + else painter->setBrush(blackBrush); + } + else + { + if ((signalColors.count() > 0) && (gridMode == GridMode::SIGNAL_VIEW)) painter->setBrush(greenHashBrush); + else painter->setBrush(greenBrush); + } } else - { - usedSigNum = -1; - if ((usedData[byteIdx] & (1 << bitIdx)) == (1 << bitIdx)) + { + if (prevBit) { - if (gridMode == GridMode::SIGNAL_VIEW) usedSigNum = getUsedSignalNum(bit); - if (usedSigNum == -1) + painter->setBrush(redBrush); + } + else + { + usedSigNum = -1; + if ((usedData[byteIdx] & (1 << bitIdx)) == (1 << bitIdx)) { - grayBrush = QBrush(QColor(0xB6, 0xB6, 0xB6), Qt::BDiagPattern); - painter->setBrush(grayBrush); + if (gridMode == GridMode::SIGNAL_VIEW) usedSigNum = getUsedSignalNum(bit); + if (usedSigNum == -1) + { + grayBrush = QBrush(QColor(0xB6, 0xB6, 0xB6), Qt::BDiagPattern); + painter->setBrush(grayBrush); + } + else + { + int idx = usedSigNum % signalColors.length(); //can only use as many colors as have been defined + painter->setBrush(QBrush(signalColors[idx])); + } } - else - { - int idx = usedSigNum % signalColors.length(); //can only use as many colors as have been defined - painter->setBrush(QBrush(signalColors[idx])); - } + else painter->setBrush(whiteBrush); } - else painter->setBrush(whiteBrush); } } + painter->drawRect(nearX + (x * xSector), nearY + (y * ySector), xSector, ySector); switch (textStates[byteIdx][bitIdx]) { @@ -419,7 +450,7 @@ void CANDataGrid::paintGridCells() * We already have a big bitmap that tells us which signals occupy which bits so every time there is a new * signal look ahead to see if there's room in the row to just run the signal name through as long as needed. */ - if (signalNames.count() > 0) + if ( (signalNames.count() > 0) && (gridMode == GridMode::SIGNAL_VIEW) ) { painter->setFont(sigNameFont); @@ -590,3 +621,9 @@ void CANDataGrid::setUsed(unsigned char *newData, bool bUpdate = false) if (bytesToTransfer < 64) memset(refData + bytesToTransfer, 0, 64 - bytesToTransfer); if (bUpdate) this->update(); } + +void CANDataGrid::setHeat(unsigned char *newData) +{ + memcpy(heatData, newData, 512); + this->update(); +} diff --git a/candatagrid.h b/candatagrid.h index ff6fd89..623e80f 100644 --- a/candatagrid.h +++ b/candatagrid.h @@ -36,7 +36,8 @@ enum GridTextState enum GridMode { CHANGED_BITS, //traditional view, bunch of bits we color to show what's set and how the bits have changed over time - SIGNAL_VIEW //special view for DBC window where we draw the signals in the bits they take up + SIGNAL_VIEW, //special view for DBC window where we draw the signals in the bits they take up + HEAT_VIEW //another special mode where each bit is given a heat level and we color each bit based on that. Use refData for storage of those values }; extern QVector signalColors; @@ -52,6 +53,7 @@ public: void setReference(unsigned char *, bool); void updateData(unsigned char *, bool); void setUsed(unsigned char *, bool); + void setHeat(unsigned char *); void saveImage(QString filename, int width, int height); void setCellTextState(int bitPos, GridTextState state); GridTextState getCellTextState(int bitPos); @@ -76,6 +78,7 @@ private: unsigned char refData[64]; unsigned char data[64]; unsigned char usedData[64]; + unsigned char heatData[512]; int usedSignalNum[512]; //so we can specify which signal claims this bit QVector signalNames; GridTextState textStates[64][8]; //first dimension is bytes, second is bits @@ -101,6 +104,7 @@ private: QFont sigNameFont; QFontMetrics *smallMetric; QFontMetrics *largeMetric; + QColor fire[256]; void paintGridCells(); void paintCommonBeginning(); diff --git a/re/frameinfowindow.cpp b/re/frameinfowindow.cpp index 1a10fb6..0cebcf3 100644 --- a/re/frameinfowindow.cpp +++ b/re/frameinfowindow.cpp @@ -41,27 +41,40 @@ FrameInfoWindow::FrameInfoWindow(const QVector *frames, QWidget *paren { graphByte[i] = new QCustomPlot(); setupByteGraph(graphByte[i], i); - ui->gridLayout->addWidget(graphByte[i], i / 4, i & 3); + ui->gridLower->addWidget(graphByte[i], i / 4, i & 3); } - ui->graphHistogram->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes | + ui->gridUpper->addWidget(new QLabel("Heatmap"), 0, 0); + heatmap = new CANDataGrid(); + heatmap->setMode(GridMode::HEAT_VIEW); + ui->gridUpper->addWidget(heatmap, 1, 0); + + ui->gridUpper->addWidget(new QLabel("Bit Histogram"), 0, 1); + graphHistogram = new QCustomPlot(); + ui->gridUpper->addWidget(graphHistogram, 1, 1); + + ui->gridUpper->setRowMinimumHeight(0, 20); + ui->gridUpper->setRowStretch(0, 1); + ui->gridUpper->setRowStretch(1, 10); + + graphHistogram->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes | QCP::iSelectLegend | QCP::iSelectPlottables); - ui->graphHistogram->xAxis->setRange(0, 63); - ui->graphHistogram->yAxis->setRange(0, 100); + graphHistogram->xAxis->setRange(0, 63); + graphHistogram->yAxis->setRange(0, 100); QSharedPointer graphHistoLogTicker(new QCPAxisTickerLog); - ui->graphHistogram->yAxis->setTicker(graphHistoLogTicker); - ui->graphHistogram->yAxis2->setTicker(graphHistoLogTicker); - ui->graphHistogram->yAxis->setNumberFormat("eb"); // e = exponential, b = beautiful decimal powers - ui->graphHistogram->yAxis->setNumberPrecision(0); //log ticker always picks powers of 10 so no need or use for precision + graphHistogram->yAxis->setTicker(graphHistoLogTicker); + graphHistogram->yAxis2->setTicker(graphHistoLogTicker); + graphHistogram->yAxis->setNumberFormat("eb"); // e = exponential, b = beautiful decimal powers + graphHistogram->yAxis->setNumberPrecision(0); //log ticker always picks powers of 10 so no need or use for precision - ui->graphHistogram->axisRect()->setupFullAxesBox(); - ui->graphHistogram->setBufferDevicePixelRatio(1); + graphHistogram->axisRect()->setupFullAxesBox(); + graphHistogram->setBufferDevicePixelRatio(1); - ui->graphHistogram->xAxis->setLabel("Bits"); - ui->graphHistogram->yAxis->setLabel("Instances"); + graphHistogram->xAxis->setLabel("Bits"); + graphHistogram->yAxis->setLabel("Instances"); - ui->graphHistogram->legend->setVisible(false); + graphHistogram->legend->setVisible(false); ui->timeHistogram->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes | QCP::iSelectLegend | QCP::iSelectPlottables); @@ -84,15 +97,15 @@ FrameInfoWindow::FrameInfoWindow(const QVector *frames, QWidget *paren if (useOpenGL) { - ui->graphHistogram->setAntialiasedElements(QCP::aeAll); - ui->graphHistogram->setOpenGl(true); + graphHistogram->setAntialiasedElements(QCP::aeAll); + graphHistogram->setOpenGl(true); ui->timeHistogram->setAntialiasedElements(QCP::aeAll); ui->timeHistogram->setOpenGl(true); } else { - ui->graphHistogram->setOpenGl(false); - ui->graphHistogram->setAntialiasedElements(QCP::aeNone); + graphHistogram->setOpenGl(false); + graphHistogram->setAntialiasedElements(QCP::aeNone); ui->timeHistogram->setOpenGl(false); ui->timeHistogram->setAntialiasedElements(QCP::aeNone); } @@ -304,6 +317,7 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) double maxY = -1000.0; uint8_t changedBits[8]; uint8_t referenceBits[8]; + uint8_t heatVals[512]; //these two used by bitflip heatmap functionality uint8_t refByte[8]; @@ -649,6 +663,7 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) //heat map output dataBase = new QTreeWidgetItem(); dataBase->setText(0, tr("Bitchange Heatmap")); + memset(heatVals, 0, 512); //always clear the array before populating it. for (int c = 0; c < 8 * maxLen; c++) { tempItem = new QTreeWidgetItem(); @@ -659,9 +674,13 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) histGraphX.append(c); histGraphY.append(bitfieldHistogram[c]); if (bitfieldHistogram[c] > maxY) maxY = bitfieldHistogram[c]; + uint8_t heat = bitFlipHeat[c] * 255; + if ((heat < 1) && (bitFlipHeat[c] > 0.0001)) heat = 1; //make sure any little bit of heat causes at least some output + qDebug() << "Heat for bit " << c << " is " << heat; + heatVals[c] = heat; } baseNode->addChild(dataBase); - + heatmap->setHeat(heatVals); QHash>::const_iterator it = signalInstances.constBegin(); while (it != signalInstances.constEnd()) { @@ -681,19 +700,19 @@ void FrameInfoWindow::updateDetailsWindow(QString newID) ui->treeDetails->insertTopLevelItem(0, baseNode); - ui->graphHistogram->clearGraphs(); - ui->graphHistogram->addGraph(); - ui->graphHistogram->graph()->setData(histGraphX, histGraphY); - ui->graphHistogram->graph()->setLineStyle(QCPGraph::lsStepLeft); //connect points with lines + graphHistogram->clearGraphs(); + graphHistogram->addGraph(); + graphHistogram->graph()->setData(histGraphX, histGraphY); + graphHistogram->graph()->setLineStyle(QCPGraph::lsStepLeft); //connect points with lines QBrush graphBrush; graphBrush.setColor(Qt::red); graphBrush.setStyle(Qt::SolidPattern); - ui->graphHistogram->graph()->setPen(Qt::NoPen); - ui->graphHistogram->graph()->setBrush(graphBrush); - ui->graphHistogram->yAxis->setRange(0.8, maxY * 1.2); - ui->graphHistogram->yAxis->setScaleType(QCPAxis::stLogarithmic); - ui->graphHistogram->axisRect()->setupFullAxesBox(); - ui->graphHistogram->replot(); + graphHistogram->graph()->setPen(Qt::NoPen); + graphHistogram->graph()->setBrush(graphBrush); + graphHistogram->yAxis->setRange(0.8, maxY * 1.2); + graphHistogram->yAxis->setScaleType(QCPAxis::stLogarithmic); + graphHistogram->axisRect()->setupFullAxesBox(); + graphHistogram->replot(); for (int graphs = 0; graphs < 8; graphs++) { diff --git a/re/frameinfowindow.h b/re/frameinfowindow.h index 8d3998c..012c288 100644 --- a/re/frameinfowindow.h +++ b/re/frameinfowindow.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "can_structs.h" #include "bus_protocols/j1939_handler.h" #include "dbc/dbchandler.h" @@ -32,6 +33,8 @@ private slots: private: Ui::FrameInfoWindow *ui; QCustomPlot *graphByte[8]; + QCustomPlot *graphHistogram; + CANDataGrid *heatmap; QList foundID; QList frameCache; diff --git a/ui/frameinfowindow.ui b/ui/frameinfowindow.ui index 579a709..0b61394 100644 --- a/ui/frameinfowindow.ui +++ b/ui/frameinfowindow.ui @@ -22,7 +22,7 @@ 8 - + @@ -120,20 +120,10 @@ - - + + - - - Bit Histograph - - - Qt::AlignCenter - - - - - + @@ -146,7 +136,7 @@ - +