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

Commits on Oct 21, 2020

  1. Copy the full SHA
    7cfe661 View commit details
  2. Copy the full SHA
    771ccf8 View commit details
Showing with 447 additions and 334 deletions.
  1. +2 −0 scopehal/CMakeLists.txt
  2. +4 −290 scopehal/DemoOscilloscope.cpp
  3. +3 −44 scopehal/DemoOscilloscope.h
  4. +337 −0 scopehal/TestWaveformSource.cpp
  5. +101 −0 scopehal/TestWaveformSource.h
2 changes: 2 additions & 0 deletions scopehal/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -74,6 +74,8 @@ set(SCOPEHAL_SOURCES
PeakDetectionFilter.cpp
Statistic.cpp
SpectrumChannel.cpp

TestWaveformSource.cpp
)

add_library(scopehal SHARED
294 changes: 4 additions & 290 deletions scopehal/DemoOscilloscope.cpp
Original file line number Diff line number Diff line change
@@ -36,8 +36,6 @@
#include "scopehal.h"
#include "OscilloscopeChannel.h"
#include "DemoOscilloscope.h"
#include <random>
#include <complex>

using namespace std;

@@ -80,16 +78,6 @@ DemoOscilloscope::DemoOscilloscope(SCPITransport* transport)
m_channelOffset[i] = 0;
}

m_forwardPlan = NULL;
m_reversePlan = NULL;

m_cachedNumPoints = 0;
m_cachedRawSize = 0;

m_forwardInBuf = NULL;
m_forwardOutBuf = NULL;
m_reverseOutBuf = NULL;

m_sweepFreq = 1e9;

//Default sampling configuration
@@ -104,20 +92,7 @@ DemoOscilloscope::DemoOscilloscope(SCPITransport* transport)

DemoOscilloscope::~DemoOscilloscope()
{
if(m_forwardPlan)
ffts_free(m_forwardPlan);
if(m_reversePlan)
ffts_free(m_reversePlan);

m_allocator.deallocate(m_forwardInBuf);
m_allocator.deallocate(m_forwardOutBuf);
m_allocator.deallocate(m_reverseOutBuf);

m_forwardPlan = NULL;
m_reversePlan = NULL;
m_forwardInBuf = NULL;
m_forwardOutBuf = NULL;
m_reverseOutBuf = NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -417,10 +392,10 @@ bool DemoOscilloscope::AcquireData()
SequenceSet s;
auto depth = GetSampleDepth();
int64_t sampleperiod = 1e12 / m_rate;
s[m_channels[0]] = GenerateNoisySinewave(0.9, 0.0, 1000, sampleperiod, depth);
s[m_channels[1]] = GenerateNoisySinewaveMix(0.9, 0.0, M_PI_4, 1000, sweepPeriod, sampleperiod, depth);
s[m_channels[2]] = GeneratePRBS31(0.9, 96.9696, sampleperiod, depth);
s[m_channels[3]] = Generate8b10b(0.9, 800, sampleperiod, depth);
s[m_channels[0]] = m_source.GenerateNoisySinewave(0.9, 0.0, 1000, sampleperiod, depth);
s[m_channels[1]] = m_source.GenerateNoisySinewaveMix(0.9, 0.0, M_PI_4, 1000, sweepPeriod, sampleperiod, depth);
s[m_channels[2]] = m_source.GeneratePRBS31(0.9, 96.9696, sampleperiod, depth);
s[m_channels[3]] = m_source.Generate8b10b(0.9, 800, sampleperiod, depth);

//Timestamp the waveform(s)
float now = GetTime();
@@ -446,264 +421,3 @@ bool DemoOscilloscope::AcquireData()
return true;
}

/**
@brief Generates a sinewave with a bit of extra noise added
*/
WaveformBase* DemoOscilloscope::GenerateNoisySinewave(
float amplitude,
float startphase,
int64_t period,
int64_t sampleperiod,
size_t depth)
{
auto ret = new AnalogWaveform;
ret->m_timescale = sampleperiod;
ret->Resize(depth);

const float noise_amplitude = 0.010; //gaussian noise w/ 10 mV stdev

random_device rd;
mt19937 rng(rd());
normal_distribution<> noise(0, noise_amplitude);

float samples_per_cycle = period * 1.0 / sampleperiod;
float radians_per_sample = 2 * M_PI / samples_per_cycle;

//sin is +/- 1, so need to divide amplitude by 2 to get scaling factor
float scale = amplitude / 2;

for(size_t i=0; i<depth; i++)
{
ret->m_offsets[i] = i;
ret->m_durations[i] = 1;

ret->m_samples[i] = scale * sinf(i*radians_per_sample + startphase) + noise(rng);
}

return ret;
}

/**
@brief Generates a mix of two sinewaves plus some noise
*/
WaveformBase* DemoOscilloscope::GenerateNoisySinewaveMix(
float amplitude,
float startphase1,
float startphase2,
float period1,
float period2,
int64_t sampleperiod,
size_t depth)
{
auto ret = new AnalogWaveform;
ret->m_timescale = sampleperiod;
ret->Resize(depth);

const float noise_amplitude = 0.010; //gaussian noise w/ 10 mV stdev

random_device rd;
mt19937 rng(rd());
normal_distribution<> noise(0, noise_amplitude);

float radians_per_sample1 = 2 * M_PI * sampleperiod / period1;
float radians_per_sample2 = 2 * M_PI * sampleperiod / period2;

//sin is +/- 1, so need to divide amplitude by 2 to get scaling factor.
//Divide by 2 again to avoid clipping the sum of them
float scale = amplitude / 4;

for(size_t i=0; i<depth; i++)
{
ret->m_offsets[i] = i;
ret->m_durations[i] = 1;

ret->m_samples[i] = scale *
(sinf(i*radians_per_sample1 + startphase1) + sinf(i*radians_per_sample2 + startphase2))
+ noise(rng);
}

return ret;
}

