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

Commits on Sep 16, 2020

  1. Initial implementation of pulse width trigger for LeCroy. Calling thi…

    …s the end of the trigger refactoring for now. Fixes #216.
    azonenberg committed Sep 16, 2020
    Copy the full SHA
    e368fec View commit details
Showing with 192 additions and 44 deletions.
  1. +155 −35 scopehal/LeCroyOscilloscope.cpp
  2. +7 −1 scopehal/LeCroyOscilloscope.h
  3. +10 −6 scopehal/PulseWidthTrigger.cpp
  4. +20 −2 scopehal/PulseWidthTrigger.h
190 changes: 155 additions & 35 deletions scopehal/LeCroyOscilloscope.cpp
Original file line number Diff line number Diff line change
@@ -3137,6 +3137,8 @@ void LeCroyOscilloscope::PullTrigger()
string reply = Trim(m_transport->ReadReply());
if (reply == "Edge")
PullEdgeTrigger();
else if (reply == "Width")
PullPulseWidthTrigger();

//Unrecognized trigger type
else
@@ -3145,6 +3147,24 @@ void LeCroyOscilloscope::PullTrigger()
m_trigger = NULL;
return;
}

//Pull the source (same for all types of trigger)
PullTriggerSource(m_trigger);

//TODO: holdoff
}

/**
@brief Reads the source of a trigger from the instrument
*/
void LeCroyOscilloscope::PullTriggerSource(Trigger* trig)
{
m_transport->SendCommand("VBS? 'return = app.Acquisition.Trigger.Source'"); //not visible in XStream Browser?
string reply = Trim(m_transport->ReadReply());
auto chan = GetChannelByHwName(reply);
trig->SetInput(0, StreamDescriptor(chan, 0), true);
if(!chan)
LogWarning("Unknown trigger source \"%s\"\n", reply.c_str());
}

/**
@@ -3164,15 +3184,6 @@ void LeCroyOscilloscope::PullEdgeTrigger()
m_trigger = new EdgeTrigger(this);
EdgeTrigger* et = dynamic_cast<EdgeTrigger*>(m_trigger);

//Source channel
lock_guard<recursive_mutex> lock(m_mutex);
m_transport->SendCommand("VBS? 'return = app.Acquisition.Trigger.Source'");
string reply = Trim(m_transport->ReadReply());
auto chan = GetChannelByHwName(reply);
et->SetInput(0, StreamDescriptor(chan, 0), true);
if(!chan)
LogWarning("Unknown trigger source \"%s\"\n", reply.c_str());

//Level
m_transport->SendCommand("VBS? 'return = app.Acquisition.Trigger.Edge.Level'");
et->SetLevel(stof(m_transport->ReadReply()));
@@ -3181,67 +3192,130 @@ void LeCroyOscilloscope::PullEdgeTrigger()

//Slope
m_transport->SendCommand("VBS? 'return = app.Acquisition.Trigger.Edge.Slope'");
reply = Trim(m_transport->ReadReply());
if(reply == "Positive")
et->SetType(EdgeTrigger::EDGE_RISING);
else if(reply == "Negative")
et->SetType(EdgeTrigger::EDGE_FALLING);
else if(reply == "Either")
et->SetType(EdgeTrigger::EDGE_ANY);
else
LogDebug("Unknown trigger slope %s\n", reply.c_str());
ProcessTriggerSlope(et, Trim(m_transport->ReadReply()));
}

void LeCroyOscilloscope::PushTrigger()
/**
@brief Reads settings for an edge trigger from the instrument
*/
void LeCroyOscilloscope::PullPulseWidthTrigger()
{
auto et = dynamic_cast<EdgeTrigger*>(m_trigger);
if(et)
PushEdgeTrigger(et);
//Clear out any triggers of the wrong type
if( (m_trigger != NULL) && (dynamic_cast<PulseWidthTrigger*>(m_trigger) != NULL) )
{
delete m_trigger;
m_trigger = NULL;
}

else
LogWarning("Unknown trigger type (not an edge)\n");
//Create a new trigger if necessary
if(m_trigger == NULL)
m_trigger = new PulseWidthTrigger(this);
auto pt = dynamic_cast<PulseWidthTrigger*>(m_trigger);

//Level
m_transport->SendCommand("VBS? 'return = app.Acquisition.Trigger.Width.Level'");
pt->SetLevel(stof(m_transport->ReadReply()));

//Condition
m_transport->SendCommand("VBS? 'return = app.Acquisition.Trigger.Width.Condition'");
auto reply = Trim(m_transport->ReadReply());
if(reply == "LessThan")
pt->SetCondition(PulseWidthTrigger::WIDTH_LESS);
else if(reply == "GreaterThan")
pt->SetCondition(PulseWidthTrigger::WIDTH_GREATER);
else if(reply == "InRange")
pt->SetCondition(PulseWidthTrigger::WIDTH_BETWEEN);
else if(reply == "OutOfRange")
pt->SetCondition(PulseWidthTrigger::WIDTH_NOT_BETWEEN);

//Min range
Unit ps(Unit::UNIT_PS);
m_transport->SendCommand("VBS? 'return = app.Acquisition.Trigger.Width.TimeLow'");
pt->SetLowerBound(ps.ParseString(m_transport->ReadReply()));

//Max range
m_transport->SendCommand("VBS? 'return = app.Acquisition.Trigger.Width.TimeHigh'");
pt->SetUpperBound(ps.ParseString(m_transport->ReadReply()));

//Slope
m_transport->SendCommand("VBS? 'return = app.Acquisition.Trigger.Width.Slope'");
ProcessTriggerSlope(pt, Trim(m_transport->ReadReply()));
}

