diff --git a/DSView/CMakeLists.txt b/DSView/CMakeLists.txt index 2b48a7b6..0d6074e9 100644 --- a/DSView/CMakeLists.txt +++ b/DSView/CMakeLists.txt @@ -270,7 +270,7 @@ endif() #------------------------------------------------------------------------------- add_definitions(${QT_DEFINITIONS}) -add_definitions(-Wall -Wextra -Wno-return-type -Wno-ignored-qualifiers) +add_definitions(-std=c++11 -Wall -Wextra -Wno-return-type -Wno-ignored-qualifiers) if(ENABLE_DECODE) add_definitions(-DENABLE_DECODE) diff --git a/DSView/main.cpp b/DSView/main.cpp index 3964880a..9320ec81 100644 --- a/DSView/main.cpp +++ b/DSView/main.cpp @@ -30,7 +30,7 @@ #include -#include +#include #include #include #include diff --git a/DSView/pv/devicemanager.cpp b/DSView/pv/devicemanager.cpp index 6bee8b71..e4a2b6e8 100644 --- a/DSView/pv/devicemanager.cpp +++ b/DSView/pv/devicemanager.cpp @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include #include diff --git a/DSView/pv/dialogs/deviceoptions.cpp b/DSView/pv/dialogs/deviceoptions.cpp index b2c7da57..633cc631 100644 --- a/DSView/pv/dialogs/deviceoptions.cpp +++ b/DSView/pv/dialogs/deviceoptions.cpp @@ -37,7 +37,7 @@ using namespace std; namespace pv { namespace dialogs { -DeviceOptions::DeviceOptions(QWidget *parent, shared_ptr dev_inst) : +DeviceOptions::DeviceOptions(QWidget *parent, boost::shared_ptr dev_inst) : QDialog(parent), _dev_inst(dev_inst), _layout(this), diff --git a/DSView/pv/dialogs/streamoptions.cpp b/DSView/pv/dialogs/streamoptions.cpp index 2c50d3f5..a56d3b89 100644 --- a/DSView/pv/dialogs/streamoptions.cpp +++ b/DSView/pv/dialogs/streamoptions.cpp @@ -37,7 +37,7 @@ using namespace std; namespace pv { namespace dialogs { -StreamOptions::StreamOptions(QWidget *parent, shared_ptr dev_inst, +StreamOptions::StreamOptions(QWidget *parent, boost::shared_ptr dev_inst, uint64_t sample_count, bool stream) : QDialog(parent), _dev_inst(dev_inst), diff --git a/DSView/pv/dialogs/waitingdialog.cpp b/DSView/pv/dialogs/waitingdialog.cpp index 6bd0e769..a60669a9 100644 --- a/DSView/pv/dialogs/waitingdialog.cpp +++ b/DSView/pv/dialogs/waitingdialog.cpp @@ -39,7 +39,7 @@ namespace dialogs { const QString WaitingDialog::TIPS_INFO = "Waiting"; -WaitingDialog::WaitingDialog(QWidget *parent, shared_ptr dev_inst) : +WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptr dev_inst) : QDialog(parent), _dev_inst(dev_inst), _button_box(QDialogButtonBox::Save | QDialogButtonBox::Abort, diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 10410ba6..9725804a 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -225,8 +225,7 @@ void MainWindow::setup_ui() // Set the title QString title = QApplication::applicationName()+" v"+QApplication::applicationVersion(); std::string std_title = title.toStdString(); - setWindowTitle(QApplication::translate("MainWindow", std_title.c_str(), 0, - QApplication::UnicodeUTF8)); + setWindowTitle(QApplication::translate("MainWindow", std_title.c_str(), 0)); // Setup _session events connect(&_session, SIGNAL(capture_state_changed(int)), this, @@ -520,7 +519,7 @@ void MainWindow::on_screenShot() tr("%1 Files (*.%2);;All Files (*)") .arg(format.toUpper()).arg(format)); if (!fileName.isEmpty()) - pixmap.save(fileName, format.toAscii()); + pixmap.save(fileName, format.toLatin1()); } void MainWindow::on_save() diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index 9e51799a..55624719 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -54,6 +54,9 @@ #include #include +#include +#include +#include #include @@ -179,6 +182,103 @@ void SigSession::save_file(const std::string &name){ snapshot->get_sample_count()); } +QList SigSession::getSuportedExportFormats(){ + const struct sr_output_module** supportedModules = sr_output_list(); + QList list; + while(*supportedModules){ + if(*supportedModules == NULL) + break; + QString format((*supportedModules)->desc); + format.append(" (*."); + format.append((*supportedModules)->id); + format.append(")"); + list.append(format); + *supportedModules++; + } + return list; +} + +void SigSession::cancelSaveFile(){ + saveFileThreadRunning = false; +} + +void SigSession::export_file(const std::string &name, QWidget* parent, const std::string &ext){ + const deque< boost::shared_ptr > &snapshots = + _logic_data->get_snapshots(); + if(snapshots.empty()) + return; + const boost::shared_ptr & snapshot = + snapshots.front(); + const struct sr_output_module** supportedModules = sr_output_list(); + const struct sr_output_module* outModule = NULL; + while(*supportedModules){ + if(*supportedModules == NULL) + break; + if(!strcmp((*supportedModules)->id, ext.c_str())){ + outModule = *supportedModules; + break; + } + *supportedModules++; + } + if(outModule == NULL) + return; + struct sr_output output; + GHashTable *params = g_hash_table_new(g_str_hash, g_str_equal); + GVariant* filenameGVariant = g_variant_new_string(name.c_str()); + g_hash_table_insert(params, (char*)"filename", filenameGVariant); + output.module = (sr_output_module*) outModule; + output.sdi = _dev_inst->dev_inst(); + output.param = NULL; + if(outModule->init) + outModule->init(&output, params); + QFile file(name.c_str()); + file.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out(&file); + QFuture future = QtConcurrent::run([&]{ + saveFileThreadRunning = true; + unsigned char* datat = (unsigned char*)snapshot->get_data(); + unsigned int numsamples = snapshot->get_sample_count()*snapshot->unit_size(); + GString *data_out; + int usize = 8192; + int size = usize; + struct sr_datafeed_logic lp; + struct sr_datafeed_packet p; + for(uint64_t i = 0; i < numsamples; i+=usize){ + if(numsamples - i < usize) + size = numsamples - i; + lp.data = &datat[i]; + lp.length = size; + lp.unitsize = snapshot->unit_size(); + p.type = SR_DF_LOGIC; + p.payload = &lp; + outModule->receive(&output, &p, &data_out); + if(data_out){ + out << (char*) data_out->str; + g_string_free(data_out,TRUE); + } + emit progressSaveFileValueChanged(i*100/numsamples); + if(!saveFileThreadRunning) + break; + } + }); + QFutureWatcher watcher; + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(QString::fromUtf8("Exporting data... It can take a while."), + QString::fromUtf8("Cancel"),0,100,parent,flags); + dlg.setWindowModality(Qt::WindowModal); + watcher.setFuture(future); + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + connect(this,SIGNAL(progressSaveFileValueChanged(int)),&dlg,SLOT(setValue(int))); + connect(&dlg,SIGNAL(canceled()),this,SLOT(cancelSaveFile())); + dlg.exec(); + future.waitForFinished(); + // optional, as QFile destructor will already do it: + file.close(); + outModule->cleanup(&output); + g_hash_table_destroy(params); + g_variant_unref(filenameGVariant); +} + void SigSession::set_default_device(boost::function error_handler) { shared_ptr default_device; diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index cf4674dd..b625b03d 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -86,8 +87,9 @@ class SigSession : public QObject Q_OBJECT private: - static const float Oversampling = 2.0f; + static constexpr float Oversampling = 2.0f; static const int ViewTime = 800; + bool saveFileThreadRunning = false; public: enum capture_state { @@ -115,6 +117,9 @@ public: void save_file(const std::string &name); void set_default_device(boost::function error_handler); + void export_file(const std::string &name, QWidget* parent, const std::string &ext); + + void set_default_device(); void release_device(device::DevInst *dev_inst); @@ -132,6 +137,7 @@ public: std::vector< boost::shared_ptr > get_group_signals(); + QList getSuportedExportFormats(); #ifdef ENABLE_DECODE bool add_decoder(srd_decoder *const dec); @@ -282,11 +288,15 @@ signals: void malloc_error(); void zero_adj(); + void progressSaveFileValueChanged(int percent); public slots: void reload(); void refresh(); +private slots: + void cancelSaveFile(); + private: // TODO: This should not be necessary. Multiple concurrent // sessions should should be supported and it should be diff --git a/DSView/pv/toolbars/filebar.cpp b/DSView/pv/toolbars/filebar.cpp index 736b57b8..3bf4c185 100644 --- a/DSView/pv/toolbars/filebar.cpp +++ b/DSView/pv/toolbars/filebar.cpp @@ -48,7 +48,7 @@ FileBar::FileBar(SigSession &session, QWidget *parent) : _action_open = new QAction(this); _action_open->setText(QApplication::translate( - "File", "&Open...", 0, QApplication::UnicodeUTF8)); + "File", "&Open...", 0)); _action_open->setIcon(QIcon::fromTheme("file", QIcon(":/icons/open.png"))); _action_open->setObjectName(QString::fromUtf8("actionOpen")); @@ -57,16 +57,24 @@ FileBar::FileBar(SigSession &session, QWidget *parent) : _action_save = new QAction(this); _action_save->setText(QApplication::translate( - "File", "&Save...", 0, QApplication::UnicodeUTF8)); + "File", "&Save...", 0)); _action_save->setIcon(QIcon::fromTheme("file", QIcon(":/icons/save.png"))); _action_save->setObjectName(QString::fromUtf8("actionSave")); _file_button.addAction(_action_save); connect(_action_save, SIGNAL(triggered()), this, SLOT(on_actionSave_triggered())); + _action_export = new QAction(this); + _action_export->setText(QApplication::translate("File", "&Export...", 0)); + _action_export->setIcon(QIcon::fromTheme("file",QIcon(":/icons/instant.png"))); + _action_export->setObjectName(QString::fromUtf8("actionExport")); + _file_button.addAction(_action_export); + connect(_action_export, SIGNAL(triggered()), this, SLOT(on_actionExport_triggered())); + + _action_capture = new QAction(this); _action_capture->setText(QApplication::translate( - "File", "&Capture...", 0, QApplication::UnicodeUTF8)); + "File", "&Capture...", 0)); _action_capture->setIcon(QIcon::fromTheme("file", QIcon(":/icons/capture.png"))); _action_capture->setObjectName(QString::fromUtf8("actionCapture")); @@ -84,7 +92,7 @@ void FileBar::on_actionOpen_triggered() // Show the dialog const QString file_name = QFileDialog::getOpenFileName( this, tr("Open File"), "", tr( - "DSView Sessions (*.dsl)")); + "DSView Sessions (*.dsl);;All Files (*.*)")); if (!file_name.isEmpty()) load_file(file_name); } @@ -108,6 +116,45 @@ void FileBar::show_session_error( msg.exec(); } +void FileBar::on_actionExport_triggered(){ + int unit_size; + uint64_t length; + void* buf = _session.get_buf(unit_size, length); + if (!buf) { + QMessageBox msg(this); + msg.setText("Data Export"); + msg.setInformativeText("No Data to Save!"); + msg.setStandardButtons(QMessageBox::Ok); + msg.setIcon(QMessageBox::Warning); + msg.exec(); + } else if (_session.get_device()->dev_inst()->mode != LOGIC) { + QMessageBox msg(this); + msg.setText("Export Data"); + msg.setInformativeText("DSLogic currently only support exporting logic data to file!"); + msg.setStandardButtons(QMessageBox::Ok); + msg.setIcon(QMessageBox::Warning); + msg.exec(); + }else { + QList supportedFormats = _session.getSuportedExportFormats(); + QString filter; + for(int i = 0; i < supportedFormats.count();i++){ + filter.append(supportedFormats[i]); + if(i < supportedFormats.count() - 1) + filter.append(";;"); + } + QString file_name = QFileDialog::getSaveFileName( + this, tr("Export Data"), "",filter,&filter); + if (!file_name.isEmpty()) { + QFileInfo f(file_name); + QStringList list = filter.split('.').last().split(')'); + QString ext = list.first(); + if(f.suffix().compare(ext)) + file_name+=tr(".")+ext; + _session.export_file(file_name.toStdString(), this, ext.toStdString()); + } + } +} + void FileBar::on_actionSave_triggered() { //save(); @@ -129,10 +176,13 @@ void FileBar::on_actionSave_triggered() msg.setIcon(QMessageBox::Warning); msg.exec(); }else { - const QString file_name = QFileDialog::getSaveFileName( + QString file_name = QFileDialog::getSaveFileName( this, tr("Save File"), "", tr("DSView Session (*.dsl)")); if (!file_name.isEmpty()) { + QFileInfo f(file_name); + if(f.suffix().compare("dsl")) + file_name.append(tr(".dsl")); _session.save_file(file_name.toStdString()); } } diff --git a/DSView/pv/toolbars/filebar.h b/DSView/pv/toolbars/filebar.h index 1ac54feb..a10a405c 100644 --- a/DSView/pv/toolbars/filebar.h +++ b/DSView/pv/toolbars/filebar.h @@ -58,6 +58,7 @@ private slots: void on_actionOpen_triggered(); void on_actionSave_triggered(); void on_actionCapture_triggered(); + void on_actionExport_triggered(); private: bool _enable; @@ -67,6 +68,7 @@ private: QAction *_action_open; QAction *_action_save; + QAction *_action_export; QAction *_action_capture; }; diff --git a/DSView/pv/toolbars/logobar.cpp b/DSView/pv/toolbars/logobar.cpp index 842a74ed..00063406 100644 --- a/DSView/pv/toolbars/logobar.cpp +++ b/DSView/pv/toolbars/logobar.cpp @@ -47,7 +47,7 @@ LogoBar::LogoBar(SigSession &session, QWidget *parent) : _about = new QAction(this); _about->setText(QApplication::translate( - "File", "&About...", 0, QApplication::UnicodeUTF8)); + "File", "&About...", 0)); _about->setIcon(QIcon::fromTheme("file", QIcon(":/icons/about.png"))); _about->setObjectName(QString::fromUtf8("actionAbout")); @@ -56,7 +56,7 @@ LogoBar::LogoBar(SigSession &session, QWidget *parent) : _wiki = new QAction(this); _wiki->setText(QApplication::translate( - "File", "&Wiki", 0, QApplication::UnicodeUTF8)); + "File", "&Wiki", 0)); _wiki->setIcon(QIcon::fromTheme("file", QIcon(":/icons/wiki.png"))); _wiki->setObjectName(QString::fromUtf8("actionWiki")); diff --git a/DSView/pv/view/analogsignal.cpp b/DSView/pv/view/analogsignal.cpp index ea44c0c9..6bf8ad33 100644 --- a/DSView/pv/view/analogsignal.cpp +++ b/DSView/pv/view/analogsignal.cpp @@ -65,7 +65,7 @@ AnalogSignal::~AnalogSignal() { } -shared_ptr AnalogSignal::data() const +boost::shared_ptr AnalogSignal::data() const { return _data; } @@ -86,7 +86,7 @@ void AnalogSignal::paint_mid(QPainter &p, int left, int right) assert(scale > 0); const double offset = _view->offset(); - const deque< shared_ptr > &snapshots = + const deque< boost::shared_ptr > &snapshots = _data->get_snapshots(); if (snapshots.empty()) return; diff --git a/DSView/pv/view/devmode.cpp b/DSView/pv/view/devmode.cpp index b4efbb55..295f8b0d 100644 --- a/DSView/pv/view/devmode.cpp +++ b/DSView/pv/view/devmode.cpp @@ -68,7 +68,7 @@ void DevMode::set_device() l; l = l->next) { sr_dev_mode *mode = (sr_dev_mode *)l->data; - shared_ptr mode_button = shared_ptr(new QPushButton(NULL)); + boost::shared_ptr mode_button = boost::shared_ptr(new QPushButton(NULL)); mode_button->setFlat(true); mode_button->setText(mode->name); @@ -97,7 +97,7 @@ void DevMode::paintEvent(QPaintEvent*) painter.setRenderHint(QPainter::Antialiasing); painter.setPen(Qt::NoPen); - for(std::map, sr_dev_mode *>::const_iterator i = _mode_button_list.begin(); + for(std::map, sr_dev_mode *>::const_iterator i = _mode_button_list.begin(); i != _mode_button_list.end(); i++) { const boost::shared_ptr dev_inst = _view.session().get_device(); assert(dev_inst); @@ -118,7 +118,7 @@ void DevMode::on_mode_change() assert(dev_inst); QPushButton *button = qobject_cast(sender()); - for(std::map, sr_dev_mode *>::const_iterator i = _mode_button_list.begin(); + for(std::map, sr_dev_mode *>::const_iterator i = _mode_button_list.begin(); i != _mode_button_list.end(); i++) { if ((*i).first.get() == button) { if (dev_inst->dev_inst()->mode != (*i).second->mode) { diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index b08c409f..743ce468 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -109,7 +109,7 @@ const int DsoSignal::DownMargin = 30; const int DsoSignal::RightMargin = 30; DsoSignal::DsoSignal(boost::shared_ptr dev_inst, - shared_ptr data, + boost::shared_ptr data, const sr_channel * const probe): Signal(dev_inst, probe, DS_DSO), _data(data), @@ -173,7 +173,7 @@ DsoSignal::~DsoSignal() { } -shared_ptr DsoSignal::data() const +boost::shared_ptr DsoSignal::data() const { return _data; } @@ -582,7 +582,7 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) return; _scale = height * 1.0f / 256; - const shared_ptr &snapshot = + const boost::shared_ptr &snapshot = snapshots.front(); const uint16_t number_channels = snapshot->get_channel_num(); @@ -846,8 +846,8 @@ void DsoSignal::paint_measure(QPainter &p) const double upPeriod = get_hDialValue() * DS_CONF_DSO_HDIVS * 0.8; const double dnPeriod = get_hDialValue() * DS_CONF_DSO_HDIVS * 0.2; if (_period > upPeriod) { - shared_ptr dsoSig; - BOOST_FOREACH(const shared_ptr t, traces) { + boost::shared_ptr dsoSig; + BOOST_FOREACH(const boost::shared_ptr t, traces) { if (dsoSig = dynamic_pointer_cast(t)) { dsoSig->go_hDialNext(setted); setted = true; @@ -856,8 +856,8 @@ void DsoSignal::paint_measure(QPainter &p) } else if (_period > dnPeriod) { _autoH = false; } else { - shared_ptr dsoSig; - BOOST_FOREACH(const shared_ptr t, traces) { + boost::shared_ptr dsoSig; + BOOST_FOREACH(const boost::shared_ptr t, traces) { if (dsoSig = dynamic_pointer_cast(t)) { dsoSig->go_hDialPre(setted); setted = true; diff --git a/DSView/pv/view/groupsignal.cpp b/DSView/pv/view/groupsignal.cpp index 09ec31ad..1d339875 100644 --- a/DSView/pv/view/groupsignal.cpp +++ b/DSView/pv/view/groupsignal.cpp @@ -63,7 +63,7 @@ bool GroupSignal::enabled() const return true; } -shared_ptr GroupSignal::data() const +boost::shared_ptr GroupSignal::data() const { return _data; } diff --git a/DSView/pv/view/header.cpp b/DSView/pv/view/header.cpp index 9df5918c..d240bf0f 100644 --- a/DSView/pv/view/header.cpp +++ b/DSView/pv/view/header.cpp @@ -160,7 +160,7 @@ void Header::mouseDoubleClickEvent(QMouseEvent *event) const boost::shared_ptr mTrace = get_mTrace(action, event->pos()); if (action == Trace::LABEL && mTrace) { - shared_ptr dsoSig; + boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(mTrace)) { dsoSig->auto_set(); } @@ -239,8 +239,8 @@ void Header::mousePressEvent(QMouseEvent *event) else mTrace->set_trig(Trace::EDGETRIG); } else if (action == Trace::VDIAL && mTrace) { - shared_ptr dsoSig; - BOOST_FOREACH(const shared_ptr t, traces) { + boost::shared_ptr dsoSig; + BOOST_FOREACH(const boost::shared_ptr t, traces) { if (dsoSig = dynamic_pointer_cast(t)) { dsoSig->set_hDialActive(false); if (t != mTrace) { @@ -251,17 +251,17 @@ void Header::mousePressEvent(QMouseEvent *event) if (dsoSig = dynamic_pointer_cast(mTrace)) dsoSig->set_vDialActive(!dsoSig->get_vDialActive()); } else if (action == Trace::HDIAL && mTrace) { - shared_ptr dsoSig; + boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(mTrace)) { if (dsoSig->get_hDialActive()) { - BOOST_FOREACH(const shared_ptr t, traces) { + BOOST_FOREACH(const boost::shared_ptr t, traces) { if(dsoSig = dynamic_pointer_cast(t)) { dsoSig->set_vDialActive(false); dsoSig->set_hDialActive(false); } } } else { - BOOST_FOREACH(const shared_ptr t, traces) { + BOOST_FOREACH(const boost::shared_ptr t, traces) { if(dsoSig = dynamic_pointer_cast(t)) { dsoSig->set_vDialActive(false); dsoSig->set_hDialActive(true); @@ -270,12 +270,12 @@ void Header::mousePressEvent(QMouseEvent *event) } } } else if (action == Trace::CHEN && mTrace) { - shared_ptr dsoSig; + boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(mTrace)) { dsoSig->set_enable(!dsoSig->enabled()); } } else if (action == Trace::ACDC && mTrace) { - shared_ptr dsoSig; + boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(mTrace)) { if (strcmp(_view.session().get_device()->dev_inst()->driver->name, "DSLogic") == 0) dsoSig->set_acCoupling((dsoSig->get_acCoupling()+1)%2); @@ -287,7 +287,7 @@ void Header::mousePressEvent(QMouseEvent *event) if (~QApplication::keyboardModifiers() & Qt::ControlModifier) { // Unselect all other Traces because the Ctrl is not // pressed - BOOST_FOREACH(const shared_ptr t, traces) + BOOST_FOREACH(const boost::shared_ptr t, traces) if (t != mTrace) t->select(false); } @@ -330,13 +330,13 @@ void Header::wheelEvent(QWheelEvent *event) assert(event); if (event->orientation() == Qt::Vertical) { - const vector< shared_ptr > traces( + const vector< boost::shared_ptr > traces( _view.get_traces()); // Vertical scrolling double shift = event->delta() / 20.0; bool setted = false; - BOOST_FOREACH(const shared_ptr t, traces) { - shared_ptr dsoSig; + BOOST_FOREACH(const boost::shared_ptr t, traces) { + boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(t)) { if (dsoSig->get_vDialActive()) { if (shift > 1.0) @@ -407,7 +407,7 @@ void Header::mouseMoveEvent(QMouseEvent *event) // Ensure the Trace is selected sig->select(true); } else { - shared_ptr dsoSig; + boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(sig)) { dsoSig->set_zeroPos(y); dsoSig->select(false); diff --git a/DSView/pv/view/logicsignal.cpp b/DSView/pv/view/logicsignal.cpp index 9e9ae593..4750a115 100644 --- a/DSView/pv/view/logicsignal.cpp +++ b/DSView/pv/view/logicsignal.cpp @@ -85,12 +85,12 @@ const sr_channel* LogicSignal::probe() const return _probe; } -shared_ptr LogicSignal::data() const +boost::shared_ptr LogicSignal::data() const { return _data; } -shared_ptr LogicSignal::logic_data() const +boost::shared_ptr LogicSignal::logic_data() const { return _data; } diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index af440a76..3f106b83 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -27,7 +27,7 @@ #include -#include +#include #include #include #include @@ -211,10 +211,10 @@ void View::zoom(double steps, int offset) _scale *= std::pow(3.0/2.0, -steps); _scale = max(min(_scale, _maxscale), _minscale); }else { - const vector< shared_ptr > sigs(_session.get_signals()); + const vector< boost::shared_ptr > sigs(_session.get_signals()); bool setted = false; - BOOST_FOREACH(const shared_ptr s, sigs) { - shared_ptr dsoSig; + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(s)) { if(steps > 0.5) dsoSig->go_hDialPre(setted); @@ -273,19 +273,19 @@ void View::set_preScale_preOffset() set_scale_offset(_preScale, _preOffset); } -vector< shared_ptr > View::get_traces() const +vector< boost::shared_ptr > View::get_traces() const { - const vector< shared_ptr > sigs(_session.get_signals()); + const vector< boost::shared_ptr > sigs(_session.get_signals()); #ifdef ENABLE_DECODE - const vector< shared_ptr > decode_sigs( + const vector< boost::shared_ptr > decode_sigs( _session.get_decode_signals()); - vector< shared_ptr > traces( + vector< boost::shared_ptr > traces( sigs.size() + decode_sigs.size()); #else - vector< shared_ptr > traces(sigs.size()); + vector< boost::shared_ptr > traces(sigs.size()); #endif - vector< shared_ptr >::iterator i = traces.begin(); + vector< boost::shared_ptr >::iterator i = traces.begin(); i = copy(sigs.begin(), sigs.end(), i); #ifdef ENABLE_DECODE i = copy(decode_sigs.begin(), decode_sigs.end(), i); @@ -295,8 +295,8 @@ vector< shared_ptr > View::get_traces() const return traces; } -bool View::compare_trace_v_offsets(const shared_ptr &a, - const shared_ptr &b) +bool View::compare_trace_v_offsets(const boost::shared_ptr &a, + const boost::shared_ptr &b) { assert(a); assert(b); @@ -379,14 +379,14 @@ const QPointF& View::hover_point() const void View::normalize_layout() { - const vector< shared_ptr > traces(get_traces()); + const vector< boost::shared_ptr > traces(get_traces()); int v_min = INT_MAX; - BOOST_FOREACH(const shared_ptr t, traces) + BOOST_FOREACH(const boost::shared_ptr t, traces) v_min = min(t->get_v_offset(), v_min); const int delta = -min(v_min, 0); - BOOST_FOREACH(shared_ptr t, traces) + BOOST_FOREACH(boost::shared_ptr t, traces) t->set_v_offset(t->get_v_offset() + delta); verticalScrollBar()->setSliderPosition(_v_offset + delta); @@ -406,7 +406,7 @@ int View::get_signalHeight() void View::get_scroll_layout(double &length, double &offset) const { - const set< shared_ptr > data_set = _session.get_data(); + const set< boost::shared_ptr > data_set = _session.get_data(); if (data_set.empty()) return; @@ -473,8 +473,8 @@ void View::update_scale() void View::signals_changed() { int total_rows = 0; - const vector< shared_ptr > traces(get_traces()); - BOOST_FOREACH(const shared_ptr t, traces) + const vector< boost::shared_ptr > traces(get_traces()); + BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); if (dynamic_pointer_cast(t) || @@ -489,7 +489,7 @@ void View::signals_changed() _signalHeight = (int)((height <= 0) ? 1 : height); _spanY = _signalHeight + 2 * SignalMargin; int next_v_offset = SignalMargin; - BOOST_FOREACH(shared_ptr t, traces) { + BOOST_FOREACH(boost::shared_ptr t, traces) { t->set_view(this); const double traceHeight = _signalHeight*t->rows_size(); t->set_signalHeight((int)traceHeight); @@ -557,9 +557,9 @@ int View::headerWidth() QFont font = QApplication::font(); QFontMetrics fm(font); - const vector< shared_ptr > traces(get_traces()); + const vector< boost::shared_ptr > traces(get_traces()); if (!traces.empty()){ - BOOST_FOREACH(const shared_ptr t, traces) { + BOOST_FOREACH(const boost::shared_ptr t, traces) { maxNameWidth = max(fm.boundingRect(t->get_name()).width(), maxNameWidth); maxLeftWidth = max(t->get_leftWidth(), maxLeftWidth); maxRightWidth = max(t->get_rightWidth(), maxRightWidth); @@ -796,8 +796,8 @@ int View::get_view_width() { int view_width = 0; if (_session.get_device()->dev_inst()->mode == DSO) { - const vector< shared_ptr > sigs(_session.get_signals()); - BOOST_FOREACH(const shared_ptr s, sigs) { + const vector< boost::shared_ptr > sigs(_session.get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { view_width = max((double)view_width, s->get_view_rect().width()); } } else { diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index 228318ac..5337b190 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -75,7 +75,7 @@ public: static const QSizeF LabelPadding; static const int WellPixelsPerSample = 10.0f; - static const double MaxViewRate = 1.0f; + static constexpr double MaxViewRate = 1.0f; static const int MaxPixelsPerSample = 100.0f; public: diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 8bc05428..11bc42f1 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -79,8 +79,8 @@ int Viewport::get_total_height() const { int h = 0; - const vector< shared_ptr > traces(_view.get_traces()); - BOOST_FOREACH(const shared_ptr t, traces) { + const vector< boost::shared_ptr > traces(_view.get_traces()); + BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); h += (int)(t->get_signalHeight()); } @@ -105,8 +105,8 @@ void Viewport::paintEvent(QPaintEvent *event) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this); - const vector< shared_ptr > traces(_view.get_traces()); - BOOST_FOREACH(const shared_ptr t, traces) + const vector< boost::shared_ptr > traces(_view.get_traces()); + BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); t->paint_back(p, 0, _view.get_view_width()); @@ -132,7 +132,7 @@ void Viewport::paintEvent(QPaintEvent *event) paintSignals(p); } - BOOST_FOREACH(const shared_ptr t, traces) + BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); if (t->enabled()) @@ -148,7 +148,7 @@ void Viewport::paintEvent(QPaintEvent *event) void Viewport::paintSignals(QPainter &p) { - const vector< shared_ptr > traces(_view.get_traces()); + const vector< boost::shared_ptr > traces(_view.get_traces()); if (_view.scale() != _curScale || _view.offset() != _curOffset || _view.get_signalHeight() != _curSignalHeight || @@ -162,7 +162,7 @@ void Viewport::paintSignals(QPainter &p) QPainter dbp(&pixmap); dbp.initFrom(this); //p.setRenderHint(QPainter::Antialiasing, false); - BOOST_FOREACH(const shared_ptr t, traces) + BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); if (t->enabled()) @@ -361,10 +361,10 @@ void Viewport::mousePressEvent(QMouseEvent *event) // _zoom_rect_visible = true; // } - const vector< shared_ptr > sigs(_view.session().get_signals()); - BOOST_FOREACH(const shared_ptr s, sigs) { + const vector< boost::shared_ptr > sigs(_view.session().get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { assert(s); - shared_ptr dsoSig; + boost::shared_ptr dsoSig; if ((dsoSig = dynamic_pointer_cast(s)) && dsoSig->get_trig_rect(0, _view.get_view_width()).contains(_mouse_point)) { _drag_sig = s; @@ -387,7 +387,7 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) if (event->buttons() & Qt::LeftButton) { if (_drag_sig) { - shared_ptr dsoSig; + boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(_drag_sig)) dsoSig->set_trig_vpos(_mouse_point.y()); } else { @@ -503,7 +503,7 @@ void Viewport::measure() const vector< boost::shared_ptr > sigs(_view.session().get_signals()); BOOST_FOREACH(const boost::shared_ptr s, sigs) { assert(s); - shared_ptr logicSig; + boost::shared_ptr logicSig; if (logicSig = dynamic_pointer_cast(s)) { if (logicSig->measure(_view.hover_point(), _cur_sample, _nxt_sample, _thd_sample)) { _measure_shown = true; diff --git a/DSView/pv/view/viewport.h b/DSView/pv/view/viewport.h index 68cc93c6..ae43c0e2 100644 --- a/DSView/pv/view/viewport.h +++ b/DSView/pv/view/viewport.h @@ -47,6 +47,7 @@ class Viewport : public QWidget public: static const int HitCursorMargin = 10; + static const double HitCursorTimeMargin; public: explicit Viewport(View &parent); diff --git a/libsigrok4DSL/backend.c b/libsigrok4DSL/backend.c index ca1460f8..8acddbc4 100644 --- a/libsigrok4DSL/backend.c +++ b/libsigrok4DSL/backend.c @@ -261,7 +261,7 @@ static int sanity_check_all_input_modules(void) static int sanity_check_all_output_modules(void) { int i, errors, ret = SR_OK; - struct sr_output_format **outputs; + struct sr_output_module **outputs; const char *d; sr_spew("Sanity-checking all output modules."); @@ -276,18 +276,18 @@ static int sanity_check_all_output_modules(void) sr_err("No ID in module %d ('%s').", i, d); errors++; } - if (!outputs[i]->description) { + if (!outputs[i]->desc) { sr_err("No description in module %d ('%s').", i, d); errors++; } - if (outputs[i]->df_type < 10000 || outputs[i]->df_type > 10007) { + /*if (outputs[i]->df_type < 10000 || outputs[i]->df_type > 10007) { sr_err("Invalid df_type %d in module %d ('%s').", outputs[i]->df_type, i, d); errors++; - } + }*/ /* All modules must provide a data or recv API callback. */ - if (!outputs[i]->data && !outputs[i]->receive) { + if (!outputs[i]->receive) { sr_err("No data/receive in module %d ('%s').", i, d); errors++; } diff --git a/libsigrok4DSL/configure.ac b/libsigrok4DSL/configure.ac index 0e1bb392..44d958ea 100644 --- a/libsigrok4DSL/configure.ac +++ b/libsigrok4DSL/configure.ac @@ -227,7 +227,6 @@ AC_CONFIG_FILES([Makefile version.h hardware/Makefile hardware/DSL/Makefile input/Makefile output/Makefile - output/text/Makefile libsigrok4DSL.pc tests/Makefile ]) diff --git a/libsigrok4DSL/libsigrok.h b/libsigrok4DSL/libsigrok.h index 9d719bfb..d0edced4 100644 --- a/libsigrok4DSL/libsigrok.h +++ b/libsigrok4DSL/libsigrok.h @@ -415,7 +415,7 @@ struct sr_output { * A pointer to this output format's 'struct sr_output_format'. * The frontend can use this to call the module's callbacks. */ - struct sr_output_format *format; + struct sr_output_module *module; /** * The device for which this output module is creating output. This @@ -437,29 +437,57 @@ struct sr_output { * For example, the module might store a pointer to a chunk of output * there, and only flush it when it reaches a certain size. */ - void *internal; + void *priv; }; -struct sr_output_format { +/** Generic option struct used by various subsystems. */ +struct sr_option { + /* Short name suitable for commandline usage, [a-z0-9-]. */ + char *id; + /* Short name suitable for GUI usage, can contain UTF-8. */ + char *name; + /* Description of the option, in a sentence. */ + char *desc; + /* Default value for this option. */ + GVariant *def; + /* List of possible values, if this is an option with few values. */ + GSList *values; +}; + +/** Output module driver. */ +struct sr_output_module { /** - * A unique ID for this output format. Must not be NULL. - * - * It can be used by frontends to select this output format for use. - * - * For example, calling sigrok-cli with -O hex will - * select the hexadecimal text output format. + * A unique ID for this output module, suitable for use in command-line + * clients, [a-z0-9-]. Must not be NULL. */ char *id; /** - * A short description of the output format. Must not be NULL. + * A unique name for this output module, suitable for use in GUI + * clients, can contain UTF-8. Must not be NULL. + */ + const char *name; + + /** + * A short description of the output module. Must not be NULL. * * This can be displayed by frontends, e.g. when selecting the output - * format for saving a file. + * module for saving a file. */ - char *description; + char *desc; - int df_type; + /** + * A NULL terminated array of strings containing a list of file name + * extensions typical for the input file format, or NULL if there is + * no typical extension for this file format. + */ + const char *const *exts; + + /** + * Returns a NULL-terminated list of options this module can take. + * Can be NULL, if the module has no options. + */ + const struct sr_option *(*options) (void); /** * This function is called once, at the beginning of an output stream. @@ -473,73 +501,10 @@ struct sr_output_format { * * @param o Pointer to the respective 'struct sr_output'. * - * @return SR_OK upon success, a negative error code otherwise. + * @retval SR_OK Success + * @retval other Negative error code. */ - int (*init) (struct sr_output *o); - - /** - * Whenever a chunk of data comes in, it will be passed to the - * output module via this function. The data_in and - * length_in values refers to this data; the module - * must not alter or g_free() this buffer. - * - * The function must allocate a buffer for storing its output, and - * pass along a pointer to this buffer in the data_out - * parameter, as well as storing the length of the buffer in - * length_out. The calling frontend will g_free() - * this buffer when it's done with it. - * - * IMPORTANT: The memory allocation much happen using a glib memory - * allocation call (not a "normal" malloc) since g_free() will be - * used to free the memory! - * - * If there is no output, this function MUST store NULL in the - * data_out parameter, so the caller knows not to try - * and g_free() it. - * - * Note: This API call is obsolete, use receive() instead. - * - * @param o Pointer to the respective 'struct sr_output'. - * @param data_in Pointer to the input data buffer. - * @param length_in Length of the input. - * @param data_out Pointer to the allocated output buffer. - * @param length_out Length (in bytes) of the output. - * - * @return SR_OK upon success, a negative error code otherwise. - */ - int (*data) (struct sr_output *o, const uint8_t *data_in, - uint64_t length_in, uint8_t **data_out, - uint64_t *length_out); - - /** - * This function is called when an event occurs in the datafeed - * which the output module may need to be aware of. No data is - * passed in, only the fact that the event occurs. The following - * events can currently be passed in: - * - * - SR_DF_TRIGGER: At this point in the datafeed, the trigger - * matched. The output module may mark this in some way, e.g. by - * plotting a red line on a graph. - * - * - SR_DF_END: This marks the end of the datafeed. No more calls - * into the output module will be done, so this is a good time to - * free up any memory used to keep state, for example. - * - * Any output generated by this function must have a reference to - * it stored in the data_out and length_out - * parameters, or NULL if no output was generated. - * - * Note: This API call is obsolete, use receive() instead. - * - * @param o Pointer to the respective 'struct sr_output'. - * @param event_type Type of event that occured. - * @param data_out Pointer to the allocated output buffer. - * @param length_out Length (in bytes) of the output. - * - * @return SR_OK upon success, a negative error code otherwise. - */ - int (*event) (struct sr_output *o, int event_type, uint8_t **data_out, - uint64_t *length_out); + int (*init) (struct sr_output *o, GHashTable *options); /** * This function is passed a copy of every packed in the data feed. @@ -556,9 +521,10 @@ struct sr_output_format { * @param out A pointer where a GString * should be stored if * the module generates output, or NULL if not. * - * @return SR_OK upon success, a negative error code otherwise. + * @retval SR_OK Success + * @retval other Negative error code. */ - int (*receive) (struct sr_output *o, const struct sr_dev_inst *sdi, + int (*receive) (const struct sr_output *o, const struct sr_datafeed_packet *packet, GString **out); /** @@ -566,11 +532,13 @@ struct sr_output_format { * the output module, and can be used to free any internal * resources the module may keep. * - * @return SR_OK upon success, a negative error code otherwise. + * @retval SR_OK Success + * @retval other Negative error code. */ int (*cleanup) (struct sr_output *o); }; + enum { SR_CHANNEL_LOGIC = 10000, SR_CHANNEL_DSO, diff --git a/libsigrok4DSL/output/Makefile.am b/libsigrok4DSL/output/Makefile.am index fbc093c3..a7148af5 100644 --- a/libsigrok4DSL/output/Makefile.am +++ b/libsigrok4DSL/output/Makefile.am @@ -17,21 +17,19 @@ ## along with this program. If not, see . ## -SUBDIRS = text - # Local lib, this is NOT meant to be installed! noinst_LTLIBRARIES = libsigrok4DSLoutput.la libsigrok4DSLoutput_la_SOURCES = \ - out_binary.c \ - out_vcd.c \ - out_csv.c \ - out_analog.c \ - output.c + output.c \ + csv.c \ + vcd.c \ + gnuplot.c \ + srzip.c libsigrok4DSLoutput_la_CFLAGS = \ -I$(top_srcdir) -libsigrok4DSLoutput_la_LIBADD = \ - text/libsigrok4DSLoutputtext.la +#libsigrok4DSLogicoutput_la_LIBADD = \ +# text/libsigrok4DSLogicoutputtext.la diff --git a/libsigrok4DSL/output/csv.c b/libsigrok4DSL/output/csv.c new file mode 100644 index 00000000..94bffc65 --- /dev/null +++ b/libsigrok4DSL/output/csv.c @@ -0,0 +1,225 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2011 Uwe Hermann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "config.h" /* Needed for PACKAGE_STRING and others. */ +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#define LOG_PREFIX "output/csv" + +struct context { + unsigned int num_enabled_channels; + uint64_t samplerate; + char separator; + gboolean header_done; + int *channel_index; +}; + +/* + * TODO: + * - Option to specify delimiter character and/or string. + * - Option to (not) print metadata as comments. + * - Option to specify the comment character(s), e.g. # or ; or C/C++-style. + * - Option to (not) print samplenumber / time as extra column. + * - Option to "compress" output (only print changed samples, VCD-like). + * - Option to print comma-separated bits, or whole bytes/words (for 8/16 + * channel LAs) as ASCII/hex etc. etc. + * - Trigger support. + */ + +static int init(struct sr_output *o, GHashTable *options) +{ + struct context *ctx; + struct sr_channel *ch; + GSList *l; + int i; + + (void)options; + + if (!o || !o->sdi) + return SR_ERR_ARG; + + ctx = g_malloc0(sizeof(struct context)); + o->priv = ctx; + ctx->separator = ','; + + /* Get the number of channels, and the unitsize. */ + for (l = o->sdi->channels; l; l = l->next) { + ch = l->data; + if (ch->type != SR_CHANNEL_LOGIC) + continue; + if (!ch->enabled) + continue; + ctx->num_enabled_channels++; + } + ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels); + + /* Once more to map the enabled channels. */ + for (i = 0, l = o->sdi->channels; l; l = l->next) { + ch = l->data; + if (ch->type != SR_CHANNEL_LOGIC) + continue; + if (!ch->enabled) + continue; + ctx->channel_index[i++] = ch->index; + } + + return SR_OK; +} + +static GString *gen_header(const struct sr_output *o) +{ + struct context *ctx; + struct sr_channel *ch; + GVariant *gvar; + GString *header; + GSList *l; + time_t t; + int num_channels, i; + char *samplerate_s; + + ctx = o->priv; + header = g_string_sized_new(512); + + /* Some metadata */ + t = time(NULL); + g_string_append_printf(header, "; CSV, generated by %s on %s", + PACKAGE_STRING, ctime(&t)); + + /* Columns / channels */ + num_channels = g_slist_length(o->sdi->channels); + g_string_append_printf(header, "; Channels (%d/%d):", + ctx->num_enabled_channels, num_channels); + for (i = 0, l = o->sdi->channels; l; l = l->next, i++) { + ch = l->data; + if (ch->type != SR_CHANNEL_LOGIC) + continue; + if (!ch->enabled) + continue; + g_string_append_printf(header, " %s,", ch->name); + } + if (o->sdi->channels) + /* Drop last separator. */ + g_string_truncate(header, header->len - 1); + g_string_append_printf(header, "\n"); + + if (ctx->samplerate == 0) { + if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, SR_CONF_SAMPLERATE, + &gvar) == SR_OK) { + ctx->samplerate = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } + } + if (ctx->samplerate != 0) { + samplerate_s = sr_samplerate_string(ctx->samplerate); + g_string_append_printf(header, "; Samplerate: %s\n", samplerate_s); + g_free(samplerate_s); + } + + return header; +} + +static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet, + GString **out) +{ + const struct sr_datafeed_meta *meta; + const struct sr_datafeed_logic *logic; + const struct sr_config *src; + GSList *l; + struct context *ctx; + int idx; + uint64_t i, j; + gchar *p, c; + + *out = NULL; + if (!o || !o->sdi) + return SR_ERR_ARG; + if (!(ctx = o->priv)) + return SR_ERR_ARG; + + switch (packet->type) { + case SR_DF_META: + meta = packet->payload; + for (l = meta->config; l; l = l->next) { + src = l->data; + if (src->key != SR_CONF_SAMPLERATE) + continue; + ctx->samplerate = g_variant_get_uint64(src->data); + } + break; + case SR_DF_LOGIC: + logic = packet->payload; + if (!ctx->header_done) { + *out = gen_header(o); + ctx->header_done = TRUE; + } else { + *out = g_string_sized_new(512); + } + + for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) { + for (j = 0; j < ctx->num_enabled_channels; j++) { + idx = ctx->channel_index[j]; + p = logic->data + i + idx / 8; + c = *p & (1 << (idx % 8)); + g_string_append_c(*out, c ? '1' : '0'); + g_string_append_c(*out, ctx->separator); + } + if (j) { + /* Drop last separator. */ + g_string_truncate(*out, (*out)->len - 1); + } + g_string_append_printf(*out, "\n"); + } + break; + } + + return SR_OK; +} + +static int cleanup(struct sr_output *o) +{ + struct context *ctx; + + if (!o || !o->sdi) + return SR_ERR_ARG; + + if (o->priv) { + ctx = o->priv; + g_free(ctx->channel_index); + g_free(o->priv); + o->priv = NULL; + } + + return SR_OK; +} + +SR_PRIV struct sr_output_module output_csv = { + .id = "csv", + .name = "CSV", + .desc = "Comma-separated values", + .exts = (const char*[]){"csv", NULL}, + .options = NULL, + .init = init, + .receive = receive, + .cleanup = cleanup, +}; diff --git a/libsigrok4DSL/output/gnuplot.c b/libsigrok4DSL/output/gnuplot.c new file mode 100644 index 00000000..b547a623 --- /dev/null +++ b/libsigrok4DSL/output/gnuplot.c @@ -0,0 +1,229 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2010 Uwe Hermann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "config.h" /* Needed for PACKAGE_STRING and others. */ +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#define LOG_PREFIX "output/gnuplot" + +struct context { + unsigned int num_enabled_channels; + uint64_t samplerate; + uint64_t samplecount; + gboolean header_done; + uint8_t *prevsample; + int *channel_index; +}; + +static const char *gnuplot_header = "\ +# Sample data in space-separated columns format usable by gnuplot.\n"; +static const char *gnuplot_header2 = "\ +#\n# Column\tChannel\n\ +# -----------------------------------------------------------------------------\n\ +# 0\t\tSample counter (for internal gnuplot purposes)\n"; + + +static int init(struct sr_output *o, GHashTable *options) +{ + struct context *ctx; + struct sr_channel *ch; + GSList *l; + unsigned int i; + + (void)options; + + if (!o || !o->sdi) + return SR_ERR_ARG; + + ctx = g_malloc0(sizeof(struct context)); + o->priv = ctx; + ctx->num_enabled_channels = 0; + for (l = o->sdi->channels; l; l = l->next) { + ch = l->data; + if (ch->type != SR_CHANNEL_LOGIC) + continue; + if (!ch->enabled) + continue; + ctx->num_enabled_channels++; + } + if (ctx->num_enabled_channels <= 0) { + sr_err("No logic channel enabled."); + return SR_ERR; + } + ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels); + + /* Once more to map the enabled channels. */ + for (i = 0, l = o->sdi->channels; l; l = l->next) { + ch = l->data; + if (ch->type != SR_CHANNEL_LOGIC) + continue; + if (!ch->enabled) + continue; + ctx->channel_index[i++] = ch->index; + } + + return SR_OK; +} + +static GString *gen_header(const struct sr_output *o) +{ + struct context *ctx; + struct sr_channel *ch; + GVariant *gvar; + GString *header; + time_t t; + unsigned int num_channels, i; + char *samplerate_s; + + ctx = o->priv; + if (ctx->samplerate == 0) { + if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, SR_CONF_SAMPLERATE, + &gvar) == SR_OK) { + ctx->samplerate = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } + } + + t = time(NULL); + header = g_string_sized_new(512); + g_string_printf(header, "%s", gnuplot_header); + g_string_append_printf(header, "# Generated by %s on %s", + PACKAGE_STRING, ctime(&t)); + + num_channels = g_slist_length(o->sdi->channels); + g_string_append_printf(header, "# Acquisition with %d/%d channels", + ctx->num_enabled_channels, num_channels); + if (ctx->samplerate != 0) { + samplerate_s = sr_samplerate_string(ctx->samplerate); + g_string_append_printf(header, " at %s", samplerate_s); + g_free(samplerate_s); + } + g_string_append_printf(header, "\n"); + + g_string_append_printf(header, "%s", gnuplot_header2); + + /* Columns / channels */ + for (i = 0; i < ctx->num_enabled_channels; i++) { + ch = g_slist_nth_data(o->sdi->channels, ctx->channel_index[i]); + g_string_append_printf(header, "# %d\t\t%s\n", i + 1, ch->name); + } + + return header; +} + +static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet, + GString **out) +{ + const struct sr_datafeed_meta *meta; + const struct sr_datafeed_logic *logic; + const struct sr_config *src; + GSList *l; + struct context *ctx; + const uint8_t *sample; + unsigned int curbit, p, idx, i; + + *out = NULL; + if (!o || !o->priv) + return SR_ERR_BUG; + ctx = o->priv; + + if (packet->type == SR_DF_META) { + meta = packet->payload; + for (l = meta->config; l; l = l->next) { + src = l->data; + if (src->key != SR_CONF_SAMPLERATE) + continue; + ctx->samplerate = g_variant_get_uint64(src->data); + } + } + + if (packet->type != SR_DF_LOGIC) + return SR_OK; + logic = packet->payload; + + if (!ctx->prevsample) { + /* Can't allocate this until we know the stream's unitsize. */ + ctx->prevsample = g_malloc0(logic->unitsize); + } + + if (!ctx->header_done) { + *out = gen_header(o); + ctx->header_done = TRUE; + } else { + *out = g_string_sized_new(512); + } + + for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) { + sample = logic->data + i; + ctx->samplecount++; + + /* + * Don't output the same sample multiple times, but make + * sure to output at least the first and last sample. + */ + if (i > 0 && i < logic->length - logic->unitsize) { + if (!memcmp(sample, ctx->prevsample, logic->unitsize)) + continue; + } + memcpy(ctx->prevsample, sample, logic->unitsize); + + /* The first column is a counter (needed for gnuplot). */ + g_string_append_printf(*out, "%" PRIu64 "\t", ctx->samplecount); + + /* The next columns are the values of all channels. */ + for (p = 0; p < ctx->num_enabled_channels; p++) { + idx = ctx->channel_index[p]; + curbit = (sample[idx / 8] & ((uint8_t) (1 << (idx % 8)))) >> (idx % 8); + g_string_append_printf(*out, "%d ", curbit); + } + g_string_append_printf(*out, "\n"); + } + + return SR_OK; +} + +static int cleanup(struct sr_output *o) +{ + struct context *ctx; + + if (!o || !o->priv) + return SR_ERR_BUG; + ctx = o->priv; + g_free(ctx->channel_index); + g_free(ctx->prevsample); + g_free(ctx); + + return SR_OK; +} + +SR_PRIV struct sr_output_module output_gnuplot = { + .id = "gnuplot", + .name = "Gnuplot", + .desc = "Gnuplot data file format", + .exts = (const char*[]){"dat", NULL}, + .options = NULL, + .init = init, + .receive = receive, + .cleanup = cleanup, +}; diff --git a/libsigrok4DSL/output/out_analog.c b/libsigrok4DSL/output/out_analog.c deleted file mode 100644 index e1efdad3..00000000 --- a/libsigrok4DSL/output/out_analog.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2012 Bert Vermeulen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "output/analog: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -struct context { - int num_enabled_probes; - GPtrArray *probelist; -}; - -static int init(struct sr_output *o) -{ - struct context *ctx; - struct sr_channel *probe; - GSList *l; - - sr_spew("Initializing output module."); - - if (!o || !o->sdi) - return SR_ERR_ARG; - - if (!(ctx = g_try_malloc0(sizeof(struct context)))) { - sr_err("Output module context malloc failed."); - return SR_ERR_MALLOC; - } - o->internal = ctx; - - /* Get the number of probes and their names. */ - ctx->probelist = g_ptr_array_new(); - for (l = o->sdi->channels; l; l = l->next) { - probe = l->data; - if (!probe || !probe->enabled) - continue; - g_ptr_array_add(ctx->probelist, probe->name); - ctx->num_enabled_probes++; - } - - return SR_OK; -} - -static void si_printf(float value, GString *out, char *unitstr) -{ - float v; - - if (signbit(value)) - v = -(value); - else - v = value; - - if (v < 1e-12 || v > 1e+12) - g_string_append_printf(out, "%f %s", value, unitstr); - else if (v > 1e+9) - g_string_append_printf(out, "%f G%s", value / 1e+9, unitstr); - else if (v > 1e+6) - g_string_append_printf(out, "%f M%s", value / 1e+6, unitstr); - else if (v > 1e+3) - g_string_append_printf(out, "%f k%s", value / 1e+3, unitstr); - else if (v < 1e-9) - g_string_append_printf(out, "%f n%s", value * 1e+9, unitstr); - else if (v < 1e-6) - g_string_append_printf(out, "%f u%s", value * 1e+6, unitstr); - else if (v < 1e-3) - g_string_append_printf(out, "%f m%s", value * 1e+3, unitstr); - else - g_string_append_printf(out, "%f %s", value, unitstr); - -} - -static void fancyprint(int unit, int mqflags, float value, GString *out) -{ - switch (unit) { - case SR_UNIT_VOLT: - si_printf(value, out, "V"); - break; - case SR_UNIT_AMPERE: - si_printf(value, out, "A"); - break; - case SR_UNIT_OHM: - si_printf(value, out, ""); - g_string_append_unichar(out, 0x2126); - break; - case SR_UNIT_FARAD: - si_printf(value, out, "F"); - break; - case SR_UNIT_KELVIN: - si_printf(value, out, "K"); - break; - case SR_UNIT_CELSIUS: - si_printf(value, out, ""); - g_string_append_unichar(out, 0x00b0); - g_string_append_c(out, 'C'); - break; - case SR_UNIT_FAHRENHEIT: - si_printf(value, out, ""); - g_string_append_unichar(out, 0x00b0); - g_string_append_c(out, 'F'); - break; - case SR_UNIT_HERTZ: - si_printf(value, out, "Hz"); - break; - case SR_UNIT_PERCENTAGE: - g_string_append_printf(out, "%f%%", value); - break; - case SR_UNIT_BOOLEAN: - if (value > 0) - g_string_append_printf(out, "TRUE"); - else - g_string_append_printf(out, "FALSE"); - break; - case SR_UNIT_SECOND: - si_printf(value, out, "s"); - break; - case SR_UNIT_SIEMENS: - si_printf(value, out, "S"); - break; - case SR_UNIT_DECIBEL_MW: - si_printf(value, out, "dBu"); - break; - case SR_UNIT_DECIBEL_VOLT: - si_printf(value, out, "dBV"); - break; - case SR_UNIT_DECIBEL_SPL: - if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_A) - si_printf(value, out, "dB(A)"); - else if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_C) - si_printf(value, out, "dB(C)"); - else if (mqflags & SR_MQFLAG_SPL_FREQ_WEIGHT_Z) - si_printf(value, out, "dB(Z)"); - else - /* No frequency weighting, or non-standard "flat" */ - si_printf(value, out, "dB(SPL)"); - if (mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_S) - g_string_append(out, " S"); - else if (mqflags & SR_MQFLAG_SPL_TIME_WEIGHT_F) - g_string_append(out, " F"); - if (mqflags & SR_MQFLAG_SPL_LAT) - g_string_append(out, " LAT"); - else if (mqflags & SR_MQFLAG_SPL_PCT_OVER_ALARM) - /* Not a standard function for SLMs, so this is - * a made-up notation. */ - g_string_append(out, " %oA"); - break; - case SR_UNIT_CONCENTRATION: - g_string_append_printf(out, "%f ppm", value * 1000000); - break; - default: - si_printf(value, out, ""); - break; - } - if ((mqflags & (SR_MQFLAG_AC | SR_MQFLAG_DC)) == (SR_MQFLAG_AC | SR_MQFLAG_DC)) - g_string_append_printf(out, " AC+DC"); - else if (mqflags & SR_MQFLAG_AC) - g_string_append_printf(out, " AC"); - else if (mqflags & SR_MQFLAG_DC) - g_string_append_printf(out, " DC"); - g_string_append_c(out, '\n'); -} - -static int receive(struct sr_output *o, const struct sr_dev_inst *sdi, - const struct sr_datafeed_packet *packet, GString **out) -{ - const struct sr_datafeed_analog *analog; - struct sr_channel *probe; - GSList *l; - const float *fdata; - int i, p; - - (void)sdi; - - *out = NULL; - if (!o || !o->sdi) - return SR_ERR_ARG; - - switch (packet->type) { - case SR_DF_FRAME_BEGIN: - *out = g_string_new("FRAME-BEGIN\n"); - break; - case SR_DF_FRAME_END: - *out = g_string_new("FRAME-END\n"); - break; - case SR_DF_ANALOG: - analog = packet->payload; - fdata = (const float *)analog->data; - *out = g_string_sized_new(512); - for (i = 0; i < analog->num_samples; i++) { - for (l = analog->probes, p = 0; l; l = l->next, p++) { - probe = l->data; - g_string_append_printf(*out, "%s: ", probe->name); - fancyprint(analog->unit, analog->mqflags, - fdata[i + p], *out); - } - } - break; - } - - return SR_OK; -} - -static int cleanup(struct sr_output *o) -{ - struct context *ctx; - - if (!o || !o->sdi) - return SR_ERR_ARG; - ctx = o->internal; - - g_ptr_array_free(ctx->probelist, 1); - g_free(ctx); - o->internal = NULL; - - return SR_OK; -} - -SR_PRIV struct sr_output_format output_analog = { - .id = "analog", - .description = "Analog data", - .df_type = SR_DF_ANALOG, - .init = init, - .receive = receive, - .cleanup = cleanup -}; diff --git a/libsigrok4DSL/output/out_binary.c b/libsigrok4DSL/output/out_binary.c deleted file mode 100644 index 2fd858eb..00000000 --- a/libsigrok4DSL/output/out_binary.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2010 Uwe Hermann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "output/binary: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -static int data(struct sr_output *o, const uint8_t *data_in, - uint64_t length_in, uint8_t **data_out, uint64_t *length_out) -{ - uint8_t *outbuf; - - (void)o; - - if (!data_in) { - sr_err("%s: data_in was NULL", __func__); - return SR_ERR_ARG; - } - - if (!length_out) { - sr_err("%s: length_out was NULL", __func__); - return SR_ERR_ARG; - } - - if (length_in == 0) { - sr_err("%s: length_in was 0", __func__); - return SR_ERR_ARG; - } - - if (!(outbuf = g_try_malloc0(length_in))) { - sr_err("%s: outbuf malloc failed", __func__); - return SR_ERR_MALLOC; - } - - memcpy(outbuf, data_in, length_in); - *data_out = outbuf; - *length_out = length_in; - - return SR_OK; -} - -SR_PRIV struct sr_output_format output_binary = { - .id = "binary", - .description = "Raw binary", - .df_type = SR_DF_LOGIC, - .init = NULL, - .data = data, - .event = NULL, -}; diff --git a/libsigrok4DSL/output/out_csv.c b/libsigrok4DSL/output/out_csv.c deleted file mode 100644 index 6eec47e7..00000000 --- a/libsigrok4DSL/output/out_csv.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2011 Uwe Hermann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "config.h" /* Needed for PACKAGE_STRING and others. */ -#include "libsigrok.h" -#include "libsigrok-internal.h" - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "output/csv: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -struct context { - unsigned int num_enabled_probes; - unsigned int unitsize; - uint64_t samplerate; - GString *header; - char separator; -}; - -/* - * TODO: - * - Option to specify delimiter character and/or string. - * - Option to (not) print metadata as comments. - * - Option to specify the comment character(s), e.g. # or ; or C/C++-style. - * - Option to (not) print samplenumber / time as extra column. - * - Option to "compress" output (only print changed samples, VCD-like). - * - Option to print comma-separated bits, or whole bytes/words (for 8/16 - * probe LAs) as ASCII/hex etc. etc. - * - Trigger support. - */ - -static int init(struct sr_output *o) -{ - struct context *ctx; - struct sr_channel *probe; - GSList *l; - GVariant *gvar; - int num_probes; - time_t t; - - if (!o) { - sr_err("%s: o was NULL", __func__); - return SR_ERR_ARG; - } - - if (!o->sdi) { - sr_err("%s: o->sdi was NULL", __func__); - return SR_ERR_ARG; - } - - if (!(ctx = g_try_malloc0(sizeof(struct context)))) { - sr_err("%s: ctx malloc failed", __func__); - return SR_ERR_MALLOC; - } - - o->internal = ctx; - - /* Get the number of probes, and the unitsize. */ - for (l = o->sdi->channels; l; l = l->next) { - probe = l->data; - if (probe->enabled) - ctx->num_enabled_probes++; - } - - ctx->unitsize = (ctx->num_enabled_probes + 7) / 8; - - num_probes = g_slist_length(o->sdi->channels); - - if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, - SR_CONF_SAMPLERATE, &gvar) == SR_OK) { - ctx->samplerate = g_variant_get_uint64(gvar); - g_variant_unref(gvar); - } else - ctx->samplerate = 0; - - ctx->separator = ','; - ctx->header = g_string_sized_new(512); - - t = time(NULL); - - /* Some metadata */ - g_string_append_printf(ctx->header, "; CSV, generated by %s on %s", - PACKAGE_STRING, ctime(&t)); - g_string_append_printf(ctx->header, "; Samplerate: %"PRIu64"\n", - ctx->samplerate); - - /* Columns / channels */ - g_string_append_printf(ctx->header, "; Channels (%d/%d): ", - ctx->num_enabled_probes, num_probes); - for (l = o->sdi->channels; l; l = l->next) { - probe = l->data; - if (probe->enabled) - g_string_append_printf(ctx->header, "%s, ", probe->name); - } - g_string_append_printf(ctx->header, "\n"); - - return SR_OK; -} - -static int event(struct sr_output *o, int event_type, uint8_t **data_out, - uint64_t *length_out) -{ - struct context *ctx; - - if (!o) { - sr_err("%s: o was NULL", __func__); - return SR_ERR_ARG; - } - - if (!(ctx = o->internal)) { - sr_err("%s: o->internal was NULL", __func__); - return SR_ERR_ARG; - } - - if (!data_out) { - sr_err("%s: data_out was NULL", __func__); - return SR_ERR_ARG; - } - - switch (event_type) { - case SR_DF_TRIGGER: - sr_dbg("%s: SR_DF_TRIGGER event", __func__); - /* TODO */ - *data_out = NULL; - *length_out = 0; - break; - case SR_DF_END: - sr_dbg("%s: SR_DF_END event", __func__); - /* TODO */ - *data_out = NULL; - *length_out = 0; - g_free(o->internal); - o->internal = NULL; - break; - default: - sr_err("%s: unsupported event type: %d", __func__, event_type); - *data_out = NULL; - *length_out = 0; - break; - } - - return SR_OK; -} - -static int data(struct sr_output *o, const uint8_t *data_in, - uint64_t length_in, uint8_t **data_out, uint64_t *length_out) -{ - struct context *ctx; - GString *outstr; - uint64_t sample, i; - int j; - - if (!o) { - sr_err("%s: o was NULL", __func__); - return SR_ERR_ARG; - } - - if (!(ctx = o->internal)) { - sr_err("%s: o->internal was NULL", __func__); - return SR_ERR_ARG; - } - - if (!data_in) { - sr_err("%s: data_in was NULL", __func__); - return SR_ERR_ARG; - } - - if (ctx->header) { - /* First data packet. */ - outstr = ctx->header; - ctx->header = NULL; - } else { - outstr = g_string_sized_new(512); - } - - for (i = 0; i <= length_in - ctx->unitsize; i += ctx->unitsize) { - memcpy(&sample, data_in + i, ctx->unitsize); - for (j = ctx->num_enabled_probes - 1; j >= 0; j--) { - g_string_append_printf(outstr, "%d%c", - (int)((sample & (1 << j)) >> j), - ctx->separator); - } - g_string_append_printf(outstr, "\n"); - } - - *data_out = (uint8_t *)outstr->str; - *length_out = outstr->len; - g_string_free(outstr, FALSE); - - return SR_OK; -} - -SR_PRIV struct sr_output_format output_csv = { - .id = "csv", - .description = "Comma-separated values (CSV)", - .df_type = SR_DF_LOGIC, - .init = init, - .data = data, - .event = event, -}; diff --git a/libsigrok4DSL/output/out_vcd.c b/libsigrok4DSL/output/out_vcd.c deleted file mode 100644 index 7c00acb7..00000000 --- a/libsigrok4DSL/output/out_vcd.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2010 Uwe Hermann - * Copyright (C) 2013 Bert Vermeulen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include "config.h" /* Needed for PACKAGE and others. */ -#include "libsigrok.h" -#include "libsigrok-internal.h" - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "output/vcd: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -struct context { - int num_enabled_probes; - GArray *probeindices; - GString *header; - uint8_t *prevsample; - int period; - uint64_t samplerate; - unsigned int unitsize; -}; - -static const char *vcd_header_comment = "\ -$comment\n Acquisition with %d/%d probes at %s\n$end\n"; - -static int init(struct sr_output *o) -{ - struct context *ctx; - struct sr_channel *probe; - GSList *l; - GVariant *gvar; - int num_probes, i; - char *samplerate_s, *frequency_s, *timestamp; - time_t t; - - if (!(ctx = g_try_malloc0(sizeof(struct context)))) { - sr_err("%s: ctx malloc failed", __func__); - return SR_ERR_MALLOC; - } - - o->internal = ctx; - ctx->num_enabled_probes = 0; - ctx->probeindices = g_array_new(FALSE, FALSE, sizeof(int)); - - for (l = o->sdi->channels; l; l = l->next) { - probe = l->data; - if (!probe->enabled) - continue; - ctx->probeindices = g_array_append_val( - ctx->probeindices, probe->index); - ctx->num_enabled_probes++; - } - if (ctx->num_enabled_probes > 94) { - sr_err("VCD only supports 94 probes."); - return SR_ERR; - } - - ctx->unitsize = (ctx->num_enabled_probes + 7) / 8; - ctx->header = g_string_sized_new(512); - num_probes = g_slist_length(o->sdi->channels); - - /* timestamp */ - t = time(NULL); - timestamp = g_strdup(ctime(&t)); - timestamp[strlen(timestamp)-1] = 0; - g_string_printf(ctx->header, "$date %s $end\n", timestamp); - g_free(timestamp); - - /* generator */ - g_string_append_printf(ctx->header, "$version %s %s $end\n", - PACKAGE, PACKAGE_VERSION); - - if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, - SR_CONF_SAMPLERATE, &gvar) == SR_OK) { - ctx->samplerate = g_variant_get_uint64(gvar); - g_variant_unref(gvar); - if (!((samplerate_s = sr_samplerate_string(ctx->samplerate)))) { - g_string_free(ctx->header, TRUE); - g_free(ctx); - return SR_ERR; - } - g_string_append_printf(ctx->header, vcd_header_comment, - ctx->num_enabled_probes, num_probes, samplerate_s); - g_free(samplerate_s); - } - - /* timescale */ - /* VCD can only handle 1/10/100 (s - fs), so scale up first */ - if (ctx->samplerate > SR_MHZ(1)) - ctx->period = SR_GHZ(1); - else if (ctx->samplerate > SR_KHZ(1)) - ctx->period = SR_MHZ(1); - else - ctx->period = SR_KHZ(1); - if (!(frequency_s = sr_period_string(ctx->period))) { - g_string_free(ctx->header, TRUE); - g_free(ctx); - return SR_ERR; - } - g_string_append_printf(ctx->header, "$timescale %s $end\n", frequency_s); - g_free(frequency_s); - - /* scope */ - g_string_append_printf(ctx->header, "$scope module %s $end\n", PACKAGE); - - /* Wires / channels */ - for (i = 0, l = o->sdi->channels; l; l = l->next, i++) { - probe = l->data; - if (!probe->enabled) - continue; - g_string_append_printf(ctx->header, "$var wire 1 %c %s $end\n", - (char)('!' + i), probe->name); - } - - g_string_append(ctx->header, "$upscope $end\n" - "$enddefinitions $end\n$dumpvars\n"); - - if (!(ctx->prevsample = g_try_malloc0(ctx->unitsize))) { - g_string_free(ctx->header, TRUE); - g_free(ctx); - sr_err("%s: ctx->prevsample malloc failed", __func__); - return SR_ERR_MALLOC; - } - - return SR_OK; -} - -static int receive(struct sr_output *o, const struct sr_dev_inst *sdi, - const struct sr_datafeed_packet *packet, GString **out) -{ - const struct sr_datafeed_logic *logic; - struct context *ctx; - unsigned int i; - int p, curbit, prevbit, index; - uint8_t *sample; - static uint64_t samplecount = 0; - - (void)sdi; - - *out = NULL; - if (!o || !o->internal) - return SR_ERR_ARG; - ctx = o->internal; - - if (packet->type == SR_DF_END) { - *out = g_string_new("$dumpoff\n$end\n"); - return SR_OK; - } else if (packet->type != SR_DF_LOGIC) - return SR_OK; - - if (ctx->header) { - /* The header is still here, this must be the first packet. */ - *out = ctx->header; - ctx->header = NULL; - } else { - *out = g_string_sized_new(512); - } - - logic = packet->payload; - for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) { - samplecount++; - - sample = logic->data + i; - - for (p = 0; p < ctx->num_enabled_probes; p++) { - index = g_array_index(ctx->probeindices, int, p); - curbit = (sample[p / 8] & (((uint8_t) 1) << index)) >> index; - prevbit = (ctx->prevsample[p / 8] & (((uint64_t) 1) << index)) >> index; - - /* VCD only contains deltas/changes of signals. */ - if (prevbit == curbit) - continue; - - /* Output which signal changed to which value. */ - g_string_append_printf(*out, "#%" PRIu64 "\n%i%c\n", - (uint64_t)(((float)samplecount / ctx->samplerate) - * ctx->period), curbit, (char)('!' + p)); - } - - memcpy(ctx->prevsample, sample, ctx->unitsize); - } - - return SR_OK; -} - -static int cleanup(struct sr_output *o) -{ - struct context *ctx; - - if (!o || !o->internal) - return SR_ERR_ARG; - - ctx = o->internal; - g_free(ctx); - - return SR_OK; -} - -struct sr_output_format output_vcd = { - .id = "vcd", - .description = "Value Change Dump (VCD)", - .df_type = SR_DF_LOGIC, - .init = init, - .receive = receive, - .cleanup = cleanup, -}; diff --git a/libsigrok4DSL/output/output.c b/libsigrok4DSL/output/output.c index 62ce0c18..b0ba97fe 100644 --- a/libsigrok4DSL/output/output.c +++ b/libsigrok4DSL/output/output.c @@ -1,7 +1,7 @@ /* * This file is part of the libsigrok project. * - * Copyright (C) 2010-2012 Bert Vermeulen + * Copyright (C) 2014 Bert Vermeulen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,64 +17,327 @@ * along with this program. If not, see . */ +#include #include "libsigrok.h" #include "libsigrok-internal.h" +/** @cond PRIVATE */ +#define LOG_PREFIX "output" +/** @endcond */ + /** * @file * - * Output file/data format handling. + * Output module handling. */ /** - * @defgroup grp_output Output formats + * @defgroup grp_output Output modules * - * Output file/data format handling. + * Output module handling. * - * libsigrok supports several output (file) formats, e.g. binary, VCD, - * gnuplot, and so on. It provides an output API that frontends can use. - * New output formats can be added/implemented in libsigrok without having + * libsigrok supports several output modules for file formats such as binary, + * VCD, gnuplot, and so on. It provides an output API that frontends can use. + * New output modules can be added/implemented in libsigrok without having * to change the frontends at all. * * All output modules are fed data in a stream. Devices that can stream data - * into libsigrok live, instead of storing and then transferring the whole - * buffer, can thus generate output live. + * into libsigrok, instead of storing and then transferring the whole buffer, + * can thus generate output live. * - * Output modules are responsible for allocating enough memory to store - * their own output, and passing a pointer to that memory (and length) of - * the allocated memory back to the caller. The caller is then expected to - * free this memory when finished with it. + * Output modules generate a newly allocated GString. The caller is then + * expected to free this with g_string_free() when finished with it. * * @{ */ /** @cond PRIVATE */ -extern SR_PRIV struct sr_output_format output_text_bits; -extern SR_PRIV struct sr_output_format output_text_hex; -extern SR_PRIV struct sr_output_format output_text_ascii; -extern SR_PRIV struct sr_output_format output_binary; -extern SR_PRIV struct sr_output_format output_vcd; - -extern SR_PRIV struct sr_output_format output_csv; -extern SR_PRIV struct sr_output_format output_analog; -/* extern SR_PRIV struct sr_output_format output_analog_gnuplot; */ +extern SR_PRIV struct sr_output_module output_bits; +extern SR_PRIV struct sr_output_module output_hex; +extern SR_PRIV struct sr_output_module output_ascii; +extern SR_PRIV struct sr_output_module output_binary; +extern SR_PRIV struct sr_output_module output_vcd; +extern SR_PRIV struct sr_output_module output_ols; +extern SR_PRIV struct sr_output_module output_gnuplot; +extern SR_PRIV struct sr_output_module output_chronovu_la8; +extern SR_PRIV struct sr_output_module output_csv; +extern SR_PRIV struct sr_output_module output_analog; +extern SR_PRIV struct sr_output_module output_srzip; +extern SR_PRIV struct sr_output_module output_wav; /* @endcond */ -static struct sr_output_format *output_module_list[] = { - &output_text_bits, - &output_text_hex, - &output_text_ascii, - &output_binary, - &output_vcd, +static const struct sr_output_module *output_module_list[] = { &output_csv, + &output_vcd, + &output_gnuplot, + &output_srzip, + /*&output_ascii, + &output_binary, + &output_bits, + &output_hex, + &output_ols, + &output_chronovu_la8, &output_analog, - /* &output_analog_gnuplot, */ + &output_wav,*/ NULL, }; -SR_API struct sr_output_format **sr_output_list(void) +/** + * Returns a NULL-terminated list of all available output modules. + * + * @since 0.4.0 + */ +SR_API const struct sr_output_module **sr_output_list(void) { return output_module_list; } +/** + * Returns the specified output module's ID. + * + * @since 0.4.0 + */ +SR_API const char *sr_output_id_get(const struct sr_output_module *omod) +{ + if (!omod) { + sr_err("Invalid output module NULL!"); + return NULL; + } + + return omod->id; +} + +/** + * Returns the specified output module's name. + * + * @since 0.4.0 + */ +SR_API const char *sr_output_name_get(const struct sr_output_module *omod) +{ + if (!omod) { + sr_err("Invalid output module NULL!"); + return NULL; + } + + return omod->name; +} + +/** + * Returns the specified output module's description. + * + * @since 0.4.0 + */ +SR_API const char *sr_output_description_get(const struct sr_output_module *omod) +{ + if (!omod) { + sr_err("Invalid output module NULL!"); + return NULL; + } + + return omod->desc; +} + +/** + * Returns the specified output module's file extensions typical for the file + * format, as a NULL terminated array, or returns a NULL pointer if there is + * no preferred extension. + * @note these are a suggestions only. + * + * @since 0.4.0 + */ +SR_API const char *const *sr_output_extensions_get( + const struct sr_output_module *omod) +{ + if (!omod) { + sr_err("Invalid output module NULL!"); + return NULL; + } + + return omod->exts; +} + +/** + * Return the output module with the specified ID, or NULL if no module + * with that id is found. + * + * @since 0.4.0 + */ +SR_API const struct sr_output_module *sr_output_find(char *id) +{ + int i; + + for (i = 0; output_module_list[i]; i++) { + if (!strcmp(output_module_list[i]->id, id)) + return output_module_list[i]; + } + + return NULL; +} + +/** + * Returns a NULL-terminated array of struct sr_option, or NULL if the + * module takes no options. + * + * Each call to this function must be followed by a call to + * sr_output_options_free(). + * + * @since 0.4.0 + */ +SR_API const struct sr_option **sr_output_options_get(const struct sr_output_module *omod) +{ + const struct sr_option *mod_opts, **opts; + int size, i; + + if (!omod || !omod->options) + return NULL; + + mod_opts = omod->options(); + + for (size = 0; mod_opts[size].id; size++) + ; + opts = g_malloc((size + 1) * sizeof(struct sr_option *)); + + for (i = 0; i < size; i++) + opts[i] = &mod_opts[i]; + opts[i] = NULL; + + return opts; +} + +/** + * After a call to sr_output_options_get(), this function cleans up all + * resources returned by that call. + * + * @since 0.4.0 + */ +SR_API void sr_output_options_free(const struct sr_option **options) +{ + int i; + + if (!options) + return; + + for (i = 0; options[i]; i++) { + if (options[i]->def) { + g_variant_unref(options[i]->def); + ((struct sr_option *)options[i])->def = NULL; + } + + if (options[i]->values) { + g_slist_free_full(options[i]->values, (GDestroyNotify)g_variant_unref); + ((struct sr_option *)options[i])->values = NULL; + } + } + g_free(options); +} + +/** + * Create a new output instance using the specified output module. + * + * options is a *HashTable with the keys corresponding with + * the module options' id field. The values should be GVariant + * pointers with sunk * references, of the same GVariantType as the option's + * default value. + * + * The sr_dev_inst passed in can be used by the instance to determine + * channel names, samplerate, and so on. + * + * @since 0.4.0 + */ +SR_API const struct sr_output *sr_output_new(const struct sr_output_module *omod, + GHashTable *options, const struct sr_dev_inst *sdi) +{ + struct sr_output *op; + const struct sr_option *mod_opts; + const GVariantType *gvt; + GHashTable *new_opts; + GHashTableIter iter; + gpointer key, value; + int i; + + op = g_malloc(sizeof(struct sr_output)); + op->module = omod; + op->sdi = sdi; + + new_opts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)g_variant_unref); + if (omod->options) { + mod_opts = omod->options(); + for (i = 0; mod_opts[i].id; i++) { + if (options && g_hash_table_lookup_extended(options, + mod_opts[i].id, &key, &value)) { + /* Pass option along. */ + gvt = g_variant_get_type(mod_opts[i].def); + if (!g_variant_is_of_type(value, gvt)) { + sr_err("Invalid type for '%s' option.", key); + g_free(op); + return NULL; + } + g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id), + g_variant_ref(value)); + } else { + /* Option not given: insert the default value. */ + g_hash_table_insert(new_opts, g_strdup(mod_opts[i].id), + g_variant_ref(mod_opts[i].def)); + } + } + + /* Make sure no invalid options were given. */ + if (options) { + g_hash_table_iter_init(&iter, options); + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (!g_hash_table_lookup(new_opts, key)) { + sr_err("Output module '%s' has no option '%s'", omod->id, key); + g_hash_table_destroy(new_opts); + g_free(op); + return NULL; + } + } + } + } + + if (op->module->init && op->module->init(op, new_opts) != SR_OK) { + g_free(op); + op = NULL; + } + if (new_opts) + g_hash_table_destroy(new_opts); + + return op; +} + +/** + * Send a packet to the specified output instance. + * + * The instance's output is returned as a newly allocated GString, + * which must be freed by the caller. + * + * @since 0.4.0 + */ +SR_API int sr_output_send(const struct sr_output *o, + const struct sr_datafeed_packet *packet, GString **out) +{ + return o->module->receive(o, packet, out); +} + +/** + * Free the specified output instance and all associated resources. + * + * @since 0.4.0 + */ +SR_API int sr_output_free(const struct sr_output *o) +{ + int ret; + + if (!o) + return SR_ERR_ARG; + + ret = SR_OK; + if (o->module->cleanup) + ret = o->module->cleanup((struct sr_output *)o); + g_free((gpointer)o); + + return ret; +} + /** @} */ diff --git a/libsigrok4DSL/output/srzip.c b/libsigrok4DSL/output/srzip.c new file mode 100644 index 00000000..cacd0df0 --- /dev/null +++ b/libsigrok4DSL/output/srzip.c @@ -0,0 +1,321 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2014 Bert Vermeulen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#define LOG_PREFIX "output/srzip" + +struct out_context { + gboolean zip_created; + uint64_t samplerate; + char *filename; +}; + +static int init(struct sr_output *o, GHashTable *options) +{ + struct out_context *outc; + + outc = g_malloc0(sizeof(struct out_context)); + o->priv = outc; + outc->filename = g_strdup(g_variant_get_string(g_hash_table_lookup(options, "filename"), NULL)); + if (strlen(outc->filename) == 0) + return SR_ERR_ARG; + + return SR_OK; +} + +static int zip_create(const struct sr_output *o) +{ + struct out_context *outc; + struct sr_channel *ch; + FILE *meta; + struct zip *zipfile; + struct zip_source *versrc, *metasrc; + GVariant *gvar; + GSList *l; + int tmpfile, ret; + char version[1], metafile[32], *s; + + outc = o->priv; + if (outc->samplerate == 0) { + if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, SR_CONF_SAMPLERATE, + &gvar) == SR_OK) { + outc->samplerate = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } + } + + /* Quietly delete it first, libzip wants replace ops otherwise. */ + unlink(outc->filename); + if (!(zipfile = zip_open(outc->filename, ZIP_CREATE, &ret))) + return SR_ERR; + + /* "version" */ + version[0] = '2'; + if (!(versrc = zip_source_buffer(zipfile, version, 1, 0))) + return SR_ERR; + if (zip_add(zipfile, "version", versrc) == -1) { + sr_info("Error saving version into zipfile: %s.", + zip_strerror(zipfile)); + return SR_ERR; + } + + /* init "metadata" */ + strcpy(metafile, "sigrok-meta-XXXXXX"); + if ((tmpfile = g_mkstemp(metafile)) == -1) + return SR_ERR; + close(tmpfile); + meta = g_fopen(metafile, "wb"); + fprintf(meta, "[global]\n"); + fprintf(meta, "sigrok version = %s\n", PACKAGE_VERSION); + fprintf(meta, "[device 1]\ncapturefile = logic-1\n"); + fprintf(meta, "total probes = %d\n", g_slist_length(o->sdi->channels)); + s = sr_samplerate_string(outc->samplerate); + fprintf(meta, "samplerate = %s\n", s); + g_free(s); + + for (l = o->sdi->channels; l; l = l->next) { + ch = l->data; + if (ch->type != SR_CHANNEL_LOGIC) + continue; + if (!ch->enabled) + continue; + fprintf(meta, "probe%d = %s\n", ch->index + 1, ch->name); + } + fclose(meta); + + if (!(metasrc = zip_source_file(zipfile, metafile, 0, -1))) { + unlink(metafile); + return SR_ERR; + } + if (zip_add(zipfile, "metadata", metasrc) == -1) { + unlink(metafile); + return SR_ERR; + } + + if ((ret = zip_close(zipfile)) == -1) { + sr_info("Error saving zipfile: %s.", zip_strerror(zipfile)); + unlink(metafile); + return SR_ERR; + } + + unlink(metafile); + + return SR_OK; +} + +static int zip_append(const struct sr_output *o, unsigned char *buf, + int unitsize, int length) +{ + struct out_context *outc; + struct zip *archive; + struct zip_source *logicsrc; + zip_int64_t num_files; + struct zip_file *zf; + struct zip_stat zs; + struct zip_source *metasrc; + GKeyFile *kf; + GError *error; + gsize len; + int chunk_num, next_chunk_num, tmpfile, ret, i; + const char *entry_name; + char *metafile, tmpname[32], chunkname[16]; + + outc = o->priv; + if (!(archive = zip_open(outc->filename, 0, &ret))) + return SR_ERR; + + if (zip_stat(archive, "metadata", 0, &zs) == -1) + return SR_ERR; + + metafile = g_malloc(zs.size); + zf = zip_fopen_index(archive, zs.index, 0); + zip_fread(zf, metafile, zs.size); + zip_fclose(zf); + + /* + * If the file was only initialized but doesn't yet have any + * data it in, it won't have a unitsize field in metadata yet. + */ + error = NULL; + kf = g_key_file_new(); + if (!g_key_file_load_from_data(kf, metafile, zs.size, 0, &error)) { + sr_err("Failed to parse metadata: %s.", error->message); + return SR_ERR; + } + g_free(metafile); + tmpname[0] = '\0'; + if (!g_key_file_has_key(kf, "device 1", "unitsize", &error)) { + if (error && error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) { + sr_err("Failed to check unitsize key: %s", error ? error->message : "?"); + return SR_ERR; + } + /* Add unitsize field. */ + g_key_file_set_integer(kf, "device 1", "unitsize", unitsize); + metafile = g_key_file_to_data(kf, &len, &error); + strcpy(tmpname, "sigrok-meta-XXXXXX"); + if ((tmpfile = g_mkstemp(tmpname)) == -1) + return SR_ERR; + if (write(tmpfile, metafile, len) < 0) { + sr_dbg("Failed to create new metadata: %s", strerror(errno)); + g_free(metafile); + unlink(tmpname); + return SR_ERR; + } + close(tmpfile); + if (!(metasrc = zip_source_file(archive, tmpname, 0, -1))) { + sr_err("Failed to create zip source for metadata."); + g_free(metafile); + unlink(tmpname); + return SR_ERR; + } + if (zip_replace(archive, zs.index, metasrc) == -1) { + sr_err("Failed to replace metadata file."); + g_free(metafile); + unlink(tmpname); + return SR_ERR; + } + g_free(metafile); + } + g_key_file_free(kf); + + next_chunk_num = 1; + num_files = zip_get_num_entries(archive, 0); + for (i = 0; i < num_files; i++) { + entry_name = zip_get_name(archive, i, 0); + if (strncmp(entry_name, "logic-1", 7)) + continue; + if (strlen(entry_name) == 7) { + /* This file has no extra chunks, just a single "logic-1". + * Rename it to "logic-1-1" * and continue with chunk 2. */ + if (zip_rename(archive, i, "logic-1-1") == -1) { + sr_err("Failed to rename 'logic-1' to 'logic-1-1'."); + unlink(tmpname); + return SR_ERR; + } + next_chunk_num = 2; + break; + } else if (strlen(entry_name) > 8 && entry_name[7] == '-') { + chunk_num = strtoull(entry_name + 8, NULL, 10); + if (chunk_num >= next_chunk_num) + next_chunk_num = chunk_num + 1; + } + } + snprintf(chunkname, 15, "logic-1-%d", next_chunk_num); + if (!(logicsrc = zip_source_buffer(archive, buf, length, FALSE))) { + unlink(tmpname); + return SR_ERR; + } + if (zip_add(archive, chunkname, logicsrc) == -1) { + unlink(tmpname); + return SR_ERR; + } + if ((ret = zip_close(archive)) == -1) { + sr_info("error saving session file: %s", zip_strerror(archive)); + unlink(tmpname); + return SR_ERR; + } + unlink(tmpname); + + return SR_OK; +} + +static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet, + GString **out) +{ + struct out_context *outc; + const struct sr_datafeed_meta *meta; + const struct sr_datafeed_logic *logic; + const struct sr_config *src; + GSList *l; + + int ret; + + *out = NULL; + if (!o || !o->sdi || !(outc = o->priv)) + return SR_ERR_ARG; + + switch (packet->type) { + case SR_DF_META: + meta = packet->payload; + for (l = meta->config; l; l = l->next) { + src = l->data; + if (src->key != SR_CONF_SAMPLERATE) + continue; + outc->samplerate = g_variant_get_uint64(src->data); + } + break; + case SR_DF_LOGIC: + if (!outc->zip_created) { + if ((ret = zip_create(o)) != SR_OK) + return ret; + outc->zip_created = TRUE; + } + logic = packet->payload; + ret = zip_append(o, logic->data, logic->unitsize, logic->length); + break; + } + + return SR_OK; +} + +static int cleanup(struct sr_output *o) +{ + struct out_context *outc; + + outc = o->priv; + g_free(outc->filename); + g_free(outc); + o->priv = NULL; + + return SR_OK; +} + +static struct sr_option options[] = { + { "filename", "Filename", "File to write", NULL, NULL }, + {0} +}; + +static const struct sr_option *get_options(void) +{ + if (!options[0].def) + options[0].def = g_variant_ref_sink(g_variant_new_string("")); + + return options; +} + +SR_PRIV struct sr_output_module output_srzip = { + .id = "srzip", + .name = "srzip", + .desc = "srzip session file", + .exts = (const char*[]){"sr", NULL}, + .options = get_options, + .init = init, + .receive = receive, + .cleanup = cleanup, +}; + diff --git a/libsigrok4DSL/output/text/Makefile.am b/libsigrok4DSL/output/text/Makefile.am deleted file mode 100644 index 3b5d7c2e..00000000 --- a/libsigrok4DSL/output/text/Makefile.am +++ /dev/null @@ -1,33 +0,0 @@ -## -## This file is part of the libsigrok project. -## -## Copyright (C) 2011 Uwe Hermann -## -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 2 of the License, or -## (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License -## along with this program; if not, write to the Free Software -## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -## - -# Local lib, this is NOT meant to be installed! -noinst_LTLIBRARIES = libsigrok4DSLoutputtext.la - -libsigrok4DSLoutputtext_la_SOURCES = \ - text.c \ - text.h \ - bits.c \ - hex.c \ - ascii.c - -libsigrok4DSLoutputtext_la_CFLAGS = \ - -I$(top_srcdir) - diff --git a/libsigrok4DSL/output/text/ascii.c b/libsigrok4DSL/output/text/ascii.c deleted file mode 100644 index bd87024d..00000000 --- a/libsigrok4DSL/output/text/ascii.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2010-2012 Bert Vermeulen - * Copyright (C) 2011 Håvard Espeland - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" -#include "text.h" - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "output/ascii: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -SR_PRIV int init_ascii(struct sr_output *o) -{ - return init(o, DEFAULT_BPL_ASCII, MODE_ASCII); -} - -SR_PRIV int data_ascii(struct sr_output *o, const uint8_t *data_in, - uint64_t length_in, uint8_t **data_out, - uint64_t *length_out) -{ - struct context *ctx; - unsigned int outsize, offset, p; - int max_linelen; - const uint8_t *sample; - uint8_t *outbuf; - - ctx = o->internal; - max_linelen = SR_MAX_PROBENAME_LEN + 3 + ctx->samples_per_line - + ctx->samples_per_line / 8; - /* - * Calculate space needed for probes. Set aside 512 bytes for - * extra output, e.g. trigger. - */ - outsize = 512 + (1 + (length_in / ctx->unitsize) / ctx->samples_per_line) - * (ctx->num_enabled_probes * max_linelen); - - if (!(outbuf = g_try_malloc0(outsize + 1))) { - sr_err("%s: outbuf malloc failed", __func__); - return SR_ERR_MALLOC; - } - - outbuf[0] = '\0'; - if (ctx->header) { - /* The header is still here, this must be the first packet. */ - strncpy((char *)outbuf, ctx->header, outsize); - g_free(ctx->header); - ctx->header = NULL; - } - - if (length_in >= ctx->unitsize) { - for (offset = 0; offset <= length_in - ctx->unitsize; - offset += ctx->unitsize) { - sample = data_in + offset; - - char tmpval[ctx->num_enabled_probes]; - - for (p = 0; p < ctx->num_enabled_probes; p++) { - uint8_t curbit = (sample[p / 8] & ((uint8_t) 1 << (p % 8))); - uint8_t prevbit = (ctx->prevsample[p / 8] & - ((uint8_t) 1 << (p % 8))); - - if (curbit < prevbit && ctx->line_offset > 0) { - ctx->linebuf[p * ctx->linebuf_len + - ctx->line_offset-1] = '\\'; - } - - if (curbit > prevbit) { - tmpval[p] = '/'; - } else { - if (curbit) - tmpval[p] = '"'; - else - tmpval[p] = '.'; - } - } - - /* End of line. */ - if (ctx->spl_cnt >= ctx->samples_per_line) { - flush_linebufs(ctx, outbuf); - ctx->line_offset = ctx->spl_cnt = 0; - ctx->mark_trigger = -1; - } - - for (p = 0; p < ctx->num_enabled_probes; p++) { - ctx->linebuf[p * ctx->linebuf_len + - ctx->line_offset] = tmpval[p]; - } - - ctx->line_offset++; - ctx->spl_cnt++; - - memcpy(ctx->prevsample, sample, ctx->unitsize); - } - } else { - sr_info("Short buffer (length_in=%" PRIu64 ").", length_in); - } - - *data_out = outbuf; - *length_out = strlen((const char *)outbuf); - - return SR_OK; -} - -SR_PRIV struct sr_output_format output_text_ascii = { - .id = "ascii", - .description = "ASCII", - .df_type = SR_DF_LOGIC, - .init = init_ascii, - .data = data_ascii, - .event = event, -}; diff --git a/libsigrok4DSL/output/text/bits.c b/libsigrok4DSL/output/text/bits.c deleted file mode 100644 index 3afb9016..00000000 --- a/libsigrok4DSL/output/text/bits.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2010-2012 Bert Vermeulen - * Copyright (C) 2011 Håvard Espeland - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" -#include "text.h" - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "output/bits: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -SR_PRIV int init_bits(struct sr_output *o) -{ - return init(o, DEFAULT_BPL_BITS, MODE_BITS); -} - -SR_PRIV int data_bits(struct sr_output *o, const uint8_t *data_in, - uint64_t length_in, uint8_t **data_out, - uint64_t *length_out) -{ - struct context *ctx; - unsigned int outsize, offset, p; - int max_linelen; - const uint8_t *sample; - uint8_t *outbuf, c; - - ctx = o->internal; - max_linelen = SR_MAX_PROBENAME_LEN + 3 + ctx->samples_per_line - + ctx->samples_per_line / 8; - /* - * Calculate space needed for probes. Set aside 512 bytes for - * extra output, e.g. trigger. - */ - outsize = 512 + (1 + (length_in / ctx->unitsize) / ctx->samples_per_line) - * (ctx->num_enabled_probes * max_linelen); - - if (!(outbuf = g_try_malloc0(outsize + 1))) { - sr_err("%s: outbuf malloc failed", __func__); - return SR_ERR_MALLOC; - } - - outbuf[0] = '\0'; - if (ctx->header) { - /* The header is still here, this must be the first packet. */ - strncpy((char *)outbuf, ctx->header, outsize); - g_free(ctx->header); - ctx->header = NULL; - } - - if (length_in >= ctx->unitsize) { - for (offset = 0; offset <= length_in - ctx->unitsize; - offset += ctx->unitsize) { - sample = data_in + offset; - for (p = 0; p < ctx->num_enabled_probes; p++) { - c = (sample[p / 8] & ((uint8_t) 1 << (p % 8))) ? '1' : '0'; - ctx->linebuf[p * ctx->linebuf_len + - ctx->line_offset] = c; - } - ctx->line_offset++; - ctx->spl_cnt++; - - /* Add a space every 8th bit. */ - if ((ctx->spl_cnt & 7) == 0) { - for (p = 0; p < ctx->num_enabled_probes; p++) - ctx->linebuf[p * ctx->linebuf_len + - ctx->line_offset] = ' '; - ctx->line_offset++; - } - - /* End of line. */ - if (ctx->spl_cnt >= ctx->samples_per_line) { - flush_linebufs(ctx, outbuf); - ctx->line_offset = ctx->spl_cnt = 0; - ctx->mark_trigger = -1; - } - } - } else { - sr_info("Short buffer (length_in=%" PRIu64 ").", length_in); - } - - *data_out = outbuf; - *length_out = strlen((const char *)outbuf); - - return SR_OK; -} - -SR_PRIV struct sr_output_format output_text_bits = { - .id = "bits", - .description = "Bits", - .df_type = SR_DF_LOGIC, - .init = init_bits, - .data = data_bits, - .event = event, -}; diff --git a/libsigrok4DSL/output/text/hex.c b/libsigrok4DSL/output/text/hex.c deleted file mode 100644 index 3ddaf47f..00000000 --- a/libsigrok4DSL/output/text/hex.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2010-2012 Bert Vermeulen - * Copyright (C) 2011 Håvard Espeland - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "libsigrok.h" -#include "libsigrok-internal.h" -#include "text.h" - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "output/hex: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -SR_PRIV int init_hex(struct sr_output *o) -{ - return init(o, DEFAULT_BPL_HEX, MODE_HEX); -} - -SR_PRIV int data_hex(struct sr_output *o, const uint8_t *data_in, - uint64_t length_in, uint8_t **data_out, - uint64_t *length_out) -{ - struct context *ctx; - unsigned int outsize, offset, p; - int max_linelen; - const uint8_t *sample; - uint8_t *outbuf; - - ctx = o->internal; - max_linelen = SR_MAX_PROBENAME_LEN + 3 + ctx->samples_per_line - + ctx->samples_per_line / 2; - outsize = length_in / ctx->unitsize * ctx->num_enabled_probes - / ctx->samples_per_line * max_linelen + 512; - - if (!(outbuf = g_try_malloc0(outsize + 1))) { - sr_err("%s: outbuf malloc failed", __func__); - return SR_ERR_MALLOC; - } - - outbuf[0] = '\0'; - if (ctx->header) { - /* The header is still here, this must be the first packet. */ - strncpy((char *)outbuf, ctx->header, outsize); - g_free(ctx->header); - ctx->header = NULL; - } - - ctx->line_offset = 0; - for (offset = 0; offset <= length_in - ctx->unitsize; - offset += ctx->unitsize) { - sample = data_in + offset; - for (p = 0; p < ctx->num_enabled_probes; p++) { - ctx->linevalues[p] <<= 1; - if (sample[p / 8] & ((uint8_t) 1 << (p % 8))) - ctx->linevalues[p] |= 1; - sprintf((char *)ctx->linebuf + (p * ctx->linebuf_len) + - ctx->line_offset, "%.2x", ctx->linevalues[p]); - } - ctx->spl_cnt++; - - /* Add a space after every complete hex byte. */ - if ((ctx->spl_cnt & 7) == 0) { - for (p = 0; p < ctx->num_enabled_probes; p++) - ctx->linebuf[p * ctx->linebuf_len + - ctx->line_offset + 2] = ' '; - ctx->line_offset += 3; - } - - /* End of line. */ - if (ctx->spl_cnt >= ctx->samples_per_line) { - flush_linebufs(ctx, outbuf); - ctx->line_offset = ctx->spl_cnt = 0; - } - } - - *data_out = outbuf; - *length_out = strlen((const char *)outbuf); - - return SR_OK; -} - -SR_PRIV struct sr_output_format output_text_hex = { - .id = "hex", - .description = "Hexadecimal", - .df_type = SR_DF_LOGIC, - .init = init_hex, - .data = data_hex, - .event = event, -}; diff --git a/libsigrok4DSL/output/text/text.c b/libsigrok4DSL/output/text/text.c deleted file mode 100644 index 99d20dfd..00000000 --- a/libsigrok4DSL/output/text/text.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2013 Bert Vermeulen - * Copyright (C) 2011 Håvard Espeland - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include "config.h" /* Needed for PACKAGE_STRING and others. */ -#include "libsigrok.h" -#include "libsigrok-internal.h" -#include "text.h" - -/* Message logging helpers with subsystem-specific prefix string. */ -#define LOG_PREFIX "output/text: " -#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args) -#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args) -#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args) -#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args) -#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args) -#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args) - -SR_PRIV void flush_linebufs(struct context *ctx, uint8_t *outbuf) -{ - static int max_probename_len = 0; - int len, i; - GSList *l; - char *probe_name; - - if (ctx->linebuf[0] == 0) - return; - - if (max_probename_len == 0) { - /* First time through... */ - for (l = ctx->probenames; l; l = l->next) { - probe_name = l->data; - len = strlen(probe_name); - if (len > max_probename_len) - max_probename_len = len; - } - } - - for (i = 0, l = ctx->probenames; l; l = l->next, i++) { - probe_name = l->data; - sprintf((char *)outbuf + strlen((const char *)outbuf), - "%*s:%s\n", max_probename_len, - probe_name, ctx->linebuf + i * ctx->linebuf_len); - } - - /* Mark trigger with a ^ character. */ - if (ctx->mark_trigger != -1) - { - int space_offset = ctx->mark_trigger / 8; - - if (ctx->mode == MODE_ASCII) - space_offset = 0; - - sprintf((char *)outbuf + strlen((const char *)outbuf), - "T:%*s^\n", ctx->mark_trigger + space_offset, ""); - } - - memset(ctx->linebuf, 0, i * ctx->linebuf_len); -} - -SR_PRIV int init(struct sr_output *o, int default_spl, enum outputmode mode) -{ - struct context *ctx; - struct sr_channel *probe; - GSList *l; - GVariant *gvar; - uint64_t samplerate; - int num_probes, ret; - char *samplerate_s; - - if (!(ctx = g_try_malloc0(sizeof(struct context)))) { - sr_err("%s: ctx malloc failed", __func__); - return SR_ERR_MALLOC; - } - - o->internal = ctx; - ctx->num_enabled_probes = 0; - ctx->probenames = NULL; - - for (l = o->sdi->channels; l; l = l->next) { - probe = l->data; - if (!probe->enabled) - continue; - ctx->probenames = g_slist_append(ctx->probenames, probe->name); - ctx->num_enabled_probes++; - } - - ctx->unitsize = (ctx->num_enabled_probes + 7) / 8; - ctx->line_offset = 0; - ctx->spl_cnt = 0; - ctx->mark_trigger = -1; - ctx->mode = mode; - - ret = SR_OK; - if (o->param && o->param[0]) { - ctx->samples_per_line = strtoul(o->param, NULL, 10); - if (ctx->samples_per_line < 1) { - ret = SR_ERR; - goto err; - } - } else - ctx->samples_per_line = default_spl; - - if (!(ctx->header = g_try_malloc0(512))) { - sr_err("%s: ctx->header malloc failed", __func__); - ret = SR_ERR_MALLOC; - goto err; - } - - snprintf(ctx->header, 511, "%s\n", PACKAGE_STRING); - num_probes = g_slist_length(o->sdi->channels); - if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, - SR_CONF_SAMPLERATE, &gvar) == SR_OK) { - samplerate = g_variant_get_uint64(gvar); - g_variant_unref(gvar); - if (!(samplerate_s = sr_samplerate_string(samplerate))) { - ret = SR_ERR; - goto err; - } - snprintf(ctx->header + strlen(ctx->header), - 511 - strlen(ctx->header), - "Acquisition with %d/%d probes at %s\n", - ctx->num_enabled_probes, num_probes, samplerate_s); - g_free(samplerate_s); - } - - ctx->linebuf_len = ctx->samples_per_line * 2 + 4; - if (!(ctx->linebuf = g_try_malloc0(num_probes * ctx->linebuf_len))) { - sr_err("%s: ctx->linebuf malloc failed", __func__); - ret = SR_ERR_MALLOC; - goto err; - } - - if (!(ctx->linevalues = g_try_malloc0(num_probes))) { - sr_err("%s: ctx->linevalues malloc failed", __func__); - ret = SR_ERR_MALLOC; - } - - if (mode == MODE_ASCII && - !(ctx->prevsample = g_try_malloc0(num_probes / 8))) { - sr_err("%s: ctx->prevsample malloc failed", __func__); - ret = SR_ERR_MALLOC; - } - -err: - if (ret != SR_OK) { - g_free(ctx->header); - g_free(ctx); - } - - return ret; -} - -SR_PRIV int event(struct sr_output *o, int event_type, uint8_t **data_out, - uint64_t *length_out) -{ - struct context *ctx; - int outsize; - uint8_t *outbuf; - - ctx = o->internal; - switch (event_type) { - case SR_DF_TRIGGER: - ctx->mark_trigger = ctx->spl_cnt; - *data_out = NULL; - *length_out = 0; - break; - case SR_DF_END: - outsize = ctx->num_enabled_probes - * (ctx->samples_per_line + 20) + 512; - if (!(outbuf = g_try_malloc0(outsize))) { - sr_err("%s: outbuf malloc failed", __func__); - return SR_ERR_MALLOC; - } - flush_linebufs(ctx, outbuf); - *data_out = outbuf; - *length_out = strlen((const char *)outbuf); - g_free(o->internal); - o->internal = NULL; - break; - default: - *data_out = NULL; - *length_out = 0; - break; - } - - return SR_OK; -} diff --git a/libsigrok4DSL/output/text/text.h b/libsigrok4DSL/output/text/text.h deleted file mode 100644 index 8d5c9876..00000000 --- a/libsigrok4DSL/output/text/text.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of the libsigrok project. - * - * Copyright (C) 2010-2012 Bert Vermeulen - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef LIBSIGROK_OUTPUT_TEXT_TEXT_H -#define LIBSIGROK_OUTPUT_TEXT_TEXT_H - -#define DEFAULT_BPL_BITS 64 -#define DEFAULT_BPL_HEX 192 -#define DEFAULT_BPL_ASCII 74 - -enum outputmode { - MODE_BITS = 1, - MODE_HEX, - MODE_ASCII, -}; - -struct context { - unsigned int num_enabled_probes; - int samples_per_line; - unsigned int unitsize; - int line_offset; - int linebuf_len; - GSList *probenames; - uint8_t *linebuf; - int spl_cnt; - uint8_t *linevalues; - char *header; - int mark_trigger; - uint8_t *prevsample; - enum outputmode mode; -}; - -SR_PRIV void flush_linebufs(struct context *ctx, uint8_t *outbuf); -SR_PRIV int init(struct sr_output *o, int default_spl, enum outputmode mode); -SR_PRIV int event(struct sr_output *o, int event_type, uint8_t **data_out, - uint64_t *length_out); - -SR_PRIV int init_bits(struct sr_output *o); -SR_PRIV int data_bits(struct sr_output *o, const uint8_t *data_in, - uint64_t length_in, uint8_t **data_out, - uint64_t *length_out); - -SR_PRIV int init_hex(struct sr_output *o); -SR_PRIV int data_hex(struct sr_output *o, const uint8_t *data_in, - uint64_t length_in, uint8_t **data_out, - uint64_t *length_out); - -SR_PRIV int init_ascii(struct sr_output *o); -SR_PRIV int data_ascii(struct sr_output *o, const uint8_t *data_in, - uint64_t length_in, uint8_t **data_out, - uint64_t *length_out); - -#endif diff --git a/libsigrok4DSL/output/vcd.c b/libsigrok4DSL/output/vcd.c new file mode 100644 index 00000000..e42ca8c5 --- /dev/null +++ b/libsigrok4DSL/output/vcd.c @@ -0,0 +1,271 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2010 Uwe Hermann + * Copyright (C) 2013 Bert Vermeulen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include "config.h" /* Needed for PACKAGE and others. */ +#include "libsigrok.h" +#include "libsigrok-internal.h" + +#define LOG_PREFIX "output/vcd" + +struct context { + int num_enabled_channels; + GArray *channelindices; + uint8_t *prevsample; + gboolean header_done; + int period; + int *channel_index; + uint64_t samplerate; + uint64_t samplecount; +}; + +static int init(struct sr_output *o, GHashTable *options) +{ + struct context *ctx; + struct sr_channel *ch; + GSList *l; + int num_enabled_channels, i; + + (void)options; + + num_enabled_channels = 0; + for (l = o->sdi->channels; l; l = l->next) { + ch = l->data; + if (ch->type != SR_CHANNEL_LOGIC) + continue; + if (!ch->enabled) + continue; + num_enabled_channels++; + } + if (num_enabled_channels > 94) { + sr_err("VCD only supports 94 channels."); + return SR_ERR; + } + + ctx = g_malloc0(sizeof(struct context)); + o->priv = ctx; + ctx->num_enabled_channels = num_enabled_channels; + ctx->channel_index = g_malloc(sizeof(int) * ctx->num_enabled_channels); + + /* Once more to map the enabled channels. */ + for (i = 0, l = o->sdi->channels; l; l = l->next) { + ch = l->data; + if (ch->type != SR_CHANNEL_LOGIC) + continue; + if (!ch->enabled) + continue; + ctx->channel_index[i++] = ch->index; + } + + return SR_OK; +} + +static GString *gen_header(const struct sr_output *o) +{ + struct context *ctx; + struct sr_channel *ch; + GVariant *gvar; + GString *header; + GSList *l; + time_t t; + int num_channels, i; + char *samplerate_s, *frequency_s, *timestamp; + + ctx = o->priv; + header = g_string_sized_new(512); + num_channels = g_slist_length(o->sdi->channels); + + /* timestamp */ + t = time(NULL); + timestamp = g_strdup(ctime(&t)); + timestamp[strlen(timestamp)-1] = 0; + g_string_printf(header, "$date %s $end\n", timestamp); + g_free(timestamp); + + /* generator */ + g_string_append_printf(header, "$version %s %s $end\n", + PACKAGE, PACKAGE_VERSION); + g_string_append_printf(header, "$comment\n Acquisition with " + "%d/%d channels", ctx->num_enabled_channels, num_channels); + + if (ctx->samplerate == 0) { + if (sr_config_get(o->sdi->driver, o->sdi, NULL, NULL, SR_CONF_SAMPLERATE, + &gvar) == SR_OK) { + ctx->samplerate = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } + } + if (ctx->samplerate != 0) { + samplerate_s = sr_samplerate_string(ctx->samplerate); + g_string_append_printf(header, " at %s", samplerate_s); + g_free(samplerate_s); + } + g_string_append_printf(header, "\n$end\n"); + + /* timescale */ + /* VCD can only handle 1/10/100 (s - fs), so scale up first */ + if (ctx->samplerate > SR_MHZ(1)) + ctx->period = SR_GHZ(1); + else if (ctx->samplerate > SR_KHZ(1)) + ctx->period = SR_MHZ(1); + else + ctx->period = SR_KHZ(1); + frequency_s = sr_period_string(ctx->period); + g_string_append_printf(header, "$timescale %s $end\n", frequency_s); + g_free(frequency_s); + + /* scope */ + g_string_append_printf(header, "$scope module %s $end\n", PACKAGE); + + /* Wires / channels */ + for (i = 0, l = o->sdi->channels; l; l = l->next, i++) { + ch = l->data; + if (ch->type != SR_CHANNEL_LOGIC) + continue; + if (!ch->enabled) + continue; + g_string_append_printf(header, "$var wire 1 %c %s $end\n", + (char)('!' + i), ch->name); + } + + g_string_append(header, "$upscope $end\n$enddefinitions $end\n"); + + return header; +} + +static int receive(const struct sr_output *o, const struct sr_datafeed_packet *packet, + GString **out) +{ + const struct sr_datafeed_meta *meta; + const struct sr_datafeed_logic *logic; + const struct sr_config *src; + GSList *l; + struct context *ctx; + unsigned int i; + int p, curbit, prevbit, index; + uint8_t *sample; + gboolean timestamp_written; + + *out = NULL; + if (!o || !o->priv) + return SR_ERR_BUG; + ctx = o->priv; + + switch (packet->type) { + case SR_DF_META: + meta = packet->payload; + for (l = meta->config; l; l = l->next) { + src = l->data; + if (src->key != SR_CONF_SAMPLERATE) + continue; + ctx->samplerate = g_variant_get_uint64(src->data); + } + break; + case SR_DF_LOGIC: + logic = packet->payload; + + if (!ctx->header_done) { + *out = gen_header(o); + ctx->header_done = TRUE; + } else { + *out = g_string_sized_new(512); + } + + if (!ctx->prevsample) { + /* Can't allocate this until we know the stream's unitsize. */ + ctx->prevsample = g_malloc0(logic->unitsize); + } + + for (i = 0; i <= logic->length - logic->unitsize; i += logic->unitsize) { + sample = logic->data + i; + timestamp_written = FALSE; + + for (p = 0; p < ctx->num_enabled_channels; p++) { + index = ctx->channel_index[p]; + + curbit = ((unsigned)sample[index / 8] + >> (index % 8)) & 1; + prevbit = ((unsigned)ctx->prevsample[index / 8] + >> (index % 8)) & 1; + + /* VCD only contains deltas/changes of signals. */ + if (prevbit == curbit && ctx->samplecount > 0) + continue; + + /* Output timestamp of subsequent signal changes. */ + if (!timestamp_written) + g_string_append_printf(*out, "#%.0f", + (double)ctx->samplecount / + ctx->samplerate * ctx->period); + + /* Output which signal changed to which value. */ + g_string_append_c(*out, ' '); + g_string_append_c(*out, '0' + curbit); + g_string_append_c(*out, '!' + p); + + timestamp_written = TRUE; + } + + if (timestamp_written) + g_string_append_c(*out, '\n'); + + ctx->samplecount++; + memcpy(ctx->prevsample, sample, logic->unitsize); + } + break; + case SR_DF_END: + /* Write final timestamp as length indicator. */ + *out = g_string_sized_new(512); + g_string_printf(*out, "#%.0f\n", + (double)ctx->samplecount / ctx->samplerate * ctx->period); + break; + } + + return SR_OK; +} + +static int cleanup(struct sr_output *o) +{ + struct context *ctx; + + if (!o || !o->priv) + return SR_ERR_ARG; + + ctx = o->priv; + g_free(ctx->prevsample); + g_free(ctx->channel_index); + g_free(ctx); + + return SR_OK; +} + +struct sr_output_module output_vcd = { + .id = "vcd", + .name = "VCD", + .desc = "Value Change Dump", + .exts = (const char*[]){"vcd", NULL}, + .options = NULL, + .init = init, + .receive = receive, + .cleanup = cleanup, +}; diff --git a/libsigrok4DSL/proto.h b/libsigrok4DSL/proto.h index 576c8c7b..ad37fc98 100644 --- a/libsigrok4DSL/proto.h +++ b/libsigrok4DSL/proto.h @@ -132,7 +132,7 @@ SR_API struct sr_input_format **sr_input_list(void); /*--- output/output.c -------------------------------------------------------*/ -SR_API struct sr_output_format **sr_output_list(void); +SR_API const struct sr_output_module **sr_output_list(void); /*--- strutil.c -------------------------------------------------------------*/ diff --git a/libsigrok4DSL/version.h b/libsigrok4DSL/version.h new file mode 100644 index 00000000..8597d4f8 --- /dev/null +++ b/libsigrok4DSL/version.h @@ -0,0 +1,69 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2010-2012 Bert Vermeulen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBSIGROK_VERSION_H +#define LIBSIGROK_VERSION_H + +/** + * @file + * + * Version number definitions and macros. + */ + +/** + * @ingroup grp_versions + * + * @{ + */ + +/* + * Package version macros (can be used for conditional compilation). + */ + +/** The libsigrok package 'major' version number. */ +#define SR_PACKAGE_VERSION_MAJOR 0 + +/** The libsigrok package 'minor' version number. */ +#define SR_PACKAGE_VERSION_MINOR 2 + +/** The libsigrok package 'micro' version number. */ +#define SR_PACKAGE_VERSION_MICRO 0 + +/** The libsigrok package version ("major.minor.micro") as string. */ +#define SR_PACKAGE_VERSION_STRING "0.2.0" + +/* + * Library/libtool version macros (can be used for conditional compilation). + */ + +/** The libsigrok libtool 'current' version number. */ +#define SR_LIB_VERSION_CURRENT 1 + +/** The libsigrok libtool 'revision' version number. */ +#define SR_LIB_VERSION_REVISION 2 + +/** The libsigrok libtool 'age' version number. */ +#define SR_LIB_VERSION_AGE 0 + +/** The libsigrok libtool version ("current:revision:age") as string. */ +#define SR_LIB_VERSION_STRING "1:2:0" + +/** @} */ + +#endif