WaveformBase* DemoOscilloscope::GeneratePRBS31(
float amplitude,
float period,
int64_t sampleperiod,
size_t depth)
{
auto ret = new AnalogWaveform;
ret->m_timescale = sampleperiod;
ret->Resize(depth);

//Generate the PRBS as a square wave. Interpolate zero crossings as needed.
uint32_t prbs = rand();
float scale = amplitude / 2;
float phase_to_next_edge = period;
bool value = false;
for(size_t i=0; i<depth; i++)
{
ret->m_offsets[i] = i;
ret->m_durations[i] = 1;

//Increment phase accumulator
float last_phase = phase_to_next_edge;
phase_to_next_edge -= sampleperiod;

bool last = value;
if(phase_to_next_edge < 0)
{
uint32_t next = ( (prbs >> 31) ^ (prbs >> 28) ) & 1;
prbs = (prbs << 1) | next;
value = next;

phase_to_next_edge += period;
}

//Not an edge, just repeat the value
if(last == value)
ret->m_samples[i] = value ? scale : -scale;

//Edge - interpolate
else
{
float last_voltage = last ? scale : -scale;
float cur_voltage = value ? scale : -scale;

float frac = 1 - (last_phase / sampleperiod);
float delta = cur_voltage - last_voltage;

ret->m_samples[i] = last_voltage + delta*frac;
}
}

DegradeSerialData(ret, sampleperiod, depth);

return ret;
}

WaveformBase* DemoOscilloscope::Generate8b10b(
float amplitude,
float period,
int64_t sampleperiod,
size_t depth)
{
auto ret = new AnalogWaveform;
ret->m_timescale = sampleperiod;
ret->Resize(depth);

const int patternlen = 20;
const bool pattern[patternlen] =
{
0, 0, 1, 1, 1, 1, 1, 0, 1, 0, //K28.5
1, 0, 0, 1, 0, 0, 0, 1, 0, 1 //D16.2
};

//Generate the data stream as a square wave. Interpolate zero crossings as needed.
float scale = amplitude / 2;
float phase_to_next_edge = period;
bool value = false;
int nbit = 0;
for(size_t i=0; i<depth; i++)
{
ret->m_offsets[i] = i;
ret->m_durations[i] = 1;

//Increment phase accumulator
float last_phase = phase_to_next_edge;
phase_to_next_edge -= sampleperiod;

bool last = value;
if(phase_to_next_edge < 0)
{
value = pattern[nbit ++];
if(nbit >= patternlen)
nbit = 0;

phase_to_next_edge += period;
}

//Not an edge, just repeat the value
if(last == value)
ret->m_samples[i] = value ? scale : -scale;

//Edge - interpolate
else
{
float last_voltage = last ? scale : -scale;
float cur_voltage = value ? scale : -scale;

float frac = 1 - (last_phase / sampleperiod);
float delta = cur_voltage - last_voltage;

ret->m_samples[i] = last_voltage + delta*frac;
}
}

DegradeSerialData(ret, sampleperiod, depth);

return ret;
}

/**
@brief Takes an idealized serial data stream and turns it into something less pretty
by adding noise and a band-limiting filter
*/
void DemoOscilloscope::DegradeSerialData(AnalogWaveform* cap, int64_t sampleperiod, size_t depth)
{
//RNGs
random_device rd;
mt19937 rng(rd());
normal_distribution<> noise(0, 0.01);

//Prepare for second pass: reallocate FFT buffer if sample depth changed
const size_t npoints = pow(2, ceil(log2(depth)));
size_t nouts = npoints/2 + 1;
if(m_cachedNumPoints != npoints)
{
if(m_forwardPlan)
ffts_free(m_forwardPlan);
m_forwardPlan = ffts_init_1d_real(npoints, FFTS_FORWARD);

if(m_reversePlan)
ffts_free(m_reversePlan);
m_reversePlan = ffts_init_1d_real(npoints, FFTS_BACKWARD);

m_forwardInBuf = m_allocator.allocate(npoints);
m_forwardOutBuf = m_allocator.allocate(2*nouts);
m_reverseOutBuf = m_allocator.allocate(npoints);

m_cachedNumPoints = npoints;
}

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

//Do the forward FFT
ffts_execute(m_forwardPlan, &m_forwardInBuf[0], &m_forwardOutBuf[0]);

//Simple channel response model
double sample_ghz = 1000.0 / sampleperiod;
double bin_hz = round((0.5f * sample_ghz * 1e9f) / nouts);
complex<float> pole(0, -FreqToPhase(5e9));
float prescale = abs(pole);
for(size_t i = 0; i<nouts; i++)
{
complex<float> s(0, FreqToPhase(bin_hz * i));
complex<float> h = prescale * complex<float>(1, 0) / (s - pole);

float binscale = abs(h);
m_forwardOutBuf[i*2] *= binscale; //real
m_forwardOutBuf[i*2 + 1] *= binscale; //imag
}

//Calculate the inverse FFT
ffts_execute(m_reversePlan, &m_forwardOutBuf[0], &m_reverseOutBuf[0]);

//Rescale the FFT output and copy to the output, then add noise
float fftscale = 1.0f / npoints;
for(size_t i=0; i<depth; i++)
cap->m_samples[i] = m_reverseOutBuf[i] * fftscale + noise(rng);
}
Loading