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

Commits on Nov 23, 2020

  1. MockOscilloscope: Migrated CSV import from OscilloscopeWindow class s…

    …o we can use it in headless ATE apps. Fixes #349.
    azonenberg committed Nov 23, 2020
    Copy the full SHA
    5260566 View commit details
Showing with 180 additions and 0 deletions.
  1. +178 −0 scopehal/MockOscilloscope.cpp
  2. +2 −0 scopehal/MockOscilloscope.h
178 changes: 178 additions & 0 deletions scopehal/MockOscilloscope.cpp
Original file line number Diff line number Diff line change
@@ -340,3 +340,181 @@ void MockOscilloscope::PullTrigger()
{
//no-op
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Import a waveform from CSV

bool MockOscilloscope::LoadCSV(const string& path)
{
LogTrace("Importing CSV file \"%s\"\n", path.c_str());
LogIndenter li;

FILE* fp = fopen(path.c_str(), "r");
if(!fp)
{
LogError("Failed to open file\n");
return false;
}

map<int, AnalogWaveform*> waveforms;

char line[1024];
size_t nrow = 0;
size_t ncols = 0;
vector<string> channel_names;
while(!feof(fp))
{
nrow ++;

if(!fgets(line, sizeof(line), fp))
break;

//Parse the samples for each row
//TODO: be more efficient about this
vector<float> row;
string tmp;
for(size_t i=0; i < sizeof(line); i++)
{
if(line[i] == '\0' || line[i] == ',')
{
float f;
sscanf(tmp.c_str(), "%f", &f);
row.push_back(f);

if(line[i] == '\0')
break;
else
tmp = "";
}
else
tmp += line[i];
}

//If this is the first line, figure out how many columns we have.
//First column is always timestamp in seconds.
//TODO: support timestamp in abstract sample units instead
if(nrow == 1)
{
ncols = row.size() - 1;

//See if the first row is numeric
bool numeric = true;
for(size_t i=0; (i<sizeof(line)) && (line[i] != '\0'); i++)
{
if(!isdigit(line[i]) && !isspace(line[i]) && (line[i] != ',') && (line[i] != '.') )
{
numeric = false;
break;
}
}

if(!numeric)
{
LogTrace("Found %zu signal columns, with header row\n", ncols);

//Extract names of the headers
tmp = "";
for(size_t i=0; i < sizeof(line); i++)
{
if(line[i] == '\0' || line[i] == ',')
{
channel_names.push_back(tmp);

if(line[i] == '\0')
break;
else
tmp = "";
}
else
tmp += line[i];
}

//Discard name of timestamp column
channel_names.erase(channel_names.begin());

continue;
}

else
{
for(size_t i=0; i<ncols; i++)
channel_names.push_back(string("CH") + to_string(i+1));

LogTrace("Found %zu signal columns, no header row\n", ncols);
}
}

//If we don't have any channels, create them
if(GetChannelCount() == 0)
{
//Create the columns
for(size_t i=0; i<ncols; i++)
{
//Create the channel
auto chan = new OscilloscopeChannel(
this,
channel_names[i],
OscilloscopeChannel::CHANNEL_TYPE_ANALOG,
GetDefaultChannelColor(i),
1,
i,
true);
AddChannel(chan);
chan->SetDefaultDisplayName();

//Create the waveform for the channel
auto wfm = new AnalogWaveform;
wfm->m_timescale = 1;
wfm->m_startTimestamp = 0;
wfm->m_startPicoseconds = 0;
wfm->m_triggerPhase = 0;
waveforms[i] = wfm;
chan->SetData(wfm, 0);
}
}

int64_t timestamp = row[0] * 1e12;
for(size_t i=0; i<ncols; i++)
{
if(i+1 >= row.size())
break;

auto w = waveforms[i];
w->m_offsets.push_back(timestamp);
w->m_samples.push_back(row[i+1]);

//Extend last sample
if(!w->m_durations.empty())
{
size_t last = w->m_durations.size() - 1;
w->m_durations[last] = timestamp - w->m_offsets[last];
}

//Add duration for this sample
w->m_durations.push_back(1);
}
}

fclose(fp);

//Calculate gain/offset for each channel
for(size_t i=0; i<ncols; i++)
{
float vmin = FLT_MAX;
float vmax = -FLT_MAX;

for(auto v : waveforms[i]->m_samples)
{
vmax = max(vmax, (float)v);
vmin = min(vmin, (float)v);
}

//LogDebug("vmax = %f, vmin = %f\n", vmax, vmin);

auto chan = GetChannel(i);
chan->SetVoltageRange(vmax - vmin);
chan->SetOffset((vmin-vmax) / 2);
}

return true;
}
2 changes: 2 additions & 0 deletions scopehal/MockOscilloscope.h
Original file line number Diff line number Diff line change
@@ -42,6 +42,8 @@ class MockOscilloscope : public Oscilloscope
MockOscilloscope(const std::string& name, const std::string& vendor, const std::string& serial);
virtual ~MockOscilloscope();

bool LoadCSV(const std::string& path);

//not copyable or assignable
MockOscilloscope(const MockOscilloscope& rhs) =delete;
MockOscilloscope& operator=(const MockOscilloscope& rhs) =delete;