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: 3623e1b9210e
Choose a base ref
...
head repository: ngscopeclient/scopehal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c45d7b5689ac
Choose a head ref
  • 3 commits
  • 1 file changed
  • 2 contributors

Commits on Jun 3, 2020

  1. Retrigger when acquisition is complete.

    In addition to retriggering, I also reduced the scope of lock(m_mutex)
    to just the acquisition phase, just as it is done for the LeCroy scope.
    
    This increases the responsiveness of the STOP button such that it reacts
    immediately to the event. Without it, there are 4 or more AcquireData
    calls before the STOP button event kicks in. During that time, the
    LeCroyOscilloscope::Stop() is stalled at the lock(m_mutex) phase.
    tomverbeure committed Jun 3, 2020
    Copy the full SHA
    e879d6f View commit details
  2. Copy the full SHA
    87c242c View commit details
  3. Merge pull request #137 from tomverbeure/retrigger

    Siglent: Retrigger after waveforms have been captured
    azonenberg authored Jun 3, 2020
    Copy the full SHA
    c45d7b5 View commit details
Showing with 141 additions and 127 deletions.
  1. +141 −127 scopehal/SiglentSCPIOscilloscope.cpp
268 changes: 141 additions & 127 deletions scopehal/SiglentSCPIOscilloscope.cpp
Original file line number Diff line number Diff line change
@@ -141,162 +141,176 @@ void SiglentSCPIOscilloscope::ReadWaveDescriptorBlock(SiglentWaveformDesc_t *des

bool SiglentSCPIOscilloscope::AcquireData(bool toQueue)
{
lock_guard<recursive_mutex> lock(m_mutex);

LogDebug("Acquire data\n");

double start = GetTime();

//Read the wavedesc for every enabled channel
vector<struct SiglentWaveformDesc_t*> wavedescs;
bool enabled[4] = {false};
BulkCheckChannelEnableState();
for(unsigned int i=0; i<m_analogChannelCount; i++)
enabled[i] = IsChannelEnabled(i);
map<int, vector<WaveformBase*> > pending_waveforms;

for(unsigned int i=0; i<m_analogChannelCount; i++)
{
wavedescs.push_back(new struct SiglentWaveformDesc_t);
if(enabled[i])
{
m_transport->SendCommand(m_channels[i]->GetHwname() + ":WF? DESC");
// TODO: a bunch of error checking...
ReadWaveDescriptorBlock(wavedescs[i], i);
LogDebug("name %s, number: %u\n",wavedescs[i]->InstrumentName,
wavedescs[i]->InstrumentNumber);
}
}
lock_guard<recursive_mutex> lock(m_mutex);

// grab the actual waveforms
BulkCheckChannelEnableState();
for(unsigned int i=0; i<m_analogChannelCount; i++)
enabled[i] = IsChannelEnabled(i);

//TODO: WFSU in outer loop and WF in inner loop
map<int, vector<WaveformBase*> > pending_waveforms;
unsigned int num_sequences = 1;
for(unsigned int chanNr=0; chanNr<m_analogChannelCount; chanNr++)
{
//If the channel is invisible, don't waste time capturing data
struct SiglentWaveformDesc_t *wavedesc = wavedescs[chanNr];
if(!enabled[chanNr] || string(wavedesc->DescName).empty())
// Read the wavedesc for every enabled channel
for(unsigned int i=0; i<m_analogChannelCount; i++)
{
if (!toQueue)
m_channels[chanNr]->SetData(NULL);
continue;
wavedescs.push_back(new struct SiglentWaveformDesc_t);
if(enabled[i])
{
m_transport->SendCommand(m_channels[i]->GetHwname() + ":WF? DESC");
// TODO: a bunch of error checking...
ReadWaveDescriptorBlock(wavedescs[i], i);
LogDebug("name %s, number: %u\n",wavedescs[i]->InstrumentName,
wavedescs[i]->InstrumentNumber);
}
}

//Set up the capture we're going to store our data into
AnalogWaveform* cap = new AnalogWaveform;

//TODO: get sequence count from wavedesc
//TODO: sequence mode should be multiple captures, one per sequence, with some kind of fifo or something?

//Parse the wavedesc headers
LogDebug(" Wavedesc len: %d\n", wavedesc->WaveDescLen);
LogDebug(" Usertext len: %d\n", wavedesc->UserTextLen);
LogDebug(" Trigtime len: %d\n", wavedesc->TriggerTimeArrayLen);

if(wavedesc->TriggerTimeArrayLen != 0)
num_sequences = wavedesc->TriggerTimeArrayLen;
float v_gain = wavedesc->VerticalGain;
float v_off = wavedesc->VerticalOffset;
float interval = wavedesc->HorizontalInterval * 1e12f;
double h_off = wavedesc->HorizontalOffset * 1e12f; //ps from start of waveform to trigger
double h_off_frac = fmodf(h_off, interval); //fractional sample position, in ps
if(h_off_frac < 0)
h_off_frac = interval + h_off_frac;
cap->m_triggerPhase = h_off_frac; //TODO: handle this properly in segmented mode?
//We might have multiple offsets
//double h_unit = *reinterpret_cast<double*>(pdesc + 244);

//Timestamp is a somewhat complex format that needs some shuffling around.
double fseconds = wavedesc->Timestamp.Seconds;
uint8_t seconds = floor(wavedesc->Timestamp.Seconds);
cap->m_startPicoseconds = static_cast<int64_t>( (fseconds - seconds) * 1e12f );
time_t tnow = time(NULL);
struct tm* now = localtime(&tnow);
struct tm tstruc;
tstruc.tm_sec = seconds;
tstruc.tm_min = wavedesc->Timestamp.Minutes;
tstruc.tm_hour = wavedesc->Timestamp.Hours;
tstruc.tm_mday = wavedesc->Timestamp.Days;
tstruc.tm_mon = wavedesc->Timestamp.Months;
tstruc.tm_year = wavedesc->Timestamp.Years;
tstruc.tm_wday = now->tm_wday;
tstruc.tm_yday = now->tm_yday;
tstruc.tm_isdst = now->tm_isdst;
cap->m_startTimestamp = mktime(&tstruc);
cap->m_timescale = round(interval);
for(unsigned int seqNr=0; seqNr<num_sequences; seqNr++)
{
LogDebug("Channel %s block %u\n", m_channels[chanNr]->GetHwname().c_str(), seqNr);
// Grab the actual waveforms

//Ask for the segment of interest
//(segment number is ignored for non-segmented waveforms)
if(num_sequences > 1)
//TODO: WFSU in outer loop and WF in inner loop
unsigned int num_sequences = 1;
for(unsigned int chanNr=0; chanNr<m_analogChannelCount; chanNr++)
{
//If the channel is invisible, don't waste time capturing data
struct SiglentWaveformDesc_t *wavedesc = wavedescs[chanNr];
if(!enabled[chanNr] || string(wavedesc->DescName).empty())
{
//segment 0 = "all", 1 = first part of capture
m_transport->SendCommand("WAVEFORM_SETUP SP,0,NP,0,FP,0,SN," + (seqNr+1));
if (!toQueue)
m_channels[chanNr]->SetData(NULL);
continue;
}

//Read the actual waveform data
m_transport->SendCommand(m_channels[chanNr]->GetHwname() + ":WF? DAT2");
char header[maxWaveHeaderSize] = {0};
size_t wavesize = ReadWaveHeader(header);
uint8_t *data = new uint8_t[wavesize];
m_transport->ReadRawData(wavesize, data);
// two \n...
m_transport->ReadReply();
m_transport->ReadReply();

double trigtime = 0;
if( (num_sequences > 1) && (seqNr > 0) )
//Set up the capture we're going to store our data into
AnalogWaveform* cap = new AnalogWaveform;

//TODO: get sequence count from wavedesc
//TODO: sequence mode should be multiple captures, one per sequence, with some kind of fifo or something?

//Parse the wavedesc headers
LogDebug(" Wavedesc len: %d\n", wavedesc->WaveDescLen);
LogDebug(" Usertext len: %d\n", wavedesc->UserTextLen);
LogDebug(" Trigtime len: %d\n", wavedesc->TriggerTimeArrayLen);

if(wavedesc->TriggerTimeArrayLen != 0)
num_sequences = wavedesc->TriggerTimeArrayLen;
float v_gain = wavedesc->VerticalGain;
float v_off = wavedesc->VerticalOffset;
float interval = wavedesc->HorizontalInterval * 1e12f;
double h_off = wavedesc->HorizontalOffset * 1e12f; //ps from start of waveform to trigger
double h_off_frac = fmodf(h_off, interval); //fractional sample position, in ps
if(h_off_frac < 0)
h_off_frac = interval + h_off_frac;
cap->m_triggerPhase = h_off_frac; //TODO: handle this properly in segmented mode?
//We might have multiple offsets
//double h_unit = *reinterpret_cast<double*>(pdesc + 244);

//Timestamp is a somewhat complex format that needs some shuffling around.
double fseconds = wavedesc->Timestamp.Seconds;
uint8_t seconds = floor(wavedesc->Timestamp.Seconds);
cap->m_startPicoseconds = static_cast<int64_t>( (fseconds - seconds) * 1e12f );
time_t tnow = time(NULL);
struct tm* now = localtime(&tnow);
struct tm tstruc;
tstruc.tm_sec = seconds;
tstruc.tm_min = wavedesc->Timestamp.Minutes;
tstruc.tm_hour = wavedesc->Timestamp.Hours;
tstruc.tm_mday = wavedesc->Timestamp.Days;
tstruc.tm_mon = wavedesc->Timestamp.Months;
tstruc.tm_year = wavedesc->Timestamp.Years;
tstruc.tm_wday = now->tm_wday;
tstruc.tm_yday = now->tm_yday;
tstruc.tm_isdst = now->tm_isdst;
cap->m_startTimestamp = mktime(&tstruc);
cap->m_timescale = round(interval);
for(unsigned int seqNr=0; seqNr<num_sequences; seqNr++)
{
//If a multi-segment capture, ask for the trigger time data
m_transport->SendCommand(m_channels[chanNr]->GetHwname() + ":WF? TIME");
LogDebug("Channel %s block %u\n", m_channels[chanNr]->GetHwname().c_str(), seqNr);

trigtime = ReadWaveHeader(header);
// \n
//Ask for the segment of interest
//(segment number is ignored for non-segmented waveforms)
if(num_sequences > 1)
{
//segment 0 = "all", 1 = first part of capture
m_transport->SendCommand("WAVEFORM_SETUP SP,0,NP,0,FP,0,SN," + (seqNr+1));
}

//Read the actual waveform data
m_transport->SendCommand(m_channels[chanNr]->GetHwname() + ":WF? DAT2");
char header[maxWaveHeaderSize] = {0};
size_t wavesize = ReadWaveHeader(header);
uint8_t *data = new uint8_t[wavesize];
m_transport->ReadRawData(wavesize, data);
// two \n...
m_transport->ReadReply();
m_transport->ReadReply();
//double trigoff = ptrigtime[1]; //offset to point 0 from trigger time
}

int64_t trigtime_samples = trigtime * 1e12f / interval;
//int64_t trigoff_samples = trigoff * 1e12f / interval;
//LogDebug(" Trigger time: %.3f sec (%lu samples)\n", trigtime, trigtime_samples);
//LogDebug(" Trigger offset: %.3f sec (%lu samples)\n", trigoff, trigoff_samples);
double trigtime = 0;
if( (num_sequences > 1) && (seqNr > 0) )
{
//If a multi-segment capture, ask for the trigger time data
m_transport->SendCommand(m_channels[chanNr]->GetHwname() + ":WF? TIME");

//If we have samples already in the capture, stretch the final one to our trigger offset
/*
if(cap->m_samples.size())
{
auto& last_sample = cap->m_samples[cap->m_samples.size()-1];
last_sample.m_duration = trigtime_samples - last_sample.m_offset;
}
*/
trigtime = ReadWaveHeader(header);
// \n
m_transport->ReadReply();
//double trigoff = ptrigtime[1]; //offset to point 0 from trigger time
}

//Decode the samples
unsigned int num_samples = wavesize;
LogDebug("Got %u samples\n", num_samples);
cap->Resize(num_samples);
for(unsigned int i=0; i<num_samples; i++)
{
cap->m_offsets[i] = i+trigtime_samples;
cap->m_durations[i] = 1;
if (m_acquiredDataIsSigned)
int64_t trigtime_samples = trigtime * 1e12f / interval;
//int64_t trigoff_samples = trigoff * 1e12f / interval;
//LogDebug(" Trigger time: %.3f sec (%lu samples)\n", trigtime, trigtime_samples);
//LogDebug(" Trigger offset: %.3f sec (%lu samples)\n", trigoff, trigoff_samples);

//If we have samples already in the capture, stretch the final one to our trigger offset
/*
if(cap->m_samples.size())
{
// See programming guide, page 267: https://siglentna.com/wp-content/uploads/2020/04/ProgrammingGuide_PG01-E02C.pdf
// voltage value (V) = code value * (vdiv /25) - voffset
cap->m_samples[i] = (int8_t)(data[i]) * (v_gain / 25.0) - v_off;
auto& last_sample = cap->m_samples[cap->m_samples.size()-1];
last_sample.m_duration = trigtime_samples - last_sample.m_offset;
}
*/

//Decode the samples
unsigned int num_samples = wavesize;
LogDebug("Got %u samples\n", num_samples);
cap->Resize(num_samples);
for(unsigned int i=0; i<num_samples; i++)
{
cap->m_offsets[i] = i+trigtime_samples;
cap->m_durations[i] = 1;
if (m_acquiredDataIsSigned)
{
// See programming guide, page 267: https://siglentna.com/wp-content/uploads/2020/04/ProgrammingGuide_PG01-E02C.pdf
// voltage value (V) = code value * (vdiv /25) - voffset
cap->m_samples[i] = (int8_t)(data[i]) * (v_gain / 25.0) - v_off;
}
else
cap->m_samples[i] = data[i] * v_gain - v_off;
}
else
cap->m_samples[i] = data[i] * v_gain - v_off;
}

//Done, update the data
if (!toQueue)
m_channels[chanNr]->SetData(cap);
else
pending_waveforms[chanNr].push_back(cap);
}
}

//At this point all data has been read so the scope is free to go do its thing while we crunch the results.
//Re-arm the trigger if not in one-shot mode
if(!m_triggerOneShot)
{
lock_guard<recursive_mutex> lock(m_mutex);

//Done, update the data
if (!toQueue)
m_channels[chanNr]->SetData(cap);
else
pending_waveforms[chanNr].push_back(cap);
m_transport->SendCommand("TRIG_MODE SINGLE");
m_triggerArmed = true;
}

m_pendingWaveformsMutex.lock();