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

Commits on Mar 9, 2020

  1. Copy the full SHA
    9e5b961 View commit details
  2. Copy the full SHA
    7d1d7df View commit details
122 changes: 72 additions & 50 deletions scopehal/LeCroyOscilloscope.cpp
Original file line number Diff line number Diff line change
@@ -217,7 +217,8 @@ void LeCroyOscilloscope::AddDigitalChannels(unsigned int count)
OscilloscopeChannel::CHANNEL_TYPE_DIGITAL,
GetDefaultChannelColor(m_channels.size()),
1,
m_channels.size());
m_channels.size(),
true);
m_channels.push_back(chan);
m_digitalChannels.push_back(chan);
}
@@ -1050,18 +1051,31 @@ bool LeCroyOscilloscope::AcquireData(bool toQueue)
vector<string> wavedescs;
string cmd;
bool enabled[4] = {false};
bool any_enabled = true;
BulkCheckChannelEnableState();
unsigned int firstEnabledChannel = UINT_MAX;
for(unsigned int i=0; i<m_analogChannelCount; i++)
{
enabled[i] = IsChannelEnabled(i);
if(enabled[i])
any_enabled = true;
}
for(unsigned int i=0; i<m_analogChannelCount; i++)
{
wavedescs.push_back("");
if(enabled[i])

//If NO channels are enabled, query channel 1's WAVEDESC.
//Per phone conversation w/ Honam @ LeCroy apps, this will be updated even if channel is turned off
if(enabled[i] || (!any_enabled && i==0))
{
if(firstEnabledChannel == UINT_MAX)
firstEnabledChannel = i;
SendCommand(m_channels[i]->GetHwname() + ":WF? DESC");
}
}
for(unsigned int i=0; i<m_analogChannelCount; i++)
{
if(enabled[i])
if(enabled[i] || (!any_enabled && i==0))
{
if(!ReadWaveformBlock(wavedescs[i]))
LogError("ReadWaveformBlock for wavedesc %u failed\n", i);
@@ -1072,7 +1086,7 @@ bool LeCroyOscilloscope::AcquireData(bool toQueue)
size_t expected_wavedesc_size = 346;
for(unsigned int i=0; i<m_analogChannelCount; i++)
{
if(!enabled[i])
if(!enabled[i] && !(!any_enabled && i==0))
continue;

if(wavedescs[i].size() < expected_wavedesc_size)
@@ -1087,7 +1101,7 @@ bool LeCroyOscilloscope::AcquireData(bool toQueue)
unsigned char* pdesc = NULL;
for(unsigned int i=0; i<m_analogChannelCount; i++)
{
if(enabled[i])
if(enabled[i] || (!any_enabled && i==0))
{
pdesc = (unsigned char*)(&wavedescs[i][0]);
break;
@@ -1121,6 +1135,46 @@ bool LeCroyOscilloscope::AcquireData(bool toQueue)
}
}

/*
Timestamp is a somewhat complex format that needs some shuffling around.
Timestamp starts at offset 296 bytes in the wavedesc
(296-303) double seconds
(304) byte minutes
(305) byte hours
(306) byte days
(307) byte months
(308-309) uint16 year
TODO: during startup, query instrument for its current time zone
since the wavedesc reports instment local time
*/
double fseconds = *reinterpret_cast<const double*>(wavedescs[firstEnabledChannel].c_str() + 296);
uint8_t seconds = floor(fseconds);
double basetime = fseconds - seconds;
time_t tnow = time(NULL);
struct tm tstruc;
localtime_r(&tnow, &tstruc);

//Convert the instrument time to a string, then back to a tm
//Is there a better way to do this???
//Naively poking "struct tm" fields gives incorrect results (scopehal-apps:#52)
//Maybe because tm_yday is inconsistent?
char tblock[64] = {0};
snprintf(tblock, sizeof(tblock), "%d-%d-%d %d:%02d:%02d",
*reinterpret_cast<uint16_t*>(pdesc+308),
pdesc[307],
pdesc[306],
pdesc[305],
pdesc[304],
seconds);
locale cur_locale;
auto& tget = use_facet< time_get<char> >(cur_locale);
istringstream stream(tblock);
ios::iostate state;
char format[] = "%F %T";
tget.get(stream, time_get<char>::iter_type(), stream, state, &tstruc, format, format+strlen(format));
time_t ttime = mktime(&tstruc);

//Read the timestamps if we're doing segmented capture
string wavetime;
if(num_sequences > 1)
@@ -1155,48 +1209,6 @@ bool LeCroyOscilloscope::AcquireData(bool toQueue)
if(h_off_frac < 0)
h_off_frac = interval + h_off_frac; //double h_unit = *reinterpret_cast<double*>(pdesc + 244);

/*
Timestamp is a somewhat complex format that needs some shuffling around.
Timestamp starts at offset 296 bytes in the wavedesc
(296-303) double seconds
(304) byte minutes
(305) byte hours
(306) byte days
(307) byte months
(308-309) uint16 year
*/
double fseconds = *reinterpret_cast<double*>(pdesc + 296);
uint8_t seconds = floor(fseconds);
double basetime = fseconds - seconds;

//TODO: during startup, query instrument for its current time zone
//since the wavedesc reports instment local time

//Get the current time
time_t tnow = time(NULL);
struct tm tstruc;
localtime_r(&tnow, &tstruc);

//Convert the instrument time to a string, then back to a tm
//Is there a better way to do this???
//Naively poking "struct tm" fields gives incorrect results (scopehal-apps:#52)
//Maybe because tm_yday is inconsistent?
char tblock[64] = {0};
snprintf(tblock, sizeof(tblock), "%d-%d-%d %d:%02d:%02d",
*reinterpret_cast<uint16_t*>(pdesc+308),
pdesc[307],
pdesc[306],
pdesc[305],
pdesc[304],
seconds);
locale cur_locale;
auto& tget = use_facet< time_get<char> >(cur_locale);
istringstream stream(tblock);
ios::iostate state;
char format[] = "%F %T";
tget.get(stream, time_get<char>::iter_type(), stream, state, &tstruc, format, format+strlen(format));
time_t ttime = mktime(&tstruc);

//Read the actual waveform data
string data;
if(!ReadWaveformBlock(data))
@@ -1314,15 +1326,21 @@ bool LeCroyOscilloscope::AcquireData(bool toQueue)
int num_samples = atoi(tmp.c_str());
//LogDebug("Expecting %d samples\n", num_samples);

//TODO: how do we parse this??
/*
tmp = data.substr(data.find("<FirstEventTime>") + 16);
tmp = tmp.substr(0, tmp.find("</FirstEventTime>"));
*/

//Pull out the actual binary data (Base64 coded)
tmp = data.substr(data.find("<BinaryData>") + 12);
tmp = tmp.substr(0, tmp.find("</BinaryData>"));

//Decode the base64
base64_decodestate state;
base64_init_decodestate(&state);
base64_decodestate bstate;
base64_init_decodestate(&bstate);
unsigned char* block = new unsigned char[tmp.length()]; //base64 is smaller than plaintext, leave room
base64_decode_block(tmp.c_str(), tmp.length(), (char*)block, &state);
base64_decode_block(tmp.c_str(), tmp.length(), (char*)block, &bstate);

//We have each channel's data from start to finish before the next (no interleaving).
unsigned int icapchan = 0;
@@ -1333,6 +1351,10 @@ bool LeCroyOscilloscope::AcquireData(bool toQueue)
DigitalCapture* cap = new DigitalCapture;
cap->m_timescale = interval;

//Capture timestamp
cap->m_startTimestamp = ttime;
cap->m_startPicoseconds = static_cast<int64_t>(basetime * 1e12f);

for(int j=0; j<num_samples; j++)
cap->m_samples.push_back(DigitalSample(j, 1, block[icapchan*num_samples + j]));

1 change: 1 addition & 0 deletions scopeprotocols/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ set(SCOPEPROTOCOLS_SOURCES
MDIODecoder.cpp
MDIORenderer.cpp
MovingAverageDecoder.cpp
ParallelBusDecoder.cpp
PeriodMeasurementDecoder.cpp
SincInterpolationDecoder.cpp
ThresholdDecoder.cpp
159 changes: 159 additions & 0 deletions scopeprotocols/ParallelBusDecoder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/***********************************************************************************************************************
* *
* ANTIKERNEL v0.1 *
* *
* Copyright (c) 2012-2020 Andrew D. Zonenberg *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
* following conditions are met: *
* *
* * Redistributions of source code must retain the above copyright notice, this list of conditions, and the *
* following disclaimer. *
* *
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the *
* following disclaimer in the documentation and/or other materials provided with the distribution. *
* *
* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
* POSSIBILITY OF SUCH DAMAGE. *
* *
***********************************************************************************************************************/

#include "../scopehal/scopehal.h"
#include "ParallelBusDecoder.h"
#include "../scopehal/DigitalRenderer.h"

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

ParallelBusDecoder::ParallelBusDecoder(string color)
: ProtocolDecoder(OscilloscopeChannel::CHANNEL_TYPE_DIGITAL, color, CAT_CONVERSION)
{
//Set up channels
char tmp[32];
for(size_t i=0; i<16; i++)
{
snprintf(tmp, sizeof(tmp), "din%zu", i);
m_signalNames.push_back(tmp);
m_channels.push_back(NULL);
}

m_widthname = "Width";
m_parameters[m_widthname] = ProtocolDecoderParameter(ProtocolDecoderParameter::TYPE_FLOAT);
m_parameters[m_widthname].SetIntVal(0);
}

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

ChannelRenderer* ParallelBusDecoder::CreateRenderer()
{
return new DigitalRenderer(this);
}

bool ParallelBusDecoder::ValidateChannel(size_t i, OscilloscopeChannel* channel)
{
if( (i < 16) && (channel->GetType() == OscilloscopeChannel::CHANNEL_TYPE_DIGITAL) )
return true;
return false;
}

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

string ParallelBusDecoder::GetProtocolName()
{
return "Parallel Bus";
}

void ParallelBusDecoder::SetDefaultName()
{
char hwname[256];
snprintf(hwname, sizeof(hwname), "ParallelBus(%s)", m_channels[0]->m_displayname.c_str());
m_hwname = hwname;
m_displayname = m_hwname;
}

bool ParallelBusDecoder::NeedsConfig()
{
return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Actual decoder logic

void ParallelBusDecoder::Refresh()
{
//Figure out how wide our input is
int width = m_parameters[m_widthname].GetIntVal();

//Make sure we have an input for each channel in use
vector<DigitalCapture*> inputs;
for(int i=0; i<width; i++)
{
if(m_channels[i] == NULL)
{
LogDebug("err 1\n");
SetData(NULL);
return;
}
DigitalCapture* din = dynamic_cast<DigitalCapture*>(m_channels[i]->GetData());
if(din == NULL)
{
LogDebug("err 2\n");
SetData(NULL);
return;
}
inputs.push_back(din);
}
if(inputs.empty())
{
SetData(NULL);
return;
}

//Merge all of our samples
//TODO: handle variable sample rates etc
DigitalBusCapture* cap = new DigitalBusCapture;
cap->m_samples.resize(inputs[0]->m_samples.size());
#pragma omp parallel for
for(size_t i=0; i<inputs[0]->m_samples.size(); i++)
{
vector<bool> data;
bool end = false;
for(int j=0; j<width; j++)
{
if(inputs[j]->GetDepth() >= i)
{
end = true;
break;
}

data.push_back(inputs[j]->m_samples[i].m_sample);
}

if(!end)
{
cap->m_samples[i] = DigitalBusSample(
inputs[0]->m_samples[i].m_offset,
inputs[0]->m_samples[i].m_duration,
data);
}
}
SetData(cap);

//Copy our time scales from the input
cap->m_timescale = inputs[0]->m_timescale;
cap->m_startTimestamp = inputs[0]->m_startTimestamp;
cap->m_startPicoseconds = inputs[0]->m_startPicoseconds;
}
Loading