Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ngscopeclient/scopehal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: f3be0057df3b
Choose a base ref
...
head repository: ngscopeclient/scopehal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 21ceefb5743d
Choose a head ref
  • 2 commits
  • 4 files changed
  • 1 contributor

Commits on Aug 5, 2020

  1. Copy the full SHA
    02cd779 View commit details
  2. Copy the full SHA
    21ceefb View commit details
Showing with 159 additions and 25 deletions.
  1. +87 −22 scopeprotocols/EyeDecoder2.cpp
  2. +22 −0 scopeprotocols/EyeDecoder2.h
  3. +33 −1 scopeprotocols/EyeMask.cpp
  4. +17 −2 scopeprotocols/EyeMask.h
109 changes: 87 additions & 22 deletions scopeprotocols/EyeDecoder2.cpp
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@ EyeWaveform::EyeWaveform(size_t width, size_t height, float center)
, m_height(height)
, m_totalUIs(0)
, m_centerVoltage(center)
, m_maskHitRate(0)
{
size_t npix = width*height;
m_accumdata = new int64_t[npix];
@@ -90,6 +91,10 @@ void EyeWaveform::Normalize()

EyeDecoder2::EyeDecoder2(string color)
: ProtocolDecoder(OscilloscopeChannel::CHANNEL_TYPE_EYE, color, CAT_ANALYSIS)
, m_height(0)
, m_width(0)
, m_xoff(0)
, m_xscale(0)
{
//Set up channels
m_signalNames.push_back("din");
@@ -98,9 +103,6 @@ EyeDecoder2::EyeDecoder2(string color)
m_signalNames.push_back("clk");
m_channels.push_back(NULL);

m_width = 0;
m_height = 0;

m_saturationName = "Saturation Level";
m_parameters[m_saturationName] = ProtocolDecoderParameter(ProtocolDecoderParameter::TYPE_FLOAT);
m_parameters[m_saturationName].SetFloatVal(1);
@@ -395,16 +397,15 @@ void EyeDecoder2::Refresh()
//Process the eye
size_t iclock = 0;
float yscale = m_height / m_channels[0]->GetVoltageRange();
int64_t hwidth = m_width / 2;
float fwidth = m_width / 2.0f;
float ymid = m_height / 2;
float yoff = -center*yscale + ymid;
size_t wend = waveform->m_samples.size()-1;
int64_t halfwidth = cap->m_uiWidth / 2;
float scale = fwidth / cap->m_uiWidth;
int64_t tscale = floor(cap->m_uiWidth * scale);
for(size_t i=0; i<wend; i++)
{
//If scale isn't defined yet, early out
if(m_xscale < FLT_EPSILON)
break;

//Stop when we get to the end of the clock
if(iclock + 1 >= cend)
break;
@@ -414,30 +415,30 @@ void EyeDecoder2::Refresh()
int64_t twidth = clock->m_durations[iclock];
int64_t tstart = waveform->m_offsets[i] * waveform->m_timescale + waveform->m_triggerPhase;
int64_t offset = tstart - clock->m_offsets[iclock] * clock->m_timescale;
if(offset < 0)
if(offset < -10)
continue;
if(offset > twidth)
{
iclock ++;
offset -= twidth;
offset = tstart - clock->m_offsets[iclock+1] * clock->m_timescale;
}

//Sampling clock is the middle of the UI, not the start.
//Anything more than half a UI right of the clock is negative.
if(offset > halfwidth)
offset = offset - twidth;
if(offset < -halfwidth)
continue;
//LogDebug("%zu, %zd\n", i, offset);

//Interpolate voltage
int64_t dt = (waveform->m_offsets[i+1] - waveform->m_offsets[i]) * waveform->m_timescale;
float pixel_x_f = offset * scale;
float pixel_x_f = (offset - m_xoff) * m_xscale;
float pixel_x_fround = floor(pixel_x_f);
int64_t pixel_x_round = pixel_x_fround + hwidth;
float dv = waveform->m_samples[i+1] - waveform->m_samples[i];
float dx_frac = (pixel_x_f - pixel_x_fround ) / (dt * scale );
float dx_frac = (pixel_x_f - pixel_x_fround ) / (dt * m_xscale );
float nominal_voltage = waveform->m_samples[i] + dv*dx_frac;

//Antialiasing: jitter the fractional X position by up to 1ps to fill in blank spots
pixel_x_f -= m_xscale * 0.5;
pixel_x_f += (rand() & 0xff) * m_xscale / 255.0f;

//LogDebug("%zu, %zd, %f\n", i, offset, pixel_x_f);

//Find (and sanity check) the Y coordinate
float nominal_pixel_y = nominal_voltage*yscale + yoff;
size_t y1 = static_cast<size_t>(nominal_pixel_y);
@@ -452,8 +453,10 @@ void EyeDecoder2::Refresh()
int64_t* row2 = row1 + m_width;

//Plot each point 3 times for center/left/right portions of the eye
//Map -twidth to +twidth to 0...m_width
int64_t xpos[] = {pixel_x_round, pixel_x_round + tscale, -tscale + pixel_x_round};
int64_t pixel_x_round = round(pixel_x_f);
int64_t pixel_x_round2 = round(pixel_x_f + m_xscale*cap->m_uiWidth);
int64_t pixel_x_round3 = round(pixel_x_f - m_xscale*cap->m_uiWidth);
int64_t xpos[] = { pixel_x_round, pixel_x_round2, pixel_x_round3 };
for(auto x : xpos)
{
if( (x+1 < (int64_t)m_width) && (x >= 0) )
@@ -465,15 +468,77 @@ void EyeDecoder2::Refresh()
}
}
}
fflush(stdout);

//Count total number of UIs we've integrated
cap->IntegrateUIs(clock->m_samples.size());

cap->Normalize();
SetData(cap);

//If we have an eye mask, prepare it for processing
if(m_mask.GetFileName() != "")
DoMaskTest(cap);

double dt = GetTime() - start;
total_frames ++;
total_time += dt;
LogTrace("Refresh took %.3f ms (avg %.3f)\n", dt * 1000, (total_time * 1000) / total_frames);
}

