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: 1c6fb278f814
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: accca5515b71
Choose a head ref
  • 1 commit
  • 5 files changed
  • 1 contributor

Commits on Mar 15, 2020

  1. glscopeclient: implemented waveform data saving (not fully tested as …

    …there's no load support yet). See #3.
    azonenberg committed Mar 15, 2020
    Copy the full SHA
    accca55 View commit details
Showing with 136 additions and 4 deletions.
  1. +103 −0 glscopeclient/HistoryWindow.cpp
  2. +2 −0 glscopeclient/HistoryWindow.h
  3. +25 −3 glscopeclient/OscilloscopeWindow.cpp
  4. +2 −1 glscopeclient/OscilloscopeWindow.h
  5. +4 −0 glscopeclient/ScopeApp.cpp
103 changes: 103 additions & 0 deletions glscopeclient/HistoryWindow.cpp
Original file line number Diff line number Diff line change
@@ -294,3 +294,106 @@ void HistoryWindow::JumpToHistory(TimePoint timestamp)
}
}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Serialization

void HistoryWindow::SerializeWaveforms(string dir, IDTable& table)
{
//Figure out file name, and make the waveform directory
char tmp[512];
snprintf(tmp, sizeof(tmp), "%s/scope_%d_metadata.yml", dir.c_str(), table[m_scope]);
string fname = tmp;
snprintf(tmp, sizeof(tmp), "%s/scope_%d_waveforms", dir.c_str(), table[m_scope]);
mkdir(tmp, 0755);
string dname = tmp;

//Serialize waveforms
string config = "waveforms:\n";
auto children = m_model->children();
int id = 1;
for(auto it : children)
{
TimePoint key = (*it)[m_columns.m_capturekey];

//Save metadata
config += " :\n";
snprintf(tmp, sizeof(tmp), " timestamp: %ld\n", key.first);
config += tmp;
snprintf(tmp, sizeof(tmp), " time_psec: %ld\n", key.second);
config += tmp;
snprintf(tmp, sizeof(tmp), " id: %d\n", id);
config += tmp;
config += " channels:\n";

//Format directory for this waveform
snprintf(tmp, sizeof(tmp), "%s/waveform_%d", dname.c_str(), id);
mkdir(tmp, 0755);
string wname = tmp;

//Save waveform data
WaveformHistory history = (*it)[m_columns.m_history];
for(auto jt : history)
{
int index = jt.first->GetIndex();
auto chan = jt.second;
if(chan == NULL) //trigger, disabled, etc
continue;

snprintf(tmp, sizeof(tmp), "%s/channel_%d.bin", wname.c_str(), index);
FILE* fp = fopen(tmp, "wb");

//Save channel metadata
config += " :\n";
snprintf(tmp, sizeof(tmp), " index: %d\n", index);
config += tmp;
snprintf(tmp, sizeof(tmp), " timescale: %ld\n", chan->m_timescale);
config += tmp;
snprintf(tmp, sizeof(tmp), " trigphase: %f\n", chan->m_triggerPhase);
config += tmp;

//Save channel data
auto achan = dynamic_cast<AnalogCapture*>(chan);
auto dchan = dynamic_cast<DigitalCapture*>(chan);
for(size_t i=0; i<chan->GetDepth(); i++)
{
int64_t times[2] = { chan->GetSampleStart(i), chan->GetSampleEnd(i) };
if(2 != fwrite(times, sizeof(int64_t), 2, fp))
LogError("file write error\n");
if(achan)
{
if(1 != fwrite(&(*achan)[i], sizeof(float), 1, fp))
LogError("file write error\n");
}
else if(dchan)
{
if(1 != fwrite(&(*dchan)[i], sizeof(bool), 1, fp))
LogError("file write error\n");
}
}
//TODO: support not analog or digital channels (e.g. FREESAMPLE eyes)
fclose(fp);
}

id ++;
}

//Save waveform metadata
FILE* fp = fopen(fname.c_str(), "w");
if(!fp)
{
string msg = string("The data file ") + fname + " could not be created!";
Gtk::MessageDialog errdlg(msg, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
errdlg.set_title("Cannot save session\n");
errdlg.run();
return;
}
if(config.length() != fwrite(config.c_str(), 1, config.length(), fp))
{
string msg = string("Error writing to session file ") + fname + "!";
Gtk::MessageDialog errdlg(msg, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
errdlg.set_title("Cannot save session\n");
errdlg.run();
}
fclose(fp);
}
2 changes: 2 additions & 0 deletions glscopeclient/HistoryWindow.h
Original file line number Diff line number Diff line change
@@ -64,6 +64,8 @@ class HistoryWindow : public Gtk::Window

void SetMaxWaveforms(int n);

void SerializeWaveforms(std::string dir, IDTable& table);

protected:
virtual bool on_delete_event(GdkEventAny* ignored);
virtual void OnSelectionChanged();
28 changes: 25 additions & 3 deletions glscopeclient/OscilloscopeWindow.cpp
Original file line number Diff line number Diff line change
@@ -839,7 +839,8 @@ void OscilloscopeWindow::OnFileSave(bool saveToCurrentFile, bool saveLayout, boo
}

//Serialize our configuration and save to the file
string config = SerializeConfiguration(saveLayout);
IDTable table;
string config = SerializeConfiguration(saveLayout, table);
FILE* fp = fopen(m_currentFileName.c_str(), "w");
if(!fp)
{
@@ -857,16 +858,19 @@ void OscilloscopeWindow::OnFileSave(bool saveToCurrentFile, bool saveLayout, boo
errdlg.run();
}
fclose(fp);

//Serialize waveform data if needed
if(saveWaveforms)
SerializeWaveforms(table);
}

string OscilloscopeWindow::SerializeConfiguration(bool saveLayout)
string OscilloscopeWindow::SerializeConfiguration(bool saveLayout, IDTable& table)
{
string config = "";

//TODO: save metadata

//Save instrument config regardless, since data etc needs it
IDTable table;
config += SerializeInstrumentConfiguration(table);

//Decodes depend on scope channels, but need to happen before UI elements that use them
@@ -1000,6 +1004,24 @@ string OscilloscopeWindow::SerializeUIConfiguration(IDTable& table)
return config;
}

/**
@brief Serialize all waveforms for the session
*/
void OscilloscopeWindow::SerializeWaveforms(IDTable& table)
{
//Remove all old waveforms in the data directory.
//TODO: better way that doesn't involve system()
char cwd[PATH_MAX];
getcwd(cwd, PATH_MAX);
chdir(m_currentDataDirName.c_str());
system("rm -rf scope_*");
chdir(cwd);

//Serialize waveforms for each of our instruments
for(auto it : m_historyWindows)
it.second->SerializeWaveforms(m_currentDataDirName, table);
}

void OscilloscopeWindow::OnAlphaChanged()
{
ClearAllPersistence();
3 changes: 2 additions & 1 deletion glscopeclient/OscilloscopeWindow.h
Original file line number Diff line number Diff line change
@@ -217,10 +217,11 @@ class OscilloscopeWindow : public Gtk::Window
std::string m_currentFileName;
std::string m_currentDataDirName;

std::string SerializeConfiguration(bool saveLayout);
std::string SerializeConfiguration(bool saveLayout, IDTable& table);
std::string SerializeInstrumentConfiguration(IDTable& table);
std::string SerializeDecodeConfiguration(IDTable& table);
std::string SerializeUIConfiguration(IDTable& table);
void SerializeWaveforms(IDTable& table);

//Performance counters
double m_tAcquire;
4 changes: 4 additions & 0 deletions glscopeclient/ScopeApp.cpp
Original file line number Diff line number Diff line change
@@ -54,6 +54,10 @@ void ScopeApp::run(string fileToLoad)

m_window->present();

//If no scope threads are running already (from a file load), start them now
if(m_threads.empty())
StartScopeThreads();

while(true)
{
//Poll the scope to see if we have any new data