/**
@brief Pushes settings for an edge trigger to the instrument
@brief Processes the slope for an edge or edge-derived trigger
*/
void LeCroyOscilloscope::PushEdgeTrigger(EdgeTrigger* trig)
void LeCroyOscilloscope::ProcessTriggerSlope(EdgeTrigger* trig, string reply)
{
lock_guard<recursive_mutex> lock(m_mutex);
if(reply == "Positive")
trig->SetType(EdgeTrigger::EDGE_RISING);
else if(reply == "Negative")
trig->SetType(EdgeTrigger::EDGE_FALLING);
else if(reply == "Either")
trig->SetType(EdgeTrigger::EDGE_ANY);
else
LogWarning("Unknown trigger slope %s\n", reply.c_str());
}

//Type
m_transport->SendCommand("VBS? 'app.Acquisition.Trigger.Type = \"Edge\"");
void LeCroyOscilloscope::PushTrigger()
{
lock_guard<recursive_mutex> lock(m_mutex);

//Source
//Source is the same for every channel
char tmp[128];
snprintf(
tmp,
sizeof(tmp),
"VBS? 'app.Acquisition.Trigger.Source = \"%s\"'",
trig->GetInput(0).m_channel->GetHwname().c_str());
m_trigger->GetInput(0).m_channel->GetHwname().c_str());
m_transport->SendCommand(tmp);

//The rest depends on the type
auto et = dynamic_cast<EdgeTrigger*>(m_trigger);
auto pt = dynamic_cast<PulseWidthTrigger*>(m_trigger);
if(pt)
{
m_transport->SendCommand("VBS? 'app.Acquisition.Trigger.Type = \"Width\"");
PushPulseWidthTrigger(pt);
}
else if(et)
{
m_transport->SendCommand("VBS? 'app.Acquisition.Trigger.Type = \"Edge\"");
PushEdgeTrigger(et, "app.Acquisition.Trigger.Edge");
}

else
LogWarning("Unknown trigger type (not an edge)\n");
}

/**
@brief Pushes settings for an edge trigger to the instrument
*/
void LeCroyOscilloscope::PushEdgeTrigger(EdgeTrigger* trig, string tree)
{
//Level
char tmp[128];
snprintf(
tmp,
sizeof(tmp),
"VBS? 'app.Acquisition.Trigger.Edge.Level = %f'",
"VBS? '%s.Level = %f'",
tree.c_str(),
trig->GetLevel());
m_transport->SendCommand(tmp);

//Slope
switch(trig->GetType())
{
case EdgeTrigger::EDGE_RISING:
m_transport->SendCommand("VBS? 'app.Acquisition.Trigger.Edge.Slope = \"Positive\"'");
m_transport->SendCommand(string("VBS? '") + tree + ".Slope = \"Positive\"'");
break;

case EdgeTrigger::EDGE_FALLING:
m_transport->SendCommand("VBS? 'app.Acquisition.Trigger.Edge.Slope = \"Negative\"'");
m_transport->SendCommand(string("VBS? '") + tree + ".Slope = \"Negative\"'");
break;

case EdgeTrigger::EDGE_ANY:
m_transport->SendCommand("VBS? 'app.Acquisition.Trigger.Edge.Slope = \"Either\"'");
m_transport->SendCommand(string("VBS? '") + tree + ".Slope = \"Either\"'");
break;

default:
@@ -3250,6 +3324,52 @@ void LeCroyOscilloscope::PushEdgeTrigger(EdgeTrigger* trig)
}
}