/**
@brief Checks the current capture against the eye mask
*/
void EyeDecoder2::DoMaskTest(EyeWaveform* cap)
{
//TODO: performance optimization, don't re-render mask every waveform, only when we resize

//Create the Cairo surface we're drawing on
Cairo::RefPtr< Cairo::ImageSurface > surface =
Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, m_width, m_height);
Cairo::RefPtr< Cairo::Context > cr = Cairo::Context::create(surface);

//Set up transformation to match GL's bottom-left origin
//cr->translate(0, m_height);
//cr->scale(1, -1);

//Clear to a blank background
cr->set_source_rgba(0, 0, 0, 1);
cr->rectangle(0, 0, m_width, m_height);
cr->fill();

//Software rendering
float yscale = m_height / m_channels[0]->GetVoltageRange();
m_mask.RenderForAnalysis(
cr,
cap,
m_xscale,
m_xoff,
yscale,
0,
m_height);

auto accum = cap->GetAccumData();

//Test each pixel of the eye pattern against the mask
uint32_t* data = reinterpret_cast<uint32_t*>(surface->get_data());
size_t total = 0;
size_t hits = 0;
for(size_t y=0; y<m_height; y++)
{
auto row = data + (y*m_width);
auto eyerow = accum + (y*m_width);
for(size_t x=0; x<m_width; x++)
{
//Look up the eye pattern pixel
auto bin = eyerow[x];
total += bin;

//If mask pixel isn't black, count violations
uint32_t pix = row[x];
if( (pix & 0xff) != 0)
hits += bin;
}
}

cap->SetMaskHitRate(hits * 1.0f / total);
}
22 changes: 22 additions & 0 deletions scopeprotocols/EyeDecoder2.h
Original file line number Diff line number Diff line change
@@ -75,6 +75,12 @@ class EyeWaveform : public WaveformBase

float m_saturationLevel;

float GetMaskHitRate()
{ return m_maskHitRate; }

void SetMaskHitRate(float rate)
{ m_maskHitRate = rate; }

protected:
size_t m_width;
size_t m_height;
@@ -84,6 +90,8 @@ class EyeWaveform : public WaveformBase

size_t m_totalUIs;
float m_centerVoltage;

float m_maskHitRate;
};

class EyeDecoder2 : public ProtocolDecoder
@@ -118,6 +126,16 @@ class EyeDecoder2 : public ProtocolDecoder
SetData(NULL);
}

void SetXOffset(int64_t xoff)
{
m_xoff = xoff;
}

void SetXScale(float xscale)
{
m_xscale = xscale;
}

size_t GetWidth() const
{ return m_width; }

@@ -130,10 +148,14 @@ class EyeDecoder2 : public ProtocolDecoder
PROTOCOL_DECODER_INITPROC(EyeDecoder2)

protected:
void DoMaskTest(EyeWaveform* cap);

size_t m_height;
size_t m_width;

int64_t m_xoff;
float m_xscale;

std::string m_saturationName;
std::string m_centerName;
std::string m_maskName;
34 changes: 33 additions & 1 deletion scopeprotocols/EyeMask.cpp
Original file line number Diff line number Diff line change
@@ -155,9 +155,41 @@ void EyeMask::RenderForDisplay(
float yoff,
float height) const
{
//Color is hard coded for now
cr->set_source_rgba(0, 0, 1, 0.75);
RenderInternal(cr, waveform, xscale, xoff, yscale, yoff, height);
}

void EyeMask::RenderForAnalysis(
Cairo::RefPtr<Cairo::Context> cr,
EyeWaveform* waveform,
float xscale,
float xoff,
float yscale,
float yoff,
float height) const
{
//clear background
cr->set_source_rgba(0, 0, 0, 1);
cr->move_to(-1e5, 0);
cr->line_to( 1e5, 0);
cr->line_to( 1e5, height);
cr->line_to(-1e5, height);
cr->fill();

//draw the mask
cr->set_source_rgba(1, 1, 1, 1);
RenderInternal(cr, waveform, xscale, xoff, yscale, yoff, height);
}

void EyeMask::RenderInternal(
Cairo::RefPtr<Cairo::Context> cr,
EyeWaveform* waveform,
float xscale,
float xoff,
float yscale,
float yoff,
float height) const
{
//Draw each polygon
for(auto poly : m_polygons)
{
19 changes: 17 additions & 2 deletions scopeprotocols/EyeMask.h
Original file line number Diff line number Diff line change
@@ -95,10 +95,25 @@ class EyeMask
float yoff,
float height) const;

//TODO: function to render to boolean bitmap
//TODO: function to render to nice pretty cairo bitmap
void RenderForAnalysis(
Cairo::RefPtr<Cairo::Context> cr,
EyeWaveform* waveform,
float xscale,
float xoff,
float yscale,
float yoff,
float height) const;

protected:
void RenderInternal(
Cairo::RefPtr<Cairo::Context> cr,
EyeWaveform* waveform,
float xscale,
float xoff,
float yscale,
float yoff,
float height) const;

std::string m_fname;
std::vector<EyeMaskPolygon> m_polygons;