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

Commits on Dec 18, 2020

  1. Copy the full SHA
    d9dc36c View commit details
Showing with 81 additions and 25 deletions.
  1. +1 −1 doc
  2. +1 −1 lib
  3. +45 −10 src/glscopeclient/OscilloscopeWindow.cpp
  4. +19 −11 src/glscopeclient/ScopeSyncWizard.cpp
  5. +1 −0 src/glscopeclient/ScopeSyncWizard.h
  6. +7 −0 src/glscopeclient/WaveformArea_rendering.cpp
  7. +7 −2 src/glscopeclient/main.cpp
2 changes: 1 addition & 1 deletion doc
55 changes: 45 additions & 10 deletions src/glscopeclient/OscilloscopeWindow.cpp
Original file line number Diff line number Diff line change
@@ -480,7 +480,7 @@ bool OscilloscopeWindow::OnTimer(int /*timer*/)
}

//Clean up the scope sync wizard if it's completed
if(m_syncComplete)
if(m_syncComplete && (m_scopeSyncWizard != NULL) )
{
delete m_scopeSyncWizard;
m_scopeSyncWizard = NULL;
@@ -2407,10 +2407,10 @@ bool OscilloscopeWindow::PollScopes()
if( (m_tPrimaryTrigger < 0) && m_scopes[0]->HasPendingWaveforms() )
m_tPrimaryTrigger = GetTime();

//All instruments should trigger within 100ms (arbitrary threshold) of the primary.
//All instruments should trigger within 1 sec (arbitrary threshold) of the primary.
//If it's been longer than that, something went wrong. Discard all pending data and re-arm the trigger.
double twait = GetTime() - m_tPrimaryTrigger;
if( (m_tPrimaryTrigger > 0) && ( twait > 0.1 ) )
if( (m_tPrimaryTrigger > 0) && ( twait > 1 ) )
{
LogWarning("Timed out waiting for one or more secondary instruments to trigger (%.2f ms). Resetting...\n",
twait*1000);
@@ -2438,10 +2438,6 @@ bool OscilloscopeWindow::PollScopes()
//We have data to download
had_waveforms = true;

//In multi-scope free-run mode, re-arm every instrument's trigger after we've downloaded data from all of them.
if(m_multiScopeFreeRun)
ArmTrigger(false);

//Process the waveform data from each instrument
//TODO: handle waveforms coming in faster than display framerate can keep up
pending = true;
@@ -2456,6 +2452,10 @@ bool OscilloscopeWindow::PollScopes()
//Update filters etc once every instrument has been updated
OnAllWaveformsUpdated();

//In multi-scope free-run mode, re-arm every instrument's trigger after we've processed all data
if(m_multiScopeFreeRun)
ArmTrigger(false);

//Pull as many waveforms as we can in 50 ms, then handle events
double dt = GetTime() - tstart;
if(dt > 0.05)
@@ -2730,16 +2730,51 @@ void OscilloscopeWindow::ArmTrigger(bool oneshot)
else
m_multiScopeFreeRun = false;

//In multi-scope mode, make sure all scopes are stopped with no pending waveforms
if(m_scopes.size() > 1)
{
for(ssize_t i=m_scopes.size()-1; i >= 0; i--)
{
if(m_scopes[i]->PeekTriggerArmed())
m_scopes[i]->Stop();

if(m_scopes[i]->HasPendingWaveforms())
{
LogWarning("Scope %s had pending waveforms before arming\n", m_scopes[i]->m_nickname.c_str());
m_scopes[i]->ClearPendingWaveforms();
}
}
}

for(ssize_t i=m_scopes.size()-1; i >= 0; i--)
{
if(oneshot || m_multiScopeFreeRun)
if(oneshot || (m_scopes.size() > 1) )
m_scopes[i]->StartSingleTrigger();
else
m_scopes[i]->Start();

//If we have multiple scopes, ping the scope to make sure the arm command went through
//If we have multiple scopes, ping the secondaries to make sure the arm command went through
if(i != 0)
m_scopes[i]->IDPing();
{
double start = GetTime();

while(!m_scopes[i]->PeekTriggerArmed())
{
//After 3 sec of no activity, time out
//(must be longer than the default 2 sec socket timeout)
double now = GetTime();
if( (now - start) > 3)
{
LogWarning("Timeout waiting for scope %s to arm\n", m_scopes[i]->m_nickname.c_str());
m_scopes[i]->Stop();
m_scopes[i]->StartSingleTrigger();
start = now;
}
}

//Scope is armed. Clear any garbage in the pending queue
m_scopes[i]->ClearPendingWaveforms();
}
}
m_tArm = GetTime();
m_triggerArmed = true;
30 changes: 19 additions & 11 deletions src/glscopeclient/ScopeSyncWizard.cpp
Original file line number Diff line number Diff line change
@@ -133,6 +133,7 @@ ScopeSyncWizard::ScopeSyncWizard(OscilloscopeWindow* parent)
, m_delta(0)
, m_maxSkewSamples(0)
, m_numAverages(10)
, m_shuttingDown(false)
, m_waitingForWaveform(false)
{
set_transient_for(*parent);
@@ -193,6 +194,8 @@ ScopeSyncWizard::ScopeSyncWizard(OscilloscopeWindow* parent)

ScopeSyncWizard::~ScopeSyncWizard()
{
m_shuttingDown = true;

for(auto d : m_deskewSetupPages)
delete d;
}
@@ -213,6 +216,10 @@ void ScopeSyncWizard::on_apply()

void ScopeSyncWizard::on_prepare(Gtk::Widget* page)
{
//Seems to be called during the destructor, avoid spurious events here
if(m_shuttingDown)
return;

if(page == &m_primaryProgressPage)
ConfigurePrimaryScope(m_parent->GetScope(0));

@@ -332,13 +339,10 @@ void ScopeSyncWizard::OnWaveformDataReady()
m_primaryWaveform = pw;
m_secondaryWaveform = sw;

/*
Max allowed skew between instruments is 10K points.
At 10 Gsps this is a whopping 1000 ns, typical values are in the low tens of ns.
*/
//Max allowed skew between instruments is 2.5K points for now (arbitrary limit)
m_maxSkewSamples = static_cast<int64_t>(pw->m_offsets.size() / 2);

m_maxSkewSamples = min(m_maxSkewSamples, static_cast<int64_t>(10000LL));
m_maxSkewSamples = min(m_maxSkewSamples, static_cast<int64_t>(2500LL));

m_delta = - m_maxSkewSamples;

@@ -373,7 +377,7 @@ bool ScopeSyncWizard::OnTimer()
int64_t deltaFs = m_primaryWaveform->m_timescale * d;

//Loop over samples in the primary waveform
//TODO: AVX
//TODO: Can we AVX this?
ssize_t samplesProcessed = 0;
size_t isecondary = 0;
double correlation = 0;
@@ -457,11 +461,15 @@ bool ScopeSyncWizard::OnTimer()
m_activeSecondaryPage->m_progressBar.set_fraction(1);
m_activeSecondaryPage->m_progressBar.set_text("Done");

//Average skew
//Sort the list of skews
sort(m_averageSkews.begin(), m_averageSkews.end());

//Discard the biggest and smallest two results, and average the remainder.
double sum = 0;
for(auto f : m_averageSkews)
sum += f;
skew = static_cast<int64_t>(round(sum / m_numAverages));
size_t numToAverage = m_numAverages - 4;
for(size_t i=2; i < (m_numAverages - 2); i++)
sum += m_averageSkews[i];
skew = static_cast<int64_t>(round(sum / numToAverage));
LogTrace("Average skew = %ld fs\n", skew);

//Figure out where we want the secondary to go
@@ -509,5 +517,5 @@ void ScopeSyncWizard::RequestWaveform()
{
m_parent->ArmTrigger(true);
m_waitingForWaveform = true;
Glib::signal_timeout().connect(sigc::mem_fun(*this, &ScopeSyncWizard::OnWaveformTimeout), 500);
Glib::signal_timeout().connect(sigc::mem_fun(*this, &ScopeSyncWizard::OnWaveformTimeout), 5000);
}
1 change: 1 addition & 0 deletions src/glscopeclient/ScopeSyncWizard.h
Original file line number Diff line number Diff line change
@@ -122,6 +122,7 @@ class ScopeSyncWizard : public Gtk::Assistant
int64_t m_maxSkewSamples;
std::vector<int64_t> m_averageSkews;
size_t m_numAverages;
bool m_shuttingDown;

//Trigger checks
bool m_waitingForWaveform;
7 changes: 7 additions & 0 deletions src/glscopeclient/WaveformArea_rendering.cpp
Original file line number Diff line number Diff line change
@@ -125,6 +125,13 @@ void WaveformArea::PrepareGeometry(WaveformRenderData* wdata, bool update_wavefo
return;
}

//Bail if timebase is garbage
if(pdat->m_timescale == 0)
{
wdata->m_geometryOK = false;
return;
}

//Make sure capture is the right type
auto andat = dynamic_cast<AnalogWaveform*>(pdat);
auto digdat = dynamic_cast<DigitalWaveform*>(pdat);
9 changes: 7 additions & 2 deletions src/glscopeclient/main.cpp
Original file line number Diff line number Diff line change
@@ -347,6 +347,8 @@ void ScopeThread(Oscilloscope* scope)
pthread_setname_np(pthread_self(), "ScopeThread");
#endif

auto sscope = dynamic_cast<SCPIOscilloscope*>(scope);

//Assume hyperthreading is enabled and only use one thread per physical core
omp_set_num_threads(omp_get_num_procs() / 2);

@@ -355,10 +357,13 @@ void ScopeThread(Oscilloscope* scope)
double dt = 0;
while(!g_app->IsTerminating())
{
size_t npending = scope->GetPendingWaveformCount();
//Push any pending commands
if(sscope)
sscope->GetTransport()->FlushCommandQueue();

//If the queue is too big, stop grabbing data
if(npending > 100)
size_t npending = scope->GetPendingWaveformCount();
if(npending > 20)
{
LogTrace("Queue is too big, sleeping\n");
std::this_thread::sleep_for(std::chrono::milliseconds(50));