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: 5be2100fff33
Choose a base ref
...
head repository: ngscopeclient/scopehal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: cc6719ffa941
Choose a head ref
  • 1 commit
  • 4 files changed
  • 1 contributor

Commits on Aug 15, 2020

  1. CTLEDecoder: now derived from DeEmbedDecoder and about 4.6x faster th…

    …an previous implementation
    azonenberg committed Aug 15, 2020

    Verified

    This commit was signed with the committer’s verified signature.
    makenowjust Hiroya Fujinami
    Copy the full SHA
    cc6719f View commit details
Showing with 144 additions and 237 deletions.
  1. +56 −176 scopeprotocols/CTLEDecoder.cpp
  2. +10 −12 scopeprotocols/CTLEDecoder.h
  3. +74 −48 scopeprotocols/DeEmbedDecoder.cpp
  4. +4 −1 scopeprotocols/DeEmbedDecoder.h
232 changes: 56 additions & 176 deletions scopeprotocols/CTLEDecoder.cpp
Original file line number Diff line number Diff line change
@@ -37,11 +37,10 @@ using namespace std;
// Construction / destruction

CTLEDecoder::CTLEDecoder(string color)
: ProtocolDecoder(OscilloscopeChannel::CHANNEL_TYPE_ANALOG, color, CAT_ANALYSIS)
: DeEmbedDecoder(color)
{
//Set up channels
m_signalNames.push_back("din");
m_channels.push_back(NULL);
//delete the de-embed params
m_parameters.clear();

m_dcGainName = "DC Gain (dB)";
m_parameters[m_dcGainName] = ProtocolDecoderParameter(ProtocolDecoderParameter::TYPE_FLOAT);
@@ -63,35 +62,16 @@ CTLEDecoder::CTLEDecoder(string color)
m_parameters[m_acGainName] = ProtocolDecoderParameter(ProtocolDecoderParameter::TYPE_FLOAT);
m_parameters[m_acGainName].SetFloatVal(6);

m_range = 1;
m_offset = 0;
m_min = FLT_MAX;
m_max = -FLT_MAX;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Factory methods

bool CTLEDecoder::ValidateChannel(size_t i, OscilloscopeChannel* channel)
{
if( (i == 0) && (channel->GetType() == OscilloscopeChannel::CHANNEL_TYPE_ANALOG) )
return true;
return false;
m_cachedDcGain = 1;
m_cachedZeroFreq = 1;
m_cachedPole1Freq = 1;
m_cachedPole2Freq = 1;
m_cachedAcGain = 1;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Accessors

double CTLEDecoder::GetVoltageRange()
{
return m_range;
}

double CTLEDecoder::GetOffset()
{
return m_offset;
}

string CTLEDecoder::GetProtocolName()
{
return "CTLE";
@@ -141,109 +121,44 @@ void CTLEDecoder::SetDefaultName()
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Actual decoder logic

void CTLEDecoder::ClearSweeps()
bool CTLEDecoder::LoadSparameters()
{
m_range = 1;
m_offset = 0;
m_min = FLT_MAX;
m_max = -FLT_MAX;
return true;
}

void CTLEDecoder::Refresh()
int64_t CTLEDecoder::GetGroupDelay()
{
//Get the input data
if(m_channels[0] == NULL)
{
SetData(NULL);
return;
}
auto din = dynamic_cast<AnalogWaveform*>(m_channels[0]->GetData());
if(din == NULL)
{
SetData(NULL);
return;
}

//We need meaningful data
const size_t npoints_raw = din->m_samples.size();
if(npoints_raw == 0)
{
SetData(NULL);
return;
}

//Zero pad to next power of two up
const size_t npoints = pow(2, ceil(log2(npoints_raw)));

//Format the input data as raw samples for the FFT
//TODO: handle non-uniform sample rates
float* rdin;
size_t insize = npoints * sizeof(float);

#ifdef _WIN32
rdin = (float*)_aligned_malloc(insize, 32);
#else
posix_memalign((void**)&rdin, 32, insize);
#endif

//Copy the input, then fill any extra space with zeroes
memcpy(rdin, &din->m_samples[0], npoints_raw*sizeof(float));
for(size_t i=npoints_raw; i<npoints; i++)
rdin[i] = 0;

//Set up the FFT
float* rdout;
const size_t nouts = npoints/2 + 1;

#ifdef _WIN32
rdout = (float*)_aligned_malloc(2 * nouts * sizeof(float), 32);
#else
posix_memalign((void**)&rdout, 32, 2 * nouts * sizeof(float));
#endif

//Calculate the FFT
auto plan = ffts_init_1d_real(npoints, FFTS_FORWARD);
ffts_execute(plan, &rdin[0], &rdout[0]);
ffts_free(plan);

//Calculate size of each bin
double ps = din->m_timescale * (din->m_offsets[1] - din->m_offsets[0]);
double sample_ghz = 1000 / ps;
double bin_hz = round((0.5f * sample_ghz * 1e9f) / nouts);
//no phase shift
return 0;
}

//Pull out our settings
float dcgain_db = m_parameters[m_dcGainName].GetFloatVal();
float zfreq = m_parameters[m_zeroFreqName].GetFloatVal();
float pole1 = m_parameters[m_poleFreq1Name].GetFloatVal();
float pole2 = m_parameters[m_poleFreq2Name].GetFloatVal();
float acgain_db = m_parameters[m_acGainName].GetFloatVal();
void CTLEDecoder::InterpolateSparameters(float bin_hz, bool /*invert*/, size_t nouts)
{
m_cachedBinSize = bin_hz;

//Do the actual equalization
for(size_t i=0; i<nouts; i++)
{
float freq = bin_hz * i;

float gain;

//For now, piecewise response. We should smooth this!
//How can we get a nicer looking transfer function?

//Below zero, use DC gain
float db;
if(freq <= zfreq)
db = dcgain_db;
if(freq <= m_cachedZeroFreq)
db = m_cachedDcGain;

//Then linearly rise to the pole
//should we interpolate vs F or log(f)?
else if(freq < pole1)
else if(freq < m_cachedPole1Freq)
{
float frac = (freq - zfreq) / (pole1 - zfreq);
db = dcgain_db + (acgain_db - dcgain_db) * frac;
float frac = (freq - m_cachedZeroFreq) / (m_cachedPole1Freq - m_cachedZeroFreq);
db = m_cachedDcGain + (m_cachedAcGain - m_cachedDcGain) * frac;
}

//Then flat between poles
else if(freq <= pole2)
db = acgain_db;
else if(freq <= m_cachedPole2Freq)
db = m_cachedAcGain;

//Then linear falloff
else
@@ -253,77 +168,42 @@ void CTLEDecoder::Refresh()
//db = acgain_db / scale;
}

gain = pow(10, db/20);
//Gain
m_resampledSparamAmplitudes.push_back(pow(10, db/20));

//Amplitude correction
rdout[i*2 + 0] *= gain;
rdout[i*2 + 1] *= gain;
//Zero phase for now
m_resampledSparamSines.push_back(0);
m_resampledSparamCosines.push_back(1);
}
}

//Set up the inverse FFT
float* ddout;
#ifdef _WIN32
ddout = (float*)_aligned_malloc(insize * sizeof(float), 32);
#else
posix_memalign((void**)&ddout, 32, insize * sizeof(float));
#endif

//Calculate the inverse FFT
plan = ffts_init_1d_real(npoints, FFTS_BACKWARD);
ffts_execute(plan, &rdout[0], &ddout[0]);
ffts_free(plan);

//Calculate maximum group delay for the first few S-parameter bins (approx propagation delay of the channel)
/*
auto& s21 = m_sparams[SPair(2,1)];
float max_delay = 0;
for(size_t i=0; i<s21.size()-1 && i<50; i++)
max_delay = max(max_delay, s21.GetGroupDelay(i));
int64_t groupdelay_samples = ceil( (max_delay * 1e12) / din->m_timescale);
*/
int64_t groupdelay_samples = 0;

//Set up output and copy timestamps
auto cap = new AnalogWaveform;
cap->m_startTimestamp = din->m_startTimestamp;
cap->m_startPicoseconds = din->m_startPicoseconds;
cap->m_timescale = din->m_timescale;

//Calculate bounds for the *meaningful* output data.
//Since we're phase shifting, there's gonna be some garbage response at one end of the channel.
size_t istart = groupdelay_samples;
size_t iend = npoints_raw;

//Copy waveform data after rescaling
float scale = 1.0f / npoints;
float vmin = FLT_MAX;
float vmax = -FLT_MAX;
for(size_t i=istart; i<iend; i++)
void CTLEDecoder::Refresh()
{
//Pull out our settings
float dcgain_db = m_parameters[m_dcGainName].GetFloatVal();
float zfreq = m_parameters[m_zeroFreqName].GetFloatVal();
float pole1 = m_parameters[m_poleFreq1Name].GetFloatVal();
float pole2 = m_parameters[m_poleFreq2Name].GetFloatVal();
float acgain_db = m_parameters[m_acGainName].GetFloatVal();

if(
(dcgain_db != m_cachedDcGain) ||
(zfreq != m_cachedZeroFreq) ||
(pole1 != m_cachedPole1Freq) ||
(pole2 != m_cachedPole2Freq) ||
(acgain_db != m_cachedAcGain) )
{
cap->m_offsets.push_back(din->m_offsets[i]);
cap->m_durations.push_back(din->m_durations[i]);
float v = ddout[i] * scale;
vmin = min(v, vmin);
vmax = max(v, vmax);
cap->m_samples.push_back(v);
//force re-interpolation of S-parameters
m_cachedBinSize = 0;

m_cachedDcGain = dcgain_db;
m_cachedZeroFreq = zfreq;
m_cachedPole1Freq = pole1;
m_cachedPole2Freq = pole2;
m_cachedAcGain = acgain_db;
}

//Calculate bounds
m_max = max(m_max, vmax);
m_min = min(m_min, vmin);
m_range = (m_max - m_min) * 1.05;
m_offset = -( (m_max - m_min)/2 + m_min );

SetData(cap);

//Clean up
#ifdef _WIN32
_aligned_free(rdin);
_aligned_free(rdout);
_aligned_free(ddout);
#else
free(rdin);
free(rdout);
free(ddout);
#endif
//Do the actual refresh operation
DoRefresh(false);
}

22 changes: 10 additions & 12 deletions scopeprotocols/CTLEDecoder.h
Original file line number Diff line number Diff line change
@@ -35,9 +35,9 @@
#ifndef CTLEDecoder_h
#define CTLEDecoder_h

#include "../scopehal/ProtocolDecoder.h"
#include "DeEmbedDecoder.h"

class CTLEDecoder : public ProtocolDecoder
class CTLEDecoder : public DeEmbedDecoder
{
public:
CTLEDecoder(std::string color);
@@ -50,26 +50,24 @@ class CTLEDecoder : public ProtocolDecoder
static std::string GetProtocolName();
virtual void SetDefaultName();

virtual double GetVoltageRange();
virtual double GetOffset();
virtual bool ValidateChannel(size_t i, OscilloscopeChannel* channel);

virtual void ClearSweeps();

PROTOCOL_DECODER_INITPROC(CTLEDecoder)

protected:
virtual int64_t GetGroupDelay();
virtual bool LoadSparameters();
virtual void InterpolateSparameters(float bin_hz, bool invert, size_t nouts);

std::string m_dcGainName;
std::string m_zeroFreqName;
std::string m_poleFreq1Name;
std::string m_poleFreq2Name;
std::string m_acGainName;

float m_min;
float m_max;
float m_range;
float m_offset;
float m_cachedDcGain;
float m_cachedZeroFreq;
float m_cachedPole1Freq;
float m_cachedPole2Freq;
float m_cachedAcGain;
};

#endif
Loading