/**
@brief Pushes settings for an edge trigger to the instrument
*/
void LeCroyOscilloscope::PushPulseWidthTrigger(PulseWidthTrigger* trig)
{
//Push common settings for the edge trigger
PushEdgeTrigger(trig, "app.Acquisition.Trigger.Width");

//Condition
switch(trig->GetCondition())
{
case PulseWidthTrigger::WIDTH_LESS:
m_transport->SendCommand("VBS? 'app.Acquisition.Trigger.Width.Condition = \"LessThan\"'");
break;

case PulseWidthTrigger::WIDTH_GREATER:
m_transport->SendCommand("VBS? 'app.Acquisition.Trigger.Width.Condition = \"GreaterThan\"'");
break;

case PulseWidthTrigger::WIDTH_BETWEEN:
m_transport->SendCommand("VBS? 'app.Acquisition.Trigger.Width.Condition = \"InRange\"'");
break;

case PulseWidthTrigger::WIDTH_NOT_BETWEEN:
m_transport->SendCommand("VBS? 'app.Acquisition.Trigger.Width.Condition = \"OutOfRange\"'");
break;
}

//Time high
char tmp[128];
snprintf(
tmp,
sizeof(tmp),
"VBS? 'app.Acquisition.Trigger.Width.TimeHigh = \"%e\"'",
trig->GetUpperBound() * 1e-12f);
m_transport->SendCommand(tmp);

//Time low
snprintf(
tmp,
sizeof(tmp),
"VBS? 'app.Acquisition.Trigger.Width.TimeLow = \"%e\"'",
trig->GetLowerBound() * 1e-12f);
m_transport->SendCommand(tmp);
}

/**
@brief Removes whitespace from the start and end of a string
*/
8 changes: 7 additions & 1 deletion scopehal/LeCroyOscilloscope.h
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@
#include "../xptools/Socket.h"

class EdgeTrigger;
class PulseWidthTrigger;

/**
@brief A Teledyne LeCroy oscilloscope using the MAUI/XStream command set.
@@ -201,7 +202,12 @@ class LeCroyOscilloscope

protected:
void PullEdgeTrigger();
void PushEdgeTrigger(EdgeTrigger* trig);
void PullPulseWidthTrigger();
void PullTriggerSource(Trigger* trig);
void ProcessTriggerSlope(EdgeTrigger* trig, std::string reply);

void PushEdgeTrigger(EdgeTrigger* trig, std::string tree);
void PushPulseWidthTrigger(PulseWidthTrigger* trig);

void BulkCheckChannelEnableState();

16 changes: 10 additions & 6 deletions scopehal/PulseWidthTrigger.cpp
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@

#include "scopehal.h"
#include "PulseWidthTrigger.h"
#include "LeCroyOscilloscope.h"

using namespace std;

@@ -44,12 +45,15 @@ PulseWidthTrigger::PulseWidthTrigger(Oscilloscope* scope)
m_uppername = "Upper Bound";
m_parameters[m_uppername] = FilterParameter(FilterParameter::TYPE_FLOAT, Unit(Unit::UNIT_PS));

m_widthtypename = "Condition";
m_parameters[m_widthtypename] = FilterParameter(FilterParameter::TYPE_ENUM, Unit(Unit::UNIT_COUNTS));
m_parameters[m_widthtypename].AddEnumValue("Less than", WIDTH_LESS);
m_parameters[m_widthtypename].AddEnumValue("Greater than", WIDTH_GREATER);
m_parameters[m_widthtypename].AddEnumValue("Between", WIDTH_BETWEEN);
m_parameters[m_widthtypename].AddEnumValue("Not between", WIDTH_NOT_BETWEEN);
m_conditionname = "Condition";
m_parameters[m_conditionname] = FilterParameter(FilterParameter::TYPE_ENUM, Unit(Unit::UNIT_COUNTS));
m_parameters[m_conditionname].AddEnumValue("Less than", WIDTH_LESS);
m_parameters[m_conditionname].AddEnumValue("Greater than", WIDTH_GREATER);
m_parameters[m_conditionname].AddEnumValue("Between", WIDTH_BETWEEN);

//So far only LeCroy is known to support this
if(dynamic_cast<LeCroyOscilloscope*>(scope) != NULL)
m_parameters[m_conditionname].AddEnumValue("Not between", WIDTH_NOT_BETWEEN);
}

PulseWidthTrigger::~PulseWidthTrigger()
22 changes: 20 additions & 2 deletions scopehal/PulseWidthTrigger.h
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ class PulseWidthTrigger : public EdgeTrigger
PulseWidthTrigger(Oscilloscope* scope);
virtual ~PulseWidthTrigger();

enum WidthType
enum Condition
{
WIDTH_LESS,
WIDTH_GREATER,
@@ -57,8 +57,26 @@ class PulseWidthTrigger : public EdgeTrigger
static std::string GetTriggerName();
TRIGGER_INITPROC(PulseWidthTrigger);

void SetCondition(Condition type)
{ m_parameters[m_conditionname].SetIntVal(type); }

Condition GetCondition()
{ return (Condition) m_parameters[m_conditionname].GetIntVal(); }

int64_t GetLowerBound()
{ return m_parameters[m_lowername].GetIntVal(); }

void SetLowerBound(int64_t bound)
{ m_parameters[m_lowername].SetIntVal(bound); }

int64_t GetUpperBound()
{ return m_parameters[m_uppername].GetIntVal(); }

void SetUpperBound(int64_t bound)
{ m_parameters[m_uppername].SetIntVal(bound); }

protected:
std::string m_widthtypename;
std::string m_conditionname;
std::string m_lowername;
std::string m_uppername;
};