Improve display of eye diagram

This commit is contained in:
Jan Käberich 2022-10-21 19:50:06 +02:00
parent ee3c6274ad
commit b9b501bd00
6 changed files with 79 additions and 344 deletions

View File

@ -321,8 +321,7 @@ void TDRThread::run()
Fft::transform(frequencyDomain, true);
tdr.data.clear();
tdr.data.resize(fft_bins);
tdr.data.resize(fft_bins, TraceMath::Data());
for(unsigned int i = 0;i<fft_bins;i++) {
tdr.data[i].x = fs * i;

View File

@ -53,6 +53,7 @@ public:
class Data {
public:
Data() : x(){}
double x;
std::complex<double> y;
};

View File

@ -405,6 +405,20 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Trace blurring:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="traceBlurring">
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -34,7 +34,8 @@ EyeDiagramPlot::EyeDiagramPlot(TraceModel &model, QWidget *parent)
jitter(0.0000000001),
linearEdge(true),
patternbits(9),
cycles(200)
cycles(200),
traceBlurring(2)
{
plotAreaTop = 0;
plotAreaLeft = 0;
@ -179,6 +180,7 @@ void EyeDiagramPlot::fromJSON(nlohmann::json j)
patternbits = j.value("patternBits", patternbits);
cycles = j.value("cycles", cycles);
xSamples = j.value("xSamples", xSamples);
traceBlurring = j.value("traceBlurring", traceBlurring);
for(unsigned int hash : j["traces"]) {
// attempt to find the traces with this hash
@ -231,6 +233,7 @@ nlohmann::json EyeDiagramPlot::toJSON()
j["patternBits"] = patternbits;
j["cycles"] = cycles;
j["xSamples"] = xSamples;
j["traceBlurring"] = traceBlurring;
return j;
}
@ -308,6 +311,7 @@ void EyeDiagramPlot::axisSetupDialog()
ui->displayedCycles->setValue(cycles);
ui->pointsPerCycle->setValue(xSamples);
ui->traceBlurring->setValue(traceBlurring);
connect(ui->Xauto, &QCheckBox::toggled, [=](bool checked) {
ui->Xmin->setEnabled(!checked);
@ -345,6 +349,7 @@ void EyeDiagramPlot::axisSetupDialog()
cycles = ui->displayedCycles->value();
xSamples = ui->pointsPerCycle->value();
traceBlurring = ui->traceBlurring->value();
xAxis.set(xAxis.getType(), false, ui->Xauto->isChecked(), ui->Xmin->value(), ui->Xmax->value(), ui->Xdivs->value());
yAxis.set(yAxis.getType(), false, ui->Yauto->isChecked(), ui->Ymin->value(), ui->Ymax->value(), ui->Ydivs->value());
@ -547,6 +552,9 @@ void EyeDiagramPlot::draw(QPainter &p)
if(displayData->size() >= 2) {
std::lock_guard<std::mutex> guard(bufferSwitchMutex);
if((*displayData)[0].y[0] == 0.0 && (*displayData)[0].y[1] == 0.0) {
qDebug() << "detected null data, displaydata:" << displayData;
}
unsigned int pxWidth = plotAreaWidth;
unsigned int pxHeight = plotAreaBottom - plotAreaTop;
std::vector<std::vector<unsigned int>> bitmap;
@ -555,7 +563,7 @@ void EyeDiagramPlot::draw(QPainter &p)
y.resize(pxHeight, 0);
}
unsigned int highestIntensity = 0;
unsigned int numTraces = (*displayData)[0].y.size();
unsigned int numTraces = (*displayData)[displayData->size()-1].y.size();
auto addLine = [&](int x0, int y0, int x1, int y1, bool skipFirst = true) {
bool first = true;
@ -564,13 +572,19 @@ void EyeDiagramPlot::draw(QPainter &p)
first = false;
return;
}
if(x < 0 || x >= (int) pxWidth || y < 0 || y >= (int) pxHeight) {
return;
}
auto &bin = bitmap[x][y];
bin++;
if(bin > highestIntensity) {
highestIntensity = bin;
for(int i=-traceBlurring;i<=traceBlurring;i++) {
for(int j=-traceBlurring;j<=traceBlurring;j++) {
if(i*i+j*j <= traceBlurring*traceBlurring) {
if(x+i < 0 || x+i >= (int) pxWidth || y+j < 0 || y+j >= (int) pxHeight) {
return;
}
auto &bin = bitmap[x+i][y+j];
bin++;
if(bin > highestIntensity) {
highestIntensity = bin;
}
}
}
}
};
@ -588,7 +602,7 @@ void EyeDiagramPlot::draw(QPainter &p)
};
// Assemble the bitmap
for(unsigned int i=1;i<xSamples;i++) {
for(unsigned int i=1;i<displayData->size();i++) {
int x0 = xAxis.transform((*displayData)[i-1].x, 0, pxWidth);
int x1 = xAxis.transform((*displayData)[i].x, 0, pxWidth);
if((x0 < 0 && x1 < 0) || (x0 >= (int) pxWidth && x1 >= (int) pxWidth)) {
@ -602,19 +616,46 @@ void EyeDiagramPlot::draw(QPainter &p)
}
}
/*
* Only a small amount of pixels will have a lot of traces of top of each other.
* This would result in using mostly the colder colors in the intensity grading.
*
* Generate a histogram of pixel usage and create an adjustment curve to evenly
* distribute all intensity colors
*/
unsigned int hist[highestIntensity+1];
memset(hist, 0, sizeof(hist));
unsigned long total = 0;
for(unsigned int i=1;i<pxWidth;i++) {
for(unsigned int j=0;j<pxHeight;j++) {
hist[bitmap[i][j]]++;
total++;
}
}
unsigned int sum = 0;
double correctedCurve[highestIntensity+1];
correctedCurve[0] = 0.0;
total -= hist[0];
for(unsigned int i=1;i<=highestIntensity;i++) {
sum += hist[i];
correctedCurve[i] = pow((double) sum / total, 2); // not totally even distribution, x^2 seems to look better
}
// draw the bitmap
pen = QPen();
pen.setCosmetic(true);
for(unsigned int i=1;i<pxWidth;i++) {
for(unsigned int j=0;j<pxHeight;j++) {
if(bitmap[i][j] > 0) {
double value = (double) bitmap[i][j] / highestIntensity;
double value = correctedCurve[bitmap[i][j]];
pen.setColor(Util::getIntensityGradeColor(value));
p.setPen(pen);
p.drawPoint(plotAreaLeft + i + 1, plotAreaTop + j + 1);
}
}
}
} else {
qDebug() << "Empty eye data, displaydata:" << displayData;
}
if(dropPending) {
p.setOpacity(0.5);
@ -627,7 +668,7 @@ void EyeDiagramPlot::draw(QPainter &p)
p.setFont(font);
p.setOpacity(1.0);
p.setPen(Qt::white);
auto text = "Drop here to add\n" + dropTrace->name() + "\nto waterfall plot";
auto text = "Drop here to add\n" + dropTrace->name() + "\nto eye diagram";
p.drawText(plotRect, Qt::AlignCenter, text);
}
}
@ -720,6 +761,7 @@ void EyeDiagramPlot::updateThread(unsigned int xSamples)
std::vector<std::complex<double>> inVec(xSamples * (cycles + 1), 0.0); // needs to calculate one more cycle than required for the display (settling)
// resize working buffer
qDebug() << "Clearing old eye data, calcData:" << calcData;
calcData->clear();
calcData->resize(xSamples);
for(auto& s : *calcData) {
@ -879,12 +921,12 @@ void EyeDiagramPlot::updateThread(unsigned int xSamples)
// fill data from outVec
for(unsigned int i=0;i<xSamples;i++) {
(*calcData)[i].x = i * timestep;
(*calcData).at(i).x = i * timestep;
}
for(unsigned int i=xSamples;i<inVec.size();i++) {
unsigned int x = i % xSamples;
unsigned int y = i / xSamples;
(*calcData)[x].y[y] = outVec[i].real();
unsigned int y = i / xSamples - 1;
(*calcData).at(x).y.at(y) = outVec[i].real();
}
qDebug() << "Eye calculation: Convolution done";
@ -892,9 +934,14 @@ void EyeDiagramPlot::updateThread(unsigned int xSamples)
{
std::lock_guard<std::mutex> guard(bufferSwitchMutex);
// switch buffers
qDebug() << "Switching diplay buffers, calcData:" << calcData;
auto buf = displayData;
displayData = calcData;
calcData = buf;
if((*displayData)[0].y[0] == 0.0 && (*displayData)[0].y[1] == 0.0) {
qDebug() << "detected null after eye calculation";
}
qDebug() << "Buffer switch complete, displayData:" << displayData;
}
setStatus("Eye calculation complete");

View File

@ -91,6 +91,7 @@ private:
bool linearEdge;
unsigned int patternbits;
unsigned int cycles;
int traceBlurring;
int plotAreaLeft, plotAreaWidth, plotAreaBottom, plotAreaTop;

View File

@ -1,327 +0,0 @@
{
"Modes": [
{
"name": "Vector Network Analyzer",
"settings": {
"de-embedding": null,
"de-embedding_enabled": false,
"markers": null,
"sweep": {
"IFBW": 10000.0,
"frequency": {
"log": false,
"power": -10.0,
"start": 1000000.0,
"stop": 6000000000.0
},
"points": 501,
"power": {
"frequency": 1000000000.0,
"start": -30.0,
"stop": -15.0
},
"single": false,
"type": "Frequency"
},
"tiles": {
"orientation": "vertical",
"sizes": [
373,
372
],
"split": true,
"tile1": {
"orientation": "horizontal",
"sizes": [
797,
796
],
"split": true,
"tile1": {
"plot": "smithchart",
"plotsettings": {
"Z0": 50.0,
"constantLines": null,
"edge_reflection": 1.0,
"frequency_override": false,
"limit_to_edge": true,
"limit_to_span": true,
"offset_axis_x": 0.0,
"override_max": 6000000000.0,
"override_min": 0.0,
"traces": [
753243053
]
},
"split": false
},
"tile2": {
"plot": "EyeDiagram",
"plotsettings": {
"XAxis": {
"autorange": false,
"div": 7.62939453125e-09,
"max": 5.653089396158854e-08,
"min": -1.9763051350911456e-08
},
"YAxis": {
"autorange": false,
"div": 0.3814697265625,
"max": 2.9595544473356057,
"min": -2.381021724539393
},
"bitPerSymbol": 1,
"cycles": 200,
"datarate": 100000000.0,
"falltime": 1e-09,
"highlevel": 1.0,
"jitter": 1e-10,
"linearEdge": true,
"lowlevel": 0.0,
"noise": 0.01,
"patternBits": 9,
"risetime": 1e-09,
"traces": [
3153058534
],
"xSamples": 200
},
"split": false
}
},
"tile2": {
"orientation": "horizontal",
"sizes": [
797,
796
],
"split": true,
"tile1": {
"plot": "EyeDiagram",
"plotsettings": {
"XAxis": {
"autorange": true,
"div": 1e-07,
"max": 1e-06,
"min": 0.0
},
"YAxis": {
"autorange": true,
"div": 0.1,
"max": 1.2,
"min": -0.2
},
"bitPerSymbol": 2,
"cycles": 200,
"datarate": 2000000.0,
"falltime": 1e-09,
"highlevel": 1.0,
"jitter": 4e-10,
"linearEdge": true,
"lowlevel": 0.0,
"noise": 0.03,
"patternBits": 9,
"risetime": 1e-09,
"traces": [
2663219575
],
"xSamples": 200
},
"split": false
},
"tile2": {
"plot": "smithchart",
"plotsettings": {
"Z0": 50.0,
"constantLines": null,
"edge_reflection": 1.0,
"frequency_override": false,
"limit_to_edge": true,
"limit_to_span": true,
"offset_axis_x": 0.0,
"override_max": 6000000000.0,
"override_min": 0.0,
"traces": [
1360958916
]
},
"split": false
}
}
},
"traces": [
{
"color": "#ffff00",
"hash": 753243053,
"livetype": 0,
"math": null,
"math_enabled": false,
"name": "S11",
"parameter": "S11",
"paused": false,
"reflection": true,
"type": "Live",
"velocityFactor": 0.66,
"visible": true
},
{
"color": "#0000ff",
"hash": 3153058534,
"livetype": 0,
"math": null,
"math_enabled": false,
"name": "S12",
"parameter": "S12",
"paused": false,
"reflection": false,
"type": "Live",
"velocityFactor": 0.66,
"visible": true
},
{
"color": "#00ff00",
"hash": 2663219575,
"livetype": 0,
"math": null,
"math_enabled": false,
"name": "S21",
"parameter": "S21",
"paused": false,
"reflection": false,
"type": "Live",
"velocityFactor": 0.66,
"visible": true
},
{
"color": "#ff0000",
"hash": 1360958916,
"livetype": 0,
"math": null,
"math_enabled": false,
"name": "S22",
"parameter": "S22",
"paused": false,
"reflection": true,
"type": "Live",
"velocityFactor": 0.66,
"visible": true
}
]
},
"type": "Vector Network Analyzer"
},
{
"name": "Signal Generator",
"settings": {
"frequency": 1000000000.0,
"port": 0,
"power": 0.0,
"sweep": {
"dwell": 1.0,
"enabled": false,
"span": 0.0,
"steps": 100.0
}
},
"type": "Signal Generator"
},
{
"name": "Spectrum Analyzer",
"settings": {
"markers": null,
"sweep": {
"acquisition": {
"RBW": 100.0,
"detector": "+Peak",
"signal ID": true,
"window": "Kaiser"
},
"frequency": {
"start": 999975000.0,
"stop": 1000025000.0
},
"single": false,
"trackingGenerator": {
"enabled": false,
"offset": 0.0,
"port": 1,
"power": -20.0
}
},
"tiles": {
"plot": "XY-plot",
"plotsettings": {
"XAxis": {
"div": 5000.0,
"log": false,
"max": 1000025000.0,
"min": 999975000.0,
"mode": "Use Span",
"type": "Frequency"
},
"YPrimary": {
"autorange": false,
"div": 10.0,
"log": false,
"max": 0.0,
"min": -120.0,
"traces": [
3115643686,
1375490686
],
"type": "Magnitude"
},
"YSecondary": {
"autorange": true,
"div": 0.0,
"log": false,
"max": 1.0,
"min": -1.0,
"traces": null,
"type": "Disabled"
},
"limitLines": null
},
"split": false
},
"traces": [
{
"color": "#ffff00",
"hash": 1375490686,
"livetype": 0,
"math": null,
"math_enabled": false,
"name": "PORT1",
"parameter": "PORT1",
"paused": false,
"reflection": false,
"type": "Live",
"velocityFactor": 0.66,
"visible": true
},
{
"color": "#0000ff",
"hash": 3115643686,
"livetype": 0,
"math": null,
"math_enabled": false,
"name": "PORT2",
"parameter": "PORT2",
"paused": false,
"reflection": false,
"type": "Live",
"velocityFactor": 0.66,
"visible": true
}
]
},
"type": "Spectrum Analyzer"
}
],
"Reference": {
"Mode": "Internal",
"Output": "Off"
},
"activeMode": "Vector Network Analyzer",
"version": "1.5.0-alpha.1-329f4487e